public enum Alignment {

    LEFT(Gravity.LEFT),
    CENTER(android.view.Gravity.CENTER),
    RIGHT(Gravity.RIGHT);

    public final int gravity;

    Alignment(int gravity) {
        this.gravity = gravity;
    }

} 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 enum EventProviderType {
    EMPTY(0, true, "", ""),
    CALENDAR(1, true, Manifest.permission.READ_CALENDAR, "com.android.calendar") {
        @Override
        public EventProvider getEventProvider(Context context, int widgetId) {
            return new CalendarEventProvider(this, context, widgetId);
        }
    },
    DMFS_OPEN_TASKS(2, false, DmfsOpenTasksContract.PERMISSION, "org.dmfs.tasks") {
        @Override
        public EventProvider getEventProvider(Context context, int widgetId) {
            return new DmfsOpenTasksProvider(this, context, widgetId);
        }
    },
    SAMSUNG_TASKS(3, false, Manifest.permission.READ_CALENDAR, "com.android.calendar") {
        @Override
        public EventProvider getEventProvider(Context context, int widgetId) {
            return new SamsungTasksProvider(this, context, widgetId);
        }
    };

    private static final String TAG = EventProviderType.class.getSimpleName();
    public final int id;
    public final boolean isCalendar;
    public final String permission;
    private final String authority;

    private static List<OrderedEventSource> sources = new CopyOnWriteArrayList<>();
    private static Set<String> permissionsNeeded = new CopyOnWriteArraySet<>();
    private static volatile boolean initialized = false;

    EventProviderType(int id, boolean isCalendar, String permission, String authority) {
        this.id = id;
        this.isCalendar = isCalendar;
        this.permission = permission;
        this.authority = authority;
    }

    public static void initialize(Context context, boolean reInitialize) {
        if (initialized && !reInitialize) return;

        sources.clear();
        for(EventProviderType type : EventProviderType.values()) {
            EventProvider provider = type.getEventProvider(context, 0);
            provider.fetchAvailableSources()
            .onSuccess( ss -> {
                Log.i(TAG, "provider " + type + ", " + (ss.isEmpty() ? "no" : ss.size()) + " sources");
                sources.addAll(OrderedEventSource.fromSources(ss));
            })
            .onFailure(e -> {
                Log.i(TAG, "provider " + type + " initialization error: " + e.getMessage());
                if (e instanceof SecurityException) {
                    Log.i(TAG, "provider " + type + ", needs " + type.permission);
                    permissionsNeeded.add(type.permission);
                }
            });
        }
        initialized = true;
    }

    @NonNull
    public static EventProviderType fromId(int id) {
        for(EventProviderType type : EventProviderType.values()) {
            if(type.id == id) return type;
        }
        return EMPTY;
    }

    @NonNull
    public static Set<String> getNeededPermissions() {
        return permissionsNeeded;
    }

    public static List<OrderedEventSource> getAvailableSources() {
        return sources;
    }

    public static void forget() {
        sources.clear();
        permissionsNeeded.clear();
        initialized = false;
    }

    public EventProvider getEventProvider(Context context, int widgetId) {
        return new EventProvider(this, context, widgetId);
    }

    public WidgetEntryVisualizer<? extends WidgetEntry> getVisualizer(Context context, int widgetId) {
        EventProvider eventProvider = getEventProvider(context, widgetId);
        return isCalendar
            ? new CalendarEventVisualizer(eventProvider)
            : new TaskVisualizer(eventProvider);
    }

    public boolean hasEventSources() {
        for(OrderedEventSource orderedSource: sources) {
            if (orderedSource.source.providerType == this) return true;
        }
        return false;
    }

    public static void registerProviderChangedReceivers(Context context, EnvironmentChangedReceiver receiver) {
        Set<String> registeredAuthorities = new HashSet<>();
        for(EventProviderType type : EventProviderType.values()) {
            String authority = type.authority;
            if (type.hasEventSources() && authority.length() > 0 && !registeredAuthorities.contains(authority)) {
                registeredAuthorities.add(authority);
                registerProviderChangedReceiver(context, receiver, authority);
            }
        }
    }

    private static void registerProviderChangedReceiver(Context context, EnvironmentChangedReceiver receiver,
                                                        String authority) {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.intent.action.PROVIDER_CHANGED");
        intentFilter.addDataScheme("content");
        intentFilter.addDataAuthority(authority, null);
        context.registerReceiver(receiver, intentFilter);
    }

} public static Intent createOpenCalendarAtDayIntent(DateTime goToTime) {
    Intent intent = createViewIntent();
    Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
    builder.appendPath(TIME);
    if (goToTime.getMillis() != 0) {
        intent.putExtra(KEY_DETAIL_VIEW, true);
        ContentUris.appendId(builder, goToTime.getMillis());
    }
    intent.setData(builder.build());
    return intent;
} public static void setBackgroundColor(RemoteViews rv, int viewId, int color) {
    rv.setInt(viewId, METHOD_SET_BACKGROUND_COLOR, color);
} public static void setBackgroundColorFromAttr(Context context, RemoteViews rv, int viewId,
                                              int colorAttrId) {
    setBackgroundColor(rv, viewId, getColorValue(context, colorAttrId));
} public static void setPadding(InstanceSettings settings, RemoteViews rv, @IdRes int viewId,
      @DimenRes int leftDimenId, @DimenRes int topDimenId, @DimenRes int rightDimenId, @DimenRes int bottomDimenId) {
    int leftPadding = getScaledValueInPixels(settings, leftDimenId);
    int topPadding = getScaledValueInPixels(settings, topDimenId);
    int rightPadding = getScaledValueInPixels(settings, rightDimenId);
    int bottomPadding = getScaledValueInPixels(settings, bottomDimenId);
    rv.setViewPadding(viewId, leftPadding, topPadding, rightPadding, bottomPadding);
} public static void setTextColorFromAttr(Context context, RemoteViews rv, int viewId, int colorAttrId) {
    rv.setTextColor(viewId, getColorValue(context, colorAttrId));
} public static void setTextSize(InstanceSettings settings, RemoteViews rv, int viewId, int dimenId) {
    rv.setFloat(viewId, METHOD_SET_TEXT_SIZE, getScaledValueInScaledPixels(settings, dimenId));
}
import android.content.Context;
import android.content.Intent;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.widget.RemoteViews;
import org.andstatus.todoagenda.Alignment;
import org.andstatus.todoagenda.R;
import org.andstatus.todoagenda.prefs.TextShadingPref;
import org.andstatus.todoagenda.provider.EventProvider;
import org.andstatus.todoagenda.provider.EventProviderType;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static org.andstatus.todoagenda.util.CalendarIntentUtil.createOpenCalendarAtDayIntent;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setBackgroundColor;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setBackgroundColorFromAttr;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setPadding;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setTextColorFromAttr;
import static org.andstatus.todoagenda.util.RemoteViewsUtil.setTextSize;
package org.andstatus.todoagenda.widget;





public class DayHeaderVisualizer extends WidgetEntryVisualizer<DayHeader> {

    private final Alignment alignment;
    private final boolean horizontalLineBelowDayHeader;

    public DayHeaderVisualizer(Context context, int widgetId) {
        super(new EventProvider(EventProviderType.EMPTY, context, widgetId));