public enum AlarmIndicatorScaled {
    VERY_SMALL(R.id.event_entry_indicator_alarm_very_small),
    SMALL(R.id.event_entry_indicator_alarm_small),
    MEDIUM(R.id.event_entry_indicator_alarm);

    @LayoutRes
    public final int indicatorResId;

    AlarmIndicatorScaled(int indicatorResId) {
        this.indicatorResId = indicatorResId;
    }
} public enum RecurringIndicatorScaled {
    VERY_SMALL(R.id.event_entry_indicator_recurring_very_small),
    SMALL(R.id.event_entry_indicator_recurring_small),
    MEDIUM(R.id.event_entry_indicator_recurring);

    @LayoutRes
    public final int indicatorResId;

    RecurringIndicatorScaled(int indicatorResId) {
        this.indicatorResId = indicatorResId;
    }
} public enum TextShading {

    WHITE(R.style.Theme_Calendar_White, R.string.appearance_theme_white),
    LIGHT(R.style.Theme_Calendar_Light, R.string.appearance_theme_light),
    DARK(R.style.Theme_Calendar_Dark, R.string.appearance_theme_dark),
    BLACK(R.style.Theme_Calendar_Black, R.string.appearance_theme_black);

    @StyleRes
    public final int themeResId;
    @StringRes
    public final int titleResId;

    TextShading(int themeResId, int titleResId) {
        this.themeResId = themeResId;
        this.titleResId = titleResId;
    }

    public static TextShading fromName(String themeName, TextShading defaultShading) {
        try {
            return TextShading.valueOf(themeName);
        } catch (Exception e) {
            return defaultShading;
        }
    }

} public enum TextShadingPref {

    WIDGET_HEADER("headerTheme",   TextShading.DARK, R.string.appearance_header_theme_title,
            false, TimeSection.ALL),
    DAY_HEADER_PAST("dayHeaderThemePast", TextShading.DARK, R.string.day_header_theme_title,
            true, TimeSection.PAST),
    ENTRY_PAST("entryThemePast", TextShading.BLACK, R.string.appearance_entries_theme_title,
            false, TimeSection.PAST),
    DAY_HEADER_TODAY("dayHeaderTheme", TextShading.LIGHT, R.string.day_header_theme_title,
            true, TimeSection.TODAY),
    ENTRY_TODAY("entryTheme", TextShading.WHITE, R.string.appearance_entries_theme_title,
            false, TimeSection.TODAY),
    DAY_HEADER_FUTURE("dayHeaderThemeFuture", TextShading.DARK, R.string.day_header_theme_title,
            true, TimeSection.FUTURE),
    ENTRY_FUTURE("entryThemeFuture", TextShading.BLACK, R.string.appearance_entries_theme_title,
            false, TimeSection.FUTURE);

    public final String preferenceName;
    public final TextShading defaultShading;
    @StringRes
    public final int titleResId;
    public final boolean dependsOnDayHeader;
    public final TimeSection timeSection;

    TextShadingPref(String preferenceName, TextShading defaultShading, int titleResId,
                    boolean dependsOnDayHeader, TimeSection timeSection) {
        this.preferenceName = preferenceName;
        this.defaultShading = defaultShading;
        this.titleResId = titleResId;
        this.dependsOnDayHeader = dependsOnDayHeader;
        this.timeSection = timeSection;
    }

    public static TextShadingPref forDayHeader(WidgetEntry<?> entry) {
        return entry.timeSection.select(DAY_HEADER_PAST, DAY_HEADER_TODAY, DAY_HEADER_FUTURE);
    }

    public static TextShadingPref forDetails(WidgetEntry<?> entry) {
        return entry.timeSection.select(DAY_HEADER_PAST, DAY_HEADER_TODAY, DAY_HEADER_FUTURE);
    }

    public static TextShadingPref forTitle(WidgetEntry<?> entry) {
        return entry.timeSection.select(ENTRY_PAST, ENTRY_TODAY, ENTRY_FUTURE);
    }

} public class EventProvider {

    protected static final String AND_BRACKET = " AND (";
    protected static final String OPEN_BRACKET = "( ";
    protected static final String CLOSING_BRACKET = " )";
    protected static final String AND = " AND ";
    protected static final String OR = " OR ";
    protected static final String EQUALS = " = ";
    protected static final String NOT_EQUALS = " != ";
    protected static final String LTE = " <= ";
    protected static final String IS_NULL = " IS NULL";

    public final EventProviderType type;
    public final Context context;
    public final int widgetId;
    protected final MyContentResolver myContentResolver;

    // Below are parameters, which may change in settings
    protected KeywordsFilter mKeywordsFilter;
    protected DateTime mStartOfTimeRange;
    protected DateTime mEndOfTimeRange;

    public EventProvider(EventProviderType type, Context context, int widgetId) {
        this.type = type;
        this.context = context;
        this.widgetId = widgetId;
        myContentResolver =  new MyContentResolver(type, context, widgetId);
    }

    protected void initialiseParameters() {
        mKeywordsFilter = new KeywordsFilter(getSettings().getHideBasedOnKeywords());
        mStartOfTimeRange = getSettings().getStartOfTimeRange();
        mEndOfTimeRange = getSettings().getEndOfTimeRange();
    }

    @NonNull
    public InstanceSettings getSettings() {
        return myContentResolver.getSettings();
    }

    protected int getAsOpaque(int color) {
        return argb(255, red(color), green(color), blue(color));
    }

    public static Optional<Integer> getColumnIndex(Cursor cursor, String columnName) {
        return Optional.of(cursor.getColumnIndex(columnName))
                .filter(ind -> ind >=0)
                .filter(ind -> cursor.getType(ind) != FIELD_TYPE_NULL);
    }

    public Try<List<EventSource>> fetchAvailableSources() {
        return Try.success(Collections.emptyList());
    }

    protected FilterMode getFilterMode() {
        return getSettings().getFilterMode();
    }
} public class CalendarEntry extends WidgetEntry<CalendarEntry> {

    private static final String ARROW = "→";
    private static final String SPACE = " ";
    static final String SPACE_DASH_SPACE = " - ";

    private boolean allDay;
    private CalendarEvent event;

    public static CalendarEntry fromEvent(InstanceSettings settings, CalendarEvent event, DateTime entryDate) {
        CalendarEntry entry = new CalendarEntry(settings, entryDate, event.getEndDate());
        entry.allDay = event.isAllDay();
        entry.event = event;
        return entry;
    }

    private CalendarEntry(InstanceSettings settings, DateTime entryDate, DateTime endDate) {
        super(settings, WidgetEntry.getEntryPosition(settings, entryDate, endDate), entryDate, endDate);
    }

    @Override
    public String getTitle() {
        String title = event.getTitle();
        if (TextUtils.isEmpty(title)) {
            title = getContext().getResources().getString(R.string.no_title);
        }
        return title;
    }

    public int getColor() {
        return event.getColor();
    }

    public boolean isAllDay() {
        return allDay;
    }

    @Override
    public String getLocation() {
        return event.getLocation();
    }

    public boolean isAlarmActive() {
        return event.isAlarmActive();
    }

    public boolean isRecurring() {
        return event.isRecurring();
    }

    public boolean isPartOfMultiDayEvent() {
        return getEvent().isPartOfMultiDayEvent();
    }

    public boolean isStartOfMultiDayEvent() {
        return isPartOfMultiDayEvent() && !getEvent().getStartDate().isBefore(entryDate);
    }

    public boolean isEndOfMultiDayEvent() {
        return isPartOfMultiDayEvent() && isLastEntryOfEvent();
    }

    public boolean spansOneFullDay() {
        return entryDate.plusDays(1).isEqual(event.getEndDate());
    }

    public CalendarEvent getEvent() {
        return event;
    }

    @Override
    public String getEventTimeString() {
        return hideEventTime() ? "" : createTimeSpanString();
    }

    private boolean hideEventTime() {
        return spansOneFullDay() && !(isStartOfMultiDayEvent() || isEndOfMultiDayEvent()) ||
                isAllDay();
    }

    private String createTimeSpanString() {
        String startStr;
        String endStr;
        String separator = SPACE_DASH_SPACE;
        if (!isDateDefined(entryDate) || (isPartOfMultiDayEvent() && DateUtil.isMidnight(entryDate)
                && !isStartOfMultiDayEvent())) {
            startStr = ARROW;
            separator = SPACE;
        } else {
            startStr = DateUtil.formatTime(this::getSettings, entryDate);
        }
        if (getSettings().getShowEndTime()) {
            if (!isDateDefined(event.getEndDate()) || (isPartOfMultiDayEvent() && !isLastEntryOfEvent())) {
                endStr = ARROW;
                separator = SPACE;
            } else {
                endStr = DateUtil.formatTime(this::getSettings, event.getEndDate());
            }
        } else {
            separator = DateUtil.EMPTY_STRING;
            endStr = DateUtil.EMPTY_STRING;
        }

        if (startStr.equals(endStr)) {
            return startStr;
        }

        return startStr + separator + endStr;
    }

    public Context getContext() {
        return event.getContext();
    }

    public InstanceSettings getSettings() {
        return event.getSettings();
    }

    @Override
    public OrderedEventSource getSource() {
        return event.getEventSource();
    }

    @Override
    public String toString() {
        return super.toString() + " CalendarEntry ["
                + "allDay=" + allDay
                + ", time=" + getEventTimeString()
                + ", location=" + getLocationString()
                + ", event=" + event
                + "]";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        CalendarEntry that = (CalendarEntry) o;
        return event.equals(that.event) && entryDate.equals(that.entryDate);
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result += 31 * event.hashCode();
        result += 31 * entryDate.hashCode();
        return result;
    }
} public abstract class WidgetEntry<T extends WidgetEntry<T>> implements Comparable<WidgetEntry<T>> {

    protected final InstanceSettings settings;
    public final WidgetEntryPosition entryPosition;
    public final DateTime entryDate;
    public final DateTime entryDay;
    public final DateTime endDate;
    public final TimeSection timeSection;

    protected WidgetEntry(InstanceSettings settings, WidgetEntryPosition entryPosition, DateTime entryDate, DateTime endDate) {
        this.settings = settings;
        this.entryPosition = entryPosition;
        this.endDate = endDate;
        this.entryDate = fixEntryDate(entryPosition, entryDate);
        entryDay = calcEntryDay(settings, entryPosition, this.entryDate);
        timeSection = calcTimeSection(settings, entryPosition, entryDay, endDate);
    }

    private static DateTime fixEntryDate(WidgetEntryPosition entryPosition, DateTime entryDate) {
        switch (entryPosition) {
            case ENTRY_DATE:
                throwIfNull(entryPosition, entryDate);
                return entryDate;
            case PAST_AND_DUE_HEADER:
            case PAST_AND_DUE:
            case START_OF_TODAY:
            case HIDDEN:
                return entryDate == null
                        ? MyClock.DATETIME_MIN
                        : entryDate;
            case DAY_HEADER:
            case START_OF_DAY:
                throwIfNull(entryPosition, entryDate);
                return entryDate.withTimeAtStartOfDay();
            case END_OF_TODAY:
            case END_OF_LIST_HEADER:
            case END_OF_LIST:
            case LIST_FOOTER:
                return entryDate == null
                        ? MyClock.DATETIME_MAX
                        : entryDate;
            default:
                throw new IllegalArgumentException("Invalid position " + entryPosition + "; entryDate: " + entryDate);
        }
    }

    private static DateTime calcEntryDay(InstanceSettings settings, WidgetEntryPosition entryPosition, DateTime entryDate) {
        switch (entryPosition) {
            case START_OF_TODAY:
            case END_OF_TODAY:
                return settings.clock().now().withTimeAtStartOfDay();
            default:
                return entryDate.withTimeAtStartOfDay();
        }
    }

    private static TimeSection calcTimeSection(InstanceSettings settings, WidgetEntryPosition entryPosition,
                                               DateTime entryDay, DateTime endDate) {
        switch (entryPosition) {
            case PAST_AND_DUE_HEADER:
                return TimeSection.PAST;
            case START_OF_TODAY:
                return TimeSection.TODAY;
            case END_OF_TODAY:
            case END_OF_LIST_HEADER:
            case END_OF_LIST:
            case LIST_FOOTER:
                return TimeSection.FUTURE;
            default:
                break;
        }
        if (settings.clock().isToday(entryDay)) {
            if (entryPosition == DAY_HEADER) return TimeSection.TODAY;

            if (settings.clock().isToday(endDate)) {
                return settings.clock().isBeforeNow(endDate)
                        ? TimeSection.PAST
                        : TimeSection.TODAY;
            }
            return TimeSection.TODAY;
        }
        return settings.clock().isBeforeToday(entryDay)
                ? TimeSection.PAST
                : (settings.clock().isToday(endDate) ? TimeSection.TODAY : TimeSection.FUTURE);
    }

    private static void throwIfNull(WidgetEntryPosition entryPosition, DateTime entryDate) {
        if (entryDate == null) {
            throw new IllegalArgumentException("Invalid entry date: " + entryDate + " at position " + entryPosition);
        }
    }

    public boolean isLastEntryOfEvent() {
        return endDate == null ||
                !entryPosition.entryDateIsRequired ||
                endDate.isBefore(MyClock.startOfNextDay(this.entryDate));
    }

    public static WidgetEntryPosition getEntryPosition(InstanceSettings settings, DateTime mainDate, DateTime otherDate) {
        if (mainDate == null && otherDate == null) return settings.getTaskWithoutDates().widgetEntryPosition;

        DateTime refDate = mainDate == null ? otherDate : mainDate;
        if (settings.getShowPastEventsUnderOneHeader() && settings.clock().isBeforeToday(refDate)) {
            return PAST_AND_DUE;
        }
        if (refDate.isAfter(settings.getEndOfTimeRange())) return END_OF_LIST;
        return ENTRY_DATE;
    }

    public String getEventTimeString() {
        return "";
    }

    public OrderedEventSource getSource() {
        return OrderedEventSource.EMPTY;
    }

    public String getTitle() {
        return "";
    }

    String getLocationString() {
        return hideLocation() ? "" : getLocation();
    }

    private boolean hideLocation() {
        return getLocation().isEmpty() || !settings.getShowLocation();
    }

    public String getLocation() {
        return "";
    }

    @Override
    public int compareTo(WidgetEntry other) {
        int globalSignum = Integer.signum(entryPosition.globalOrder - other.entryPosition.globalOrder);
        if (globalSignum != 0) return globalSignum;

        if (DateUtil.isSameDay(entryDay, other.entryDay)) {
            int sameDaySignum = Integer.signum(entryPosition.sameDayOrder - other.entryPosition.sameDayOrder);
            if ((sameDaySignum != 0) && DateUtil.isSameDay(entryDay, other.entryDay)) return sameDaySignum;

            if (entryDate.isAfter(other.entryDate)) {
                return 1;
            } else if (entryDate.isBefore(other.entryDate)) {
                return -1;
            }
        } else {
            if (entryDay.isAfter(other.entryDay)) {
                return 1;
            } else if (entryDay.isBefore(other.entryDay)) {
                return -1;
            }
        }

        int sourceSignum = Integer.signum(getSource().order - other.getSource().order);
        return sourceSignum == 0
                ? getTitle().compareTo(other.getTitle())
                : sourceSignum;
    }

    public boolean duplicates(WidgetEntry other) {
        return entryPosition == other.entryPosition &&
            entryDate.equals(other.entryDate) &&
            isSameDate(endDate, other.endDate) &&
            getTitle().equals(other.getTitle()) &&
            getLocation().equals(other.getLocation());
    }

    public CharSequence formatEntryDate() {
        return settings.getEntryDateFormat().type == DateFormatType.HIDDEN
                ? ""
                : settings.entryDateFormatter().formatDate(entryDate);
    }

    @Override
    public String toString() {
        return entryPosition.value + " [" +
                "entryDate=" +
                (entryDate == MyClock.DATETIME_MIN ? "min" :
                        (entryDate == MyClock.DATETIME_MAX) ? "max" : entryDate) +
                ", endDate=" + endDate +
            "]";
    }
} public abstract class WidgetEntryVisualizer<T extends WidgetEntry<T>> {
    private final EventProvider eventProvider;

    public WidgetEntryVisualizer(EventProvider eventProvider) {
        this.eventProvider = eventProvider;
    }

    public RemoteViews getRemoteViews(WidgetEntry entry, int position) {

        RemoteViews rv = new RemoteViews(getContext().getPackageName(), getSettings().getEventEntryLayout().layoutId);
        setTitle(entry, rv);
        setDetails(entry, rv);
        setDate(entry, rv);
        setTime(entry, rv);

        setIndicators(entry, rv);
        setBackgroundColor(rv, R.id.event_entry, getSettings().getEntryBackgroundColor(entry));
        return rv;
    }

    protected void setIndicators(WidgetEntry entry, RemoteViews rv) {
        for (AlarmIndicatorScaled indicator : AlarmIndicatorScaled.values()) {
            rv.setViewVisibility(indicator.indicatorResId, View.GONE);
        }
        for (RecurringIndicatorScaled indicator : RecurringIndicatorScaled.values()) {
            rv.setViewVisibility(indicator.indicatorResId, View.GONE);
        }
    }

    @NonNull
    protected InstanceSettings getSettings() {
        return eventProvider.getSettings();
    }

    public Context getContext() {
        return eventProvider.context;
    }

    public abstract List<T> queryEventEntries();

    protected void setTitle(WidgetEntry entry, RemoteViews rv) {
        int viewId = R.id.event_entry_title;
        rv.setTextViewText(viewId, getTitleString(entry));
        setTextSize(getSettings(), rv, viewId, R.dimen.event_entry_title);
        setTextColorFromAttr(getSettings().getShadingContext(TextShadingPref.forTitle(entry)),
                rv, viewId, R.attr.eventEntryTitle);
        setMultiline(rv, viewId, getSettings().isMultilineTitle());
    }

    protected CharSequence getTitleString(WidgetEntry event) {
        return getSettings().getEventEntryLayout() == EventEntryLayout.DEFAULT
            ? event.getTitle()
            : MyStringBuilder.of(event.getTitle())
                .withSeparator(event.getLocationString(), SPACE_PIPE_SPACE);
    }

    protected void setDetails(WidgetEntry entry, RemoteViews rv) {
        if (getSettings().getEventEntryLayout() == EventEntryLayout.ONE_LINE) return;

        MyStringBuilder eventDetails = MyStringBuilder
                .of(entry.formatEntryDate())
                .withSpace(entry.getEventTimeString())
                .withSeparator(entry.getLocationString(), SPACE_PIPE_SPACE);
        int viewId = R.id.event_entry_details;
        if (eventDetails.isEmpty()) {
            rv.setViewVisibility(viewId, View.GONE);
        } else {
            rv.setViewVisibility(viewId, View.VISIBLE);
            rv.setTextViewText(viewId, eventDetails);
            setTextSize(getSettings(), rv, viewId, R.dimen.event_entry_details);
            setTextColorFromAttr(getSettings().getShadingContext(TextShadingPref.forDetails(entry)),
                    rv, viewId, R.attr.dayHeaderTitle);
            setMultiline(rv, viewId, getSettings().isMultilineDetails());
        }
    }

    protected void setDate(WidgetEntry entry, RemoteViews rv) {
        if (getSettings().getEventEntryLayout() == EventEntryLayout.DEFAULT) return;

        if (getSettings().getEntryDateFormat().type == DateFormatType.HIDDEN) {
            rv.setViewVisibility(R.id.event_entry_days, View.GONE);
            rv.setViewVisibility(R.id.event_entry_days_right, View.GONE);
        } else {
            int days = getSettings().clock().getNumberOfDaysTo(entry.entryDate);
            boolean daysAsText = getSettings().getEntryDateFormat().type != DateFormatType.NUMBER_OF_DAYS ||
                    (days > -2 && days < 2);

            int viewToShow = daysAsText ? R.id.event_entry_days : R.id.event_entry_days_right;
            int viewToHide = daysAsText ? R.id.event_entry_days_right : R.id.event_entry_days;
            rv.setViewVisibility(viewToHide, View.GONE);
            rv.setViewVisibility(viewToShow, View.VISIBLE);

            rv.setTextViewText(viewToShow, entry.formatEntryDate());
            setViewWidth(getSettings(), rv, viewToShow, daysAsText
                    ? R.dimen.days_to_event_width
                    : R.dimen.days_to_event_right_width);
            setTextSize(getSettings(), rv, viewToShow, R.dimen.event_entry_details);
            setTextColorFromAttr(getSettings().getShadingContext(TextShadingPref.forDetails(entry)),
                    rv, viewToShow, R.attr.dayHeaderTitle);
        }
    }

    protected void setTime(WidgetEntry entry, RemoteViews rv) {
        if (getSettings().getEventEntryLayout() == EventEntryLayout.DEFAULT) return;

        int viewId = R.id.event_entry_time;
        RemoteViewsUtil.setMultiline(rv, viewId, getSettings().getShowEndTime());
        rv.setTextViewText(viewId, entry.getEventTimeString().replace(CalendarEntry
                .SPACE_DASH_SPACE, "\n"));
        setViewWidth(getSettings(), rv, viewId, R.dimen.event_time_width);
        setTextSize(getSettings(), rv, viewId, R.dimen.event_entry_details);
        setTextColorFromAttr(getSettings().getShadingContext(TextShadingPref.forDetails(entry)),
                rv, viewId, R.attr.dayHeaderTitle);
    }

} public static void setAlpha(RemoteViews rv, int viewId, int alpha) {
    rv.setInt(viewId, METHOD_SET_ALPHA, alpha);
} public static void setBackgroundColor(RemoteViews rv, int viewId, int color) {
    rv.setInt(viewId, METHOD_SET_BACKGROUND_COLOR, color);
} public static void setImageFromAttr(Context context, RemoteViews rv, int viewId, int attrResId) {
    TypedValue outValue = new TypedValue();
    if (context.getTheme().resolveAttribute(attrResId, outValue, true)) {
        setImage(rv, viewId, outValue.resourceId);
    } else {
        Log.w(TAG,
        "setImageFromAttr: not found; attrResId:" + attrResId + ", resourceId:" + outValue.resourceId +
                ", out:" + outValue + ", context:" + context);
    }
}
import android.view.View;
import android.widget.RemoteViews;
import org.andstatus.todoagenda.AlarmIndicatorScaled;
import org.andstatus.todoagenda.R;
import org.andstatus.todoagenda.RecurringIndicatorScaled;
import org.andstatus.todoagenda.TextShading;
import org.andstatus.todoagenda.prefs.TextShadingPref;
import org.andstatus.todoagenda.provider.EventProvider;
import org.andstatus.todoagenda.widget.CalendarEntry;
import org.andstatus.todoagenda.widget.WidgetEntry;
import org.andstatus.todoagenda.widget.WidgetEntryVisualizer;
import org.joda.time.DateTime;
import java.util.ArrayList;
import java.util.List;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setAlpha;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setBackgroundColor;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setImageFromAttr;
package org.andstatus.todoagenda.calendar;





