Skip to content

Commit

Permalink
Added separate sorting for calendar entries
Browse files Browse the repository at this point in the history
  • Loading branch information
dgudim committed Aug 31, 2023
1 parent 5670460 commit c49c9d0
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 72 deletions.
36 changes: 27 additions & 9 deletions app/src/main/java/prototype/xd/scheduler/entities/TodoEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import static androidx.core.math.MathUtils.clamp;
import static java.lang.Math.abs;
import static prototype.xd.scheduler.entities.Group.NULL_GROUP;
import static prototype.xd.scheduler.utilities.ColorUtilities.getExpiredUpcomingColor;
import static prototype.xd.scheduler.utilities.ColorUtilities.mixColorWithBg;
import static prototype.xd.scheduler.utilities.DateManager.currentDayUTC;
import static prototype.xd.scheduler.utilities.DateManager.currentTimestampUTC;
import static prototype.xd.scheduler.utilities.DateManager.msToDays;
import static prototype.xd.scheduler.utilities.DateManager.msUTCtoDaysLocal;
import static prototype.xd.scheduler.utilities.ColorUtilities.getExpiredUpcomingColor;
import static prototype.xd.scheduler.utilities.ColorUtilities.mixColorWithBg;
import static prototype.xd.scheduler.utilities.Static.TIME_RANGE_SEPARATOR;
import static prototype.xd.scheduler.utilities.Utilities.doRangesOverlap;
import static prototype.xd.scheduler.utilities.Utilities.getPluralString;
Expand Down Expand Up @@ -39,9 +39,9 @@
import prototype.xd.scheduler.BuildConfig;
import prototype.xd.scheduler.R;
import prototype.xd.scheduler.utilities.DateManager;
import prototype.xd.scheduler.utilities.Static;
import prototype.xd.scheduler.utilities.Logger;
import prototype.xd.scheduler.utilities.SArrayMap;
import prototype.xd.scheduler.utilities.Static;
import prototype.xd.scheduler.utilities.Utilities;
import prototype.xd.scheduler.views.CalendarView;

Expand Down Expand Up @@ -158,7 +158,22 @@ public void invalidate() {
}


public enum EntryType {TODAY, GLOBAL, UPCOMING, EXPIRED, UNKNOWN}
public enum EntryType {TODAY, TODAY_CALENDAR, GLOBAL, UPCOMING, UPCOMING_CALENDAR, EXPIRED, EXPIRED_CALENDAR, UNKNOWN;
public EntryType extend(final TodoEntry entry) {
if(entry.isFromSystemCalendar()) {
switch (this) {
case TODAY:
return TODAY_CALENDAR;
case UPCOMING:
return UPCOMING_CALENDAR;
case EXPIRED:
return EXPIRED_CALENDAR;
default:
}
}
return this;
}
}

