/* * Copyright (c) 2012 Todoroo Inc * * See the file "LICENSE" for the full license governing this code. */ package com.todoroo.astrid.core; import static org.tasks.db.QueryUtils.showCompleted; import static org.tasks.db.QueryUtils.showHidden; import android.annotation.SuppressLint; import androidx.annotation.Nullable; import com.todoroo.andlib.sql.Functions; import com.todoroo.andlib.sql.Order; import com.todoroo.astrid.data.Task; import org.tasks.preferences.QueryPreferences; /** * Helpers for sorting a list of tasks * * @author Tim Su */ public class SortHelper { public static final int SORT_AUTO = 0; public static final int SORT_ALPHA = 1; public static final int SORT_DUE = 2; public static final int SORT_IMPORTANCE = 3; public static final int SORT_MODIFIED = 4; public static final int SORT_CREATED = 5; public static final int SORT_GTASKS = 6; public static final int SORT_CALDAV = 7; public static final int SORT_START = 8; public static final long APPLE_EPOCH = 978307200000L; // 1/1/2001 GMT @SuppressLint("DefaultLocale") public static final String CALDAV_ORDER_COLUMN = String.format("IFNULL(tasks.`order`, (tasks.created - %d) / 1000)", APPLE_EPOCH); private static final String ADJUSTED_DUE_DATE = "(CASE WHEN (dueDate / 1000) % 60 > 0 THEN dueDate ELSE (dueDate + 43140000) END)"; private static final String ADJUSTED_START_DATE = "(CASE WHEN (hideUntil / 1000) % 60 > 0 THEN hideUntil ELSE (hideUntil + 86399000) END)"; private static final Order ORDER_TITLE = Order.asc(Functions.upper(Task.TITLE)); /** Takes a SQL query, and if there isn't already an order, creates an order. */ public static String adjustQueryForFlagsAndSort( QueryPreferences preferences, String originalSql, int sort) { // sort if (originalSql == null) { originalSql = ""; } if (!originalSql.toUpperCase().contains("ORDER BY")) { Order order = orderForSortType(sort); if (preferences.isReverseSort()) { order = order.reverse(); } originalSql += " ORDER BY " + order; } return adjustQueryForFlags(preferences, originalSql); } public static String adjustQueryForFlags(QueryPreferences preferences, String originalSql) { String adjustedSql = originalSql; // flags if (preferences.getShowCompleted()) { adjustedSql = showCompleted(adjustedSql); } if (preferences.getShowHidden()) { adjustedSql = showHidden(adjustedSql); } return adjustedSql; } private static Order orderForSortType(int sortType) { Order order; switch (sortType) { case SORT_ALPHA: order = ORDER_TITLE; break; case SORT_DUE: order = Order.asc( "(CASE WHEN (dueDate=0) THEN (strftime('%s','now')*1000)*2 ELSE " + ADJUSTED_DUE_DATE + " END)+importance"); break; case SORT_START: order = Order.asc( "(CASE WHEN (hideUntil=0) THEN (strftime('%s','now')*1000)*2 ELSE " + ADJUSTED_START_DATE + " END)+importance"); break; case SORT_IMPORTANCE: order = Order.asc( "importance*(strftime('%s','now')*1000)+(CASE WHEN (dueDate=0) THEN (strftime('%s','now')*1000) ELSE dueDate END)"); break; case SORT_MODIFIED: order = Order.desc(Task.MODIFICATION_DATE); break; case SORT_CREATED: order = Order.desc(Task.CREATION_DATE); break; default: order = Order.asc( "(CASE WHEN (dueDate=0) " + // if no due date "THEN (strftime('%s','now')*1000)*2 " + // then now * 2 "ELSE (" + ADJUSTED_DUE_DATE + ") END) " + // else due time // add slightly less than 2 days * importance to give due date priority over importance in case of tie "+ 172799999 * importance"); } if (sortType != SORT_ALPHA) { order.addSecondaryExpression(ORDER_TITLE); } return order; } public static @Nullable String getSortGroup(int sortType) { switch (sortType) { case SORT_DUE: return "tasks.dueDate"; case SORT_START: return "tasks.hideUntil"; case SORT_IMPORTANCE: return "tasks.importance"; case SORT_MODIFIED: return "tasks.modified"; case SORT_CREATED: return "tasks.created"; default: return null; } } public static String orderSelectForSortTypeRecursive(int sortType) { String select; switch (sortType) { case SORT_ALPHA: // Return an empty string, providing a value to fill the WITH clause template select = "''"; break; case SORT_DUE: select = "(CASE WHEN (tasks.dueDate=0) THEN (strftime('%s','now')*1000)*2 ELSE " + ADJUSTED_DUE_DATE.replace("dueDate", "tasks.dueDate") + " END)+tasks.importance AS sort_duedate"; break; case SORT_START: select = "(CASE WHEN (tasks.hideUntil=0) THEN (strftime('%s','now')*1000)*2 ELSE " + ADJUSTED_START_DATE.replace("hideUntil", "tasks.hideUntil") + " END)+tasks.importance AS sort_startdate"; break; case SORT_IMPORTANCE: select = "tasks.importance*(strftime('%s','now')*1000)+(CASE WHEN (tasks.dueDate=0) THEN (strftime('%s','now')*1000) ELSE tasks.dueDate END) AS sort_importance"; break; case SORT_MODIFIED: select = "tasks.modified AS sort_modified"; break; case SORT_CREATED: select = "tasks.created AS sort_created"; break; case SORT_GTASKS: select = "tasks.`order` AS sort_manual"; break; case SORT_CALDAV: select = CALDAV_ORDER_COLUMN + " AS sort_manual"; break; default: select ="(CASE WHEN (tasks.dueDate=0) " + // if no due date "THEN (strftime('%s','now')*1000)*2 " + // then now * 2 "ELSE (" + ADJUSTED_DUE_DATE.replace("dueDate", "tasks.dueDate") + ") END) " + // else due time // add slightly less than 2 days * importance to give due date priority over importance in case of tie "+ 172799999 * tasks.importance AS sort_smart"; } return select; } public static Order orderForSortTypeRecursive(int sortMode, boolean reverse) { Order order; switch (sortMode) { case SORT_ALPHA: order = Order.asc("sort_title"); break; case SORT_DUE: order = Order.asc("sort_duedate"); break; case SORT_START: order = Order.asc("sort_startdate"); break; case SORT_IMPORTANCE: order = Order.asc("sort_importance"); break; case SORT_MODIFIED: order = Order.desc("sort_modified"); break; case SORT_CREATED: order = Order.desc("sort_created"); break; case SORT_GTASKS: case SORT_CALDAV: order = Order.asc("sort_manual"); break; default: order = Order.asc("sort_smart"); } if (sortMode != SORT_ALPHA) { order.addSecondaryExpression(Order.asc("sort_title")); } if (reverse) { order = order.reverse(); } return order; } }