public class CalendarEventVisualizer extends WidgetEntryVisualizer<CalendarEntry> {
    private final CalendarEventProvider eventProvider;

    public CalendarEventVisualizer(EventProvider eventProvider) {
        super(eventProvider);
        this.eventProvider = (CalendarEventProvider) eventProvider;
    }

    public RemoteViews getRemoteViews(WidgetEntry eventEntry, int position) {
        if (!(eventEntry instanceof CalendarEntry)) return null;

        RemoteViews rv = super.getRemoteViews(eventEntry, position);
        CalendarEntry entry = (CalendarEntry) eventEntry;
        rv.setOnClickFillInIntent(R.id.event_entry, eventProvider.createViewEventIntent(entry.getEvent()));
        setIcon(entry, rv);
        return rv;
    }

    @Override
    protected void setIndicators(WidgetEntry eventEntry, RemoteViews rv) {
        CalendarEntry entry = (CalendarEntry) eventEntry;
        setAlarmActive(entry, rv);
        setRecurring(entry, rv);
    }

    private void setAlarmActive(CalendarEntry entry, RemoteViews rv) {
        boolean showIndicator = entry.isAlarmActive() && getSettings().getIndicateAlerts();
        for (AlarmIndicatorScaled indicator : AlarmIndicatorScaled.values()) {
            setIndicator(entry, rv,
                    showIndicator && indicator == getSettings().getTextSizeScale().alarmIndicator,
                    indicator.indicatorResId, R.attr.eventEntryAlarm);
        }
    }

    private void setRecurring(CalendarEntry entry, RemoteViews rv) {
        boolean showIndicator = entry.isRecurring() && getSettings().getIndicateRecurring();
        for (RecurringIndicatorScaled indicator : RecurringIndicatorScaled.values()) {
            setIndicator(entry, rv,
                    showIndicator && indicator == getSettings().getTextSizeScale().recurringIndicator,
                    indicator.indicatorResId, R.attr.eventEntryRecurring);
        }
    }

    private void setIndicator(CalendarEntry entry, RemoteViews rv, boolean showIndication, int viewId, int imageAttrId) {
        if (showIndication) {
            rv.setViewVisibility(viewId, View.VISIBLE);
            TextShadingPref pref = TextShadingPref.forTitle(entry);
            setImageFromAttr(getSettings().getShadingContext(pref), rv, viewId, imageAttrId);
            TextShading textShading = getSettings().getShading(pref);