public static class TimeRange {

Expand Down Expand Up @@ -252,11 +267,13 @@ public int getTypeSortingIndex() {
}

public void cacheTypeSortingIndex(long targetDayUTC, @NonNull List<EntryType> order) {
typeSortingIndex = order.indexOf(getEntryType(targetDayUTC));
if (typeSortingIndex == -1) {
// fallback to treating like today's
typeSortingIndex = order.indexOf(EntryType.TODAY);
}
EntryType entryType = getEntryType(targetDayUTC);
EntryType extendedEntryType = entryType.extend(this);
typeSortingIndex = order.indexOf(extendedEntryType);
// fallback to non-extended type
typeSortingIndex = typeSortingIndex == -1 ? order.indexOf(entryType) : typeSortingIndex;
// fallback to Today's
typeSortingIndex = typeSortingIndex == -1 ? order.indexOf(EntryType.TODAY) : typeSortingIndex;
}

long cachedNearestStartMsUTC;
Expand Down Expand Up @@ -330,6 +347,7 @@ public void initParameters() {
bgColor = new Parameter<>(this, Static.BG_COLOR.CURRENT.key,
previousValue -> {
if (isFromSystemCalendar()) {
// get only by subkeys because the base key always exists, but we don't want it, and instead want calendar color
return Static.BG_COLOR.CURRENT.getOnlyBySubKeys(event.subKeys,
Static.SETTINGS_DEFAULT_CALENDAR_EVENT_BG_COLOR.applyAsInt(getCalendarEventColor()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import static prototype.xd.scheduler.utilities.ColorUtilities.getOnBgColor;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.view.LayoutInflater;
Expand All @@ -17,6 +16,7 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
Expand All @@ -31,8 +31,10 @@
import prototype.xd.scheduler.databinding.DraggableListEntryBinding;
import prototype.xd.scheduler.databinding.SortingSettingsFragmentBinding;
import prototype.xd.scheduler.entities.TodoEntry;
import prototype.xd.scheduler.entities.settings_entries.DividerSettingsEntryConfig;
import prototype.xd.scheduler.entities.settings_entries.SettingsEntryConfig;
import prototype.xd.scheduler.entities.settings_entries.SwitchSettingsEntryConfig;
import prototype.xd.scheduler.utilities.Logger;
import prototype.xd.scheduler.utilities.Static;

public class SortingSettingsFragment extends BaseSettingsFragment<SortingSettingsFragmentBinding> {
Expand All @@ -54,8 +56,11 @@ public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstan

List<SettingsEntryConfig> settingsEntries = List.of(
new SwitchSettingsEntryConfig(
Static.TREAT_GLOBAL_ITEMS_AS_TODAYS, getString(R.string.treat_global_as_todays),
(buttonView, isChecked) -> entryTypeAdapter.setGlobalEventsVisible(!isChecked), true));
Static.SORTING_TREAT_GLOBAL_ITEMS_AS_TODAYS, R.string.sorting_treat_global_as_todays,
(buttonView, isChecked) -> entryTypeAdapter.setGlobalEventsVisible(!isChecked), true),
new DividerSettingsEntryConfig(),
new SwitchSettingsEntryConfig(Static.SORTING_SORT_CALENDAR_SEPARATELY, R.string.sorting_sort_calendar_separately,
(buttonView, isChecked) -> entryTypeAdapter.setCalendarEventsVisible(isChecked), true));

binding.settingsRecyclerView.setLayoutManager(new LinearLayoutManager(wrapper.context));
MaterialDividerItemDecoration divider = new MaterialDividerItemDecoration(wrapper.context, LinearLayout.VERTICAL);
Expand All @@ -66,36 +71,58 @@ Static.TREAT_GLOBAL_ITEMS_AS_TODAYS, getString(R.string.treat_global_as_todays),

private static final class EntryTypeAdapter extends RecyclerView.Adapter<EntryTypeAdapter.CardViewHolder> {

public static final String NAME = EntryTypeAdapter.class.getSimpleName();

@NonNull
private final ItemTouchHelper itemDragHelper;
@NonNull
private final List<TodoEntry.EntryType> sortOrder;

private boolean globalEventsVisible;

private EntryTypeAdapter() {
itemDragHelper = new ItemTouchHelper(new DragHelperCallback(this));
sortOrder = Static.TODO_ITEM_SORTING_ORDER.get();
globalEventsVisible = sortOrder.contains(TodoEntry.EntryType.GLOBAL);
sortOrder = Static.TODO_ITEM_SORTING_ORDER.getUnique();
addEntryTypeIfNeeded(TodoEntry.EntryType.UPCOMING);
addEntryTypeIfNeeded(TodoEntry.EntryType.TODAY);
addEntryTypeIfNeeded(TodoEntry.EntryType.EXPIRED);
}

public void attachDragToRecyclerView(RecyclerView recyclerView) {
itemDragHelper.attachToRecyclerView(recyclerView);
}

public void setGlobalEventsVisible(boolean globalEventsVisible) {
if (globalEventsVisible && !this.globalEventsVisible) {
sortOrder.add(TodoEntry.EntryType.GLOBAL);
notifyItemInserted(sortOrder.size());
} else if (!globalEventsVisible && this.globalEventsVisible) {
int indexOfGlobal = sortOrder.indexOf(TodoEntry.EntryType.GLOBAL);
sortOrder.remove(indexOfGlobal);
notifyItemRemoved(indexOfGlobal);
addOrRemoveEntryType(TodoEntry.EntryType.GLOBAL, globalEventsVisible);
Static.TODO_ITEM_SORTING_ORDER.put(sortOrder);
}

public void setCalendarEventsVisible(boolean calendarEventsVisible) {
addOrRemoveEntryType(TodoEntry.EntryType.TODAY_CALENDAR, calendarEventsVisible);
addOrRemoveEntryType(TodoEntry.EntryType.UPCOMING_CALENDAR, calendarEventsVisible);
addOrRemoveEntryType(TodoEntry.EntryType.EXPIRED_CALENDAR, calendarEventsVisible);
Static.TODO_ITEM_SORTING_ORDER.put(sortOrder);
}

private void addOrRemoveEntryType(@NonNull TodoEntry.EntryType type, boolean add) {
if (add) {
addEntryTypeIfNeeded(type);
} else {
int index = sortOrder.indexOf(type);
if (index == -1) {
Logger.warning(NAME, "Can't remove " + type);
return;
}
sortOrder.remove(index);
notifyItemRemoved(index);
}
if (this.globalEventsVisible != globalEventsVisible) {
Static.TODO_ITEM_SORTING_ORDER.put(sortOrder);
}

private void addEntryTypeIfNeeded(@NonNull TodoEntry.EntryType type) {
if (sortOrder.contains(type)) {
Logger.warning(NAME, "Not adding duplicate type: " + type);
return;
}
this.globalEventsVisible = globalEventsVisible;
sortOrder.add(type);
notifyItemInserted(sortOrder.size());
}

@NonNull
Expand Down Expand Up @@ -126,61 +153,89 @@ private static final class CardViewHolder extends RecyclerView.ViewHolder {

@NonNull
private final DraggableListEntryBinding binding;
@NonNull
private final Context context;

private CardViewHolder(@NonNull DraggableListEntryBinding binding) {
super(binding.getRoot());
this.binding = binding;
context = binding.getRoot().getContext();
}

@SuppressLint("ClickableViewAccessibility")
private void bind(@NonNull TodoEntry.EntryType entryType, @NonNull final ItemTouchHelper dragHelper) {
String titleText;
String descriptionText;
@StringRes int titleTextRes;
@StringRes int descriptionTextRes;
int bgColor = Static.BG_COLOR.CURRENT.get();
int fontColor = Static.FONT_COLOR.CURRENT.get();
int borderColor = Static.BORDER_COLOR.CURRENT.get();
switch (entryType) {
case UPCOMING:
titleText = context.getString(R.string.upcoming_events);
descriptionText = context.getString(R.string.upcoming_events_description);
case UPCOMING_CALENDAR:
titleTextRes = R.string.upcoming_events;
descriptionTextRes = R.string.upcoming_events_description;
bgColor = getExpiredUpcomingColor(bgColor, Static.BG_COLOR.UPCOMING.get());
fontColor = getExpiredUpcomingColor(fontColor, Static.FONT_COLOR.UPCOMING.get());
borderColor = getExpiredUpcomingColor(borderColor, Static.BORDER_COLOR.UPCOMING.get());
break;
case EXPIRED:
titleText = context.getString(R.string.expired_events);
descriptionText = context.getString(R.string.expired_events_description);
case EXPIRED_CALENDAR:
titleTextRes = R.string.expired_events;
descriptionTextRes = R.string.expired_events_description;
bgColor = getExpiredUpcomingColor(bgColor, Static.BG_COLOR.EXPIRED.get());
fontColor = getExpiredUpcomingColor(fontColor, Static.FONT_COLOR.EXPIRED.get());
borderColor = getExpiredUpcomingColor(borderColor, Static.BORDER_COLOR.EXPIRED.get());
break;
case TODAY:
titleText = context.getString(R.string.todays_events);
descriptionText = context.getString(R.string.todays_events_description);
titleTextRes = R.string.todays_events;
descriptionTextRes = R.string.todays_events_description;
break;
case GLOBAL:
titleText = context.getString(R.string.global_events);
descriptionText = context.getString(R.string.global_events_description);
titleTextRes = R.string.global_events;
descriptionTextRes = R.string.global_events_description;
break;
case UNKNOWN:
default:
titleText = "ERR/UNKNOWN";
descriptionText = "ERR/UNKNOWN";
titleTextRes = -1;
descriptionTextRes = -1;
}

// extra stuff for calendar entries
ColorStateList onBgColor = ColorStateList.valueOf(getOnBgColor(bgColor));
boolean isCalendar = false;
switch (entryType) {
case UPCOMING_CALENDAR:
titleTextRes = R.string.upcoming_calendar_events;
descriptionTextRes = R.string.upcoming_calendar_events_description;
isCalendar = true;
break;
case EXPIRED_CALENDAR:
titleTextRes = R.string.expired_calendar_events;
descriptionTextRes = R.string.expired_calendar_events_description;
isCalendar = true;
break;
case TODAY_CALENDAR:
titleTextRes = R.string.todays_calendar_events;
descriptionTextRes = R.string.todays_calendar_events_description;
isCalendar = true;
break;
default:
}

if (isCalendar) {
binding.calendarIcon.setVisibility(View.VISIBLE);
binding.calendarIcon.setImageTintList(onBgColor);
} else {
binding.calendarIcon.setVisibility(View.GONE);
}

binding.itemText.setText(titleText);
binding.itemText.setText(titleTextRes);
binding.itemText.setTextColor(getHarmonizedFontColorWithBg(fontColor, bgColor));

binding.itemDescriptionText.setText(descriptionText);
binding.itemDescriptionText.setText(descriptionTextRes);
binding.itemDescriptionText.setTextColor(getHarmonizedSecondaryFontColorWithBg(fontColor, bgColor));

binding.card.setCardBackgroundColor(bgColor);
binding.card.setStrokeColor(borderColor);

binding.dragHandle.setImageTintList(ColorStateList.valueOf(getOnBgColor(bgColor)));
binding.dragHandle.setImageTintList(onBgColor);
binding.dragHandle.setOnTouchListener(
(v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Expand Down
32 changes: 21 additions & 11 deletions app/src/main/java/prototype/xd/scheduler/utilities/Static.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -25,6 +28,7 @@
import java.util.Objects;
import java.util.function.IntUnaryOperator;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

import kotlin.jvm.functions.Function2;
import prototype.xd.scheduler.BuildConfig;
Expand Down Expand Up @@ -221,20 +225,25 @@ protected List<T> getInternal(@NonNull String actualKey, @NonNull List<T> actual
String[] stringList = stringValue.split(delimiter);
List<T> list = new ArrayList<>(stringList.length);
for (String value : stringList) {
list.add(Enum.valueOf(enumClass, value));
try {
list.add(Enum.valueOf(enumClass, value));
} catch (IllegalArgumentException e) {
Logger.logException(NAME, e);
}
}
return list;
}

@NonNull
public List<T> getUnique() {
return Lists.newArrayList(ImmutableSet.copyOf(getInternal(key, defaultValue)));
}

@Override
public void put(@NonNull List<T> enumList) {
StringBuilder stringValue = new StringBuilder(enumList.get(0).name());
for (int i = 1; i < enumList.size(); i++) {
stringValue
.append(delimiter)
.append(enumList.get(i).name());
}
preferences.edit().putString(key, stringValue.toString()).apply();
preferences.edit().putString(
key,
enumList.stream().map(Enum::toString).collect(Collectors.joining(delimiter))).apply();
}

@Override
Expand Down Expand Up @@ -298,7 +307,7 @@ public static synchronized void init(@NonNull Context context) {
Logger.info(NAME, "Created folder structure: " + rootDir.mkdirs());
}
}

// init logger
Logger.setDebugEnabled(Static.DEBUG_LOGGING.get() || BuildConfig.DEBUG);

Expand Down Expand Up @@ -446,8 +455,9 @@ public static void clearBitmapUpdateFlag() {
TodoEntry.EntryType.UPCOMING,
TodoEntry.EntryType.TODAY,
TodoEntry.EntryType.EXPIRED),
"_", TodoEntry.EntryType.class);
public static final DefaultedBoolean TREAT_GLOBAL_ITEMS_AS_TODAYS = new DefaultedBoolean("treat_global_as_todays", false);
"/", TodoEntry.EntryType.class);
public static final DefaultedBoolean SORTING_TREAT_GLOBAL_ITEMS_AS_TODAYS = new DefaultedBoolean("treat_global_as_todays", false);
public static final DefaultedBoolean SORTING_SORT_CALENDAR_SEPARATELY = new DefaultedBoolean("sort_calendar_separately", false);

public static final int APP_THEME_LIGHT = MODE_NIGHT_NO;
public static final int APP_THEME_DARK = MODE_NIGHT_YES;
Expand Down
Loading

0 comments on commit c49c9d0

Please sign in to comment.