Allow user to sort by status (#660)

This commit is contained in:
Quentin Hibon
2020-11-20 02:05:21 +01:00
committed by GitHub
parent dc0b8deccf
commit bfddc42f5e
16 changed files with 262 additions and 107 deletions

View File

@@ -31,6 +31,11 @@ import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle;
* While repetitions simply record that the habit was performed at a given date,
* a checkmark provides more information, such as whether a repetition was
* expected at that day or not.
* </p>
* <p>
* Note that the status comparator in
* {@link org.isoron.uhabits.core.models.memory.MemoryHabitList}
* relies on SKIP > YES_MANUAL > YES_AUTO > NO.
* <p>
* Checkmarks are computed automatically from the list of repetitions.
*/

View File

@@ -107,14 +107,23 @@ public abstract class HabitList implements Iterable<Habit>
return observable;
}
public abstract Order getOrder();
public abstract Order getPrimaryOrder();
public abstract Order getSecondaryOrder();
/**
* Changes the order of the elements on the list.
*
* @param order the new order criterea
* @param order the new order criterion
*/
public abstract void setOrder(@NonNull Order order);
public abstract void setPrimaryOrder(@NonNull Order order);
/**
* Changes the previous order of the elements on the list.
*
* @param order the new order criterion
*/
public abstract void setSecondaryOrder(@NonNull Order order);
/**
* Returns the index of the given habit in the list, or -1 if the list does
@@ -242,6 +251,8 @@ public abstract class HabitList implements Iterable<Habit>
csv.close();
}
public enum Order
{
BY_NAME_ASC,
@@ -250,6 +261,8 @@ public abstract class HabitList implements Iterable<Habit>
BY_COLOR_DESC,
BY_SCORE_ASC,
BY_SCORE_DESC,
BY_STATUS_ASC,
BY_STATUS_DESC,
BY_POSITION
}
}

View File

@@ -35,10 +35,13 @@ public class MemoryHabitList extends HabitList
@NonNull
private LinkedList<Habit> list = new LinkedList<>();
private Comparator<Habit> comparator = null;
@NonNull
private Order primaryOrder = Order.BY_POSITION;
@NonNull
private Order order = Order.BY_POSITION;
private Order secondaryOrder = Order.BY_NAME_ASC;
private Comparator<Habit> comparator = getComposedComparatorByOrder(primaryOrder, secondaryOrder);
@Nullable
private MemoryHabitList parent = null;
@@ -55,7 +58,8 @@ public class MemoryHabitList extends HabitList
super(matcher);
this.parent = parent;
this.comparator = comparator;
this.order = parent.order;
this.primaryOrder = parent.primaryOrder;
this.secondaryOrder = parent.secondaryOrder;
parent.getObservable().addListener(this::loadFromParent);
loadFromParent();
}
@@ -105,58 +109,88 @@ public class MemoryHabitList extends HabitList
}
@Override
public synchronized Order getOrder()
public synchronized Order getPrimaryOrder()
{
return order;
return primaryOrder;
}
@Override
public synchronized void setOrder(@NonNull Order order)
public synchronized Order getSecondaryOrder()
{
this.order = order;
this.comparator = getComparatorByOrder(order);
return secondaryOrder;
}
@Override
public synchronized void setPrimaryOrder(@NonNull Order order)
{
this.primaryOrder = order;
this.comparator = getComposedComparatorByOrder(this.primaryOrder, this.secondaryOrder);
resort();
getObservable().notifyListeners();
}
private Comparator<Habit> getComparatorByOrder(Order order)
@Override
public void setSecondaryOrder(@NonNull Order order)
{
Comparator<Habit> nameComparatorAsc =
(h1, h2) -> h1.getName().compareTo(h2.getName());
this.secondaryOrder = order;
this.comparator = getComposedComparatorByOrder(this.primaryOrder, this.secondaryOrder);
resort();
getObservable().notifyListeners();
}
Comparator<Habit> nameComparatorDesc =
(h1, h2) -> nameComparatorAsc.compare(h2, h1);
private Comparator<Habit> getComposedComparatorByOrder(Order firstOrder, Order secondOrder)
{
return (h1, h2) -> {
int firstResult = getComparatorByOrder(firstOrder).compare(h1, h2);
if (firstResult != 0 || secondOrder == null) {
return firstResult;
}
return getComparatorByOrder(secondOrder).compare(h1, h2);
};
}
private Comparator<Habit> getComparatorByOrder(Order order) {
Comparator<Habit> nameComparatorAsc = (h1, h2) ->
h1.getName().compareTo(h2.getName());
Comparator<Habit> nameComparatorDesc = (h1, h2) ->
nameComparatorAsc.compare(h2, h1);
Comparator<Habit> colorComparatorAsc = (h1, h2) ->
{
Integer c1 = h1.getColor();
Integer c2 = h2.getColor();
if (c1.equals(c2)) return nameComparatorAsc.compare(h1, h2);
else return c1.compareTo(c2);
};
h1.getColor().compareTo(h2.getColor());
Comparator<Habit> colorComparatorDesc =
(h1, h2) -> colorComparatorAsc.compare(h2, h1);
Comparator<Habit> colorComparatorDesc = (h1, h2) ->
colorComparatorAsc.compare(h2, h1);
Comparator<Habit> scoreComparatorDesc = (h1, h2) ->
{
Double s1 = h1.getScores().getTodayValue();
Double s2 = h2.getScores().getTodayValue();
if (s1.equals(s2)) return nameComparatorAsc.compare(h1, h2);
else return s2.compareTo(s1);
};
Double.compare(h1.getScores().getTodayValue(), h2.getScores().getTodayValue());
Comparator<Habit> scoreComparatorAsc =
(h1, h2) -> scoreComparatorDesc.compare(h2, h1);
Comparator<Habit> scoreComparatorAsc = (h1, h2) ->
scoreComparatorDesc.compare(h2, h1);
Comparator<Habit> positionComparator = (h1, h2) ->
h1.getPosition().compareTo(h2.getPosition());
Comparator<Habit> statusComparatorDesc = (h1, h2) ->
{
Integer p1 = h1.getPosition();
Integer p2 = h2.getPosition();
if (p1.equals(p2)) return nameComparatorAsc.compare(h1, h2);
else return p1.compareTo(p2);
if (h1.isCompletedToday() != h2.isCompletedToday()) {
return h1.isCompletedToday() ? -1 : 1;
}
if (h1.isNumerical() != h2.isNumerical()) {
return h1.isNumerical() ? -1 : 1;
}
Integer v1 = Objects.requireNonNull(h1.getCheckmarks().getToday()).getValue();
Integer v2 = Objects.requireNonNull(h2.getCheckmarks().getToday()).getValue();
return v2.compareTo(v1);
};
Comparator<Habit> statusComparatorAsc = (h1, h2) -> statusComparatorDesc.compare(h2, h1);
if (order == BY_POSITION) return positionComparator;
if (order == BY_NAME_ASC) return nameComparatorAsc;
if (order == BY_NAME_DESC) return nameComparatorDesc;
@@ -164,6 +198,8 @@ public class MemoryHabitList extends HabitList
if (order == BY_COLOR_DESC) return colorComparatorDesc;
if (order == BY_SCORE_DESC) return scoreComparatorDesc;
if (order == BY_SCORE_ASC) return scoreComparatorAsc;
if (order == BY_STATUS_DESC) return statusComparatorDesc;
if (order == BY_STATUS_ASC) return statusComparatorAsc;
throw new IllegalStateException();
}
@@ -192,7 +228,7 @@ public class MemoryHabitList extends HabitList
public synchronized void reorder(@NonNull Habit from, @NonNull Habit to)
{
throwIfHasParent();
if (order != BY_POSITION) throw new IllegalStateException(
if (primaryOrder != BY_POSITION) throw new IllegalStateException(
"cannot reorder automatically sorted list");
if (indexOf(from) < 0) throw new IllegalArgumentException(

View File

@@ -116,15 +116,28 @@ public class SQLiteHabitList extends HabitList
@Override
@NonNull
public Order getOrder()
public Order getPrimaryOrder()
{
return list.getOrder();
return list.getPrimaryOrder();
}
@Override
public synchronized void setOrder(@NonNull Order order)
public Order getSecondaryOrder()
{
list.setOrder(order);
return list.getSecondaryOrder();
}
@Override
public synchronized void setPrimaryOrder(@NonNull Order order)
{
list.setPrimaryOrder(order);
getObservable().notifyListeners();
}
@Override
public synchronized void setSecondaryOrder(@NonNull Order order)
{
list.setSecondaryOrder(order);
getObservable().notifyListeners();
}

View File

@@ -60,7 +60,7 @@ public class Preferences
fallbackColor);
}
public HabitList.Order getDefaultOrder()
public HabitList.Order getDefaultPrimaryOrder()
{
String name = storage.getString("pref_default_order", "BY_POSITION");
@@ -70,16 +70,35 @@ public class Preferences
}
catch (IllegalArgumentException e)
{
setDefaultOrder(HabitList.Order.BY_POSITION);
setDefaultPrimaryOrder(HabitList.Order.BY_POSITION);
return HabitList.Order.BY_POSITION;
}
}
public void setDefaultOrder(HabitList.Order order)
public HabitList.Order getDefaultSecondaryOrder() {
String name = storage.getString("pref_default_secondary_order", "BY_NAME_ASC");
try
{
return HabitList.Order.valueOf(name);
}
catch (IllegalArgumentException e)
{
setDefaultSecondaryOrder(HabitList.Order.BY_NAME_ASC);
return HabitList.Order.BY_POSITION;
}
}
public void setDefaultPrimaryOrder(HabitList.Order order)
{
storage.putString("pref_default_order", order.name());
}
public void setDefaultSecondaryOrder(HabitList.Order order)
{
storage.putString("pref_default_secondary_order", order.name());
}
public int getDefaultScoreSpinnerPosition()
{
int defaultScoreInterval =

View File

@@ -116,9 +116,14 @@ public class HabitCardListCache implements CommandRunner.Listener
return data.habits.size();
}
public synchronized HabitList.Order getOrder()
public synchronized HabitList.Order getPrimaryOrder()
{
return filteredHabits.getOrder();
return filteredHabits.getPrimaryOrder();
}
public synchronized HabitList.Order getSecondaryOrder()
{
return filteredHabits.getSecondaryOrder();
}
public synchronized double getScore(long habitId)
@@ -196,14 +201,22 @@ public class HabitCardListCache implements CommandRunner.Listener
this.listener = listener;
}
public synchronized void setOrder(@NonNull HabitList.Order order)
public synchronized void setPrimaryOrder(@NonNull HabitList.Order order)
{
if (order == null) throw new NullPointerException();
allHabits.setOrder(order);
filteredHabits.setOrder(order);
allHabits.setPrimaryOrder(order);
filteredHabits.setPrimaryOrder(order);
refreshAllHabits();
}
public synchronized void setSecondaryOrder(@NonNull HabitList.Order order)
{
allHabits.setSecondaryOrder(order);
filteredHabits.setSecondaryOrder(order);
refreshAllHabits();
}
/**
* Interface definition for a callback to be invoked when the data on the
* cache has been modified.

View File

@@ -95,36 +95,43 @@ public class ListHabitsMenuBehavior
updateAdapterFilter();
}
public void onSortByColor()
{
if (adapter.getOrder() != HabitList.Order.BY_COLOR_ASC) {
adapter.setOrder(HabitList.Order.BY_COLOR_ASC);
} else {
adapter.setOrder(HabitList.Order.BY_COLOR_DESC);
}
}
public void onSortByManually()
{
adapter.setOrder(HabitList.Order.BY_POSITION);
adapter.setPrimaryOrder(HabitList.Order.BY_POSITION);
}
public void onSortByColor()
{
onSortToggleBy(HabitList.Order.BY_COLOR_ASC, HabitList.Order.BY_COLOR_DESC);
}
public void onSortByScore()
{
if (adapter.getOrder() != HabitList.Order.BY_SCORE_DESC) {
adapter.setOrder(HabitList.Order.BY_SCORE_DESC);
} else {
adapter.setOrder(HabitList.Order.BY_SCORE_ASC);
}
onSortToggleBy(HabitList.Order.BY_SCORE_DESC, HabitList.Order.BY_SCORE_ASC);
}
public void onSortByName()
{
if (adapter.getOrder() != HabitList.Order.BY_NAME_ASC) {
adapter.setOrder(HabitList.Order.BY_NAME_ASC);
onSortToggleBy(HabitList.Order.BY_NAME_ASC, HabitList.Order.BY_NAME_DESC);
}
public void onSortByStatus()
{
onSortToggleBy(HabitList.Order.BY_STATUS_ASC, HabitList.Order.BY_STATUS_DESC);
}
private void onSortToggleBy(HabitList.Order defaultOrder, HabitList.Order reversedOrder)
{
if (adapter.getPrimaryOrder() != defaultOrder) {
if (adapter.getPrimaryOrder() != reversedOrder) {
adapter.setSecondaryOrder(adapter.getPrimaryOrder());
}
adapter.setPrimaryOrder(defaultOrder);
} else {
adapter.setOrder(HabitList.Order.BY_NAME_DESC);
adapter.setPrimaryOrder(reversedOrder);
}
}
public void onToggleNightMode()
@@ -148,9 +155,11 @@ public class ListHabitsMenuBehavior
void setFilter(HabitMatcher build);
void setOrder(HabitList.Order order);
void setPrimaryOrder(HabitList.Order order);
HabitList.Order getOrder();
void setSecondaryOrder(HabitList.Order order);
HabitList.Order getPrimaryOrder();
}
public interface Screen