mirror of https://github.com/tasks/tasks
Move query builders to kmp
parent
bf4167651b
commit
79ebc9a2c7
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
|
||||
package com.todoroo.astrid.api;
|
||||
|
||||
import static org.tasks.date.DateTimeUtils.newDateTime;
|
||||
import static org.tasks.time.DateTimeUtils2.currentTimeMillis;
|
||||
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import org.tasks.time.DateTime;
|
||||
|
||||
/**
|
||||
* PermaSql allows for creating SQL statements that can be saved and used later without dates
|
||||
* getting stale. It also allows these values to be used in
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*/
|
||||
public final class PermaSql {
|
||||
|
||||
// --- placeholder strings
|
||||
|
||||
/** value to be replaced by end of day as long */
|
||||
public static final String VALUE_EOD = "EOD()"; // $NON-NLS-1$
|
||||
/** value to be replaced by noon today as long */
|
||||
public static final String VALUE_NOON = "NOON()"; // $NON-NLS-1$
|
||||
/** value to be replaced by end of day yesterday as long */
|
||||
public static final String VALUE_EOD_YESTERDAY = "EODY()"; // $NON-NLS-1$
|
||||
/** value to be replaced by end of day tomorrow as long */
|
||||
public static final String VALUE_EOD_TOMORROW = "EODT()"; // $NON-NLS-1$
|
||||
/** value to be replaced by end of day day after tomorrow as long */
|
||||
public static final String VALUE_EOD_DAY_AFTER = "EODTT()"; // $NON-NLS-1$
|
||||
/** value to be replaced by end of day next week as long */
|
||||
public static final String VALUE_EOD_NEXT_WEEK = "EODW()"; // $NON-NLS-1$
|
||||
/** value to be replaced by approximate end of day next month as long */
|
||||
public static final String VALUE_EOD_NEXT_MONTH = "EODM()"; // $NON-NLS-1$
|
||||
/** value to be replaced with the current time as long */
|
||||
public static final String VALUE_NOW = "NOW()"; // $NON-NLS-1$
|
||||
/** value to be replaced by noon yesterday as long */
|
||||
private static final String VALUE_NOON_YESTERDAY = "NOONY()"; // $NON-NLS-1$
|
||||
/** value to be replaced by noon tomorrow as long */
|
||||
private static final String VALUE_NOON_TOMORROW = "NOONT()"; // $NON-NLS-1$
|
||||
/** value to be replaced by noon day after tomorrow as long */
|
||||
private static final String VALUE_NOON_DAY_AFTER = "NOONTT()"; // $NON-NLS-1$
|
||||
/** value to be replaced by noon next week as long */
|
||||
private static final String VALUE_NOON_NEXT_WEEK = "NOONW()"; // $NON-NLS-1$
|
||||
/** value to be replaced by approximate noon next month as long */
|
||||
private static final String VALUE_NOON_NEXT_MONTH = "NOONM()"; // $NON-NLS-1$
|
||||
|
||||
/** Replace placeholder strings with actual */
|
||||
public static String replacePlaceholdersForQuery(String value) {
|
||||
if (value.contains(VALUE_NOW)) {
|
||||
value = value.replace(VALUE_NOW, Long.toString(currentTimeMillis()));
|
||||
}
|
||||
if (value.contains(VALUE_EOD)
|
||||
|| value.contains(VALUE_EOD_DAY_AFTER)
|
||||
|| value.contains(VALUE_EOD_NEXT_WEEK)
|
||||
|| value.contains(VALUE_EOD_TOMORROW)
|
||||
|| value.contains(VALUE_EOD_YESTERDAY)
|
||||
|| value.contains(VALUE_EOD_NEXT_MONTH)) {
|
||||
value = replaceEodValues(value);
|
||||
}
|
||||
if (value.contains(VALUE_NOON)
|
||||
|| value.contains(VALUE_NOON_DAY_AFTER)
|
||||
|| value.contains(VALUE_NOON_NEXT_WEEK)
|
||||
|| value.contains(VALUE_NOON_TOMORROW)
|
||||
|| value.contains(VALUE_NOON_YESTERDAY)
|
||||
|| value.contains(VALUE_NOON_NEXT_MONTH)) {
|
||||
value = replaceNoonValues(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String replacePlaceholdersForNewTask(String value) {
|
||||
if (value.contains(VALUE_NOW)) {
|
||||
value = value.replace(VALUE_NOW, Long.toString(currentTimeMillis()));
|
||||
}
|
||||
if (value.contains(VALUE_EOD)
|
||||
|| value.contains(VALUE_EOD_DAY_AFTER)
|
||||
|| value.contains(VALUE_EOD_NEXT_WEEK)
|
||||
|| value.contains(VALUE_EOD_TOMORROW)
|
||||
|| value.contains(VALUE_EOD_YESTERDAY)
|
||||
|| value.contains(VALUE_EOD_NEXT_MONTH)) {
|
||||
value = replaceEodValues(value, newDateTime().noon());
|
||||
}
|
||||
if (value.contains(VALUE_NOON)
|
||||
|| value.contains(VALUE_NOON_DAY_AFTER)
|
||||
|| value.contains(VALUE_NOON_NEXT_WEEK)
|
||||
|| value.contains(VALUE_NOON_TOMORROW)
|
||||
|| value.contains(VALUE_NOON_YESTERDAY)
|
||||
|| value.contains(VALUE_NOON_NEXT_MONTH)) {
|
||||
value = replaceNoonValues(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String replaceEodValues(String value) {
|
||||
return replaceEodValues(value, newDateTime().endOfDay());
|
||||
}
|
||||
|
||||
private static String replaceEodValues(String value, DateTime dateTime) {
|
||||
long time = dateTime.getMillis();
|
||||
value = value.replace(VALUE_EOD_YESTERDAY, Long.toString(time - DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_EOD, Long.toString(time));
|
||||
value = value.replace(VALUE_EOD_TOMORROW, Long.toString(time + DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_EOD_DAY_AFTER, Long.toString(time + 2 * DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_EOD_NEXT_WEEK, Long.toString(time + 7 * DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_EOD_NEXT_MONTH, Long.toString(time + 30 * DateUtilities.ONE_DAY));
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String replaceNoonValues(String value) {
|
||||
long time = newDateTime().noon().getMillis();
|
||||
value = value.replace(VALUE_NOON_YESTERDAY, Long.toString(time - DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_NOON, Long.toString(time));
|
||||
value = value.replace(VALUE_NOON_TOMORROW, Long.toString(time + DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_NOON_DAY_AFTER, Long.toString(time + 2 * DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_NOON_NEXT_WEEK, Long.toString(time + 7 * DateUtilities.ONE_DAY));
|
||||
value = value.replace(VALUE_NOON_NEXT_MONTH, Long.toString(time + 30 * DateUtilities.ONE_DAY));
|
||||
return value;
|
||||
}
|
||||
}
|
@ -1,238 +0,0 @@
|
||||
/*
|
||||
* 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.data.dao.CaldavDaoKt.APPLE_EPOCH;
|
||||
import static org.tasks.db.QueryUtils.showCompleted;
|
||||
import static org.tasks.db.QueryUtils.showHidden;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.tasks.data.sql.Functions;
|
||||
import org.tasks.data.sql.Order;
|
||||
import org.tasks.data.sql.OrderType;
|
||||
import org.tasks.data.entity.Task;
|
||||
|
||||
import org.tasks.data.entity.CaldavCalendar;
|
||||
import org.tasks.preferences.QueryPreferences;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Helpers for sorting a list of tasks
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*/
|
||||
public class SortHelper {
|
||||
|
||||
public static final int GROUP_NONE = -1;
|
||||
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 int SORT_LIST = 9;
|
||||
public static final int SORT_COMPLETED = 10;
|
||||
public static final int SORT_MANUAL = 11;
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public static final String CALDAV_ORDER_COLUMN =
|
||||
String.format(Locale.US, "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 Long NO_DATE = 3538339200000L;
|
||||
|
||||
private static final String GROUP_DUE_DATE = "((CASE WHEN (tasks.dueDate=0) THEN " + NO_DATE + " ELSE "
|
||||
+ "tasks.dueDate END)+tasks.importance * 1000)";
|
||||
|
||||
private static final String SORT_DUE_DATE = "((CASE WHEN (tasks.dueDate=0) THEN " + NO_DATE + " ELSE "
|
||||
+ ADJUSTED_DUE_DATE.replace("dueDate", "tasks.dueDate")
|
||||
+ " END)+tasks.importance * 1000)";
|
||||
|
||||
private static final String GROUP_START_DATE = "((CASE WHEN (tasks.hideUntil=0) THEN " + NO_DATE + " ELSE "
|
||||
+ "tasks.hideUntil END)+tasks.importance * 1000)";
|
||||
|
||||
private static final String SORT_START_DATE = "((CASE WHEN (tasks.hideUntil=0) THEN " + NO_DATE + " ELSE "
|
||||
+ ADJUSTED_START_DATE.replace("hideUntil", "tasks.hideUntil")
|
||||
+ " END)+tasks.importance * 1000)";
|
||||
|
||||
private static final Order ORDER_TITLE = Order.asc(Functions.upper(Task.TITLE));
|
||||
private static final Order ORDER_LIST =
|
||||
Order.asc(Functions.upper(CaldavCalendar.ORDER))
|
||||
.addSecondaryExpression(Order.asc(CaldavCalendar.NAME));
|
||||
|
||||
/** 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 (order.getOrderType() == OrderType.ASC != preferences.getSortAscending()) {
|
||||
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");
|
||||
break;
|
||||
case SORT_MODIFIED:
|
||||
order = Order.desc(Task.MODIFICATION_DATE);
|
||||
break;
|
||||
case SORT_CREATED:
|
||||
order = Order.desc(Task.CREATION_DATE);
|
||||
break;
|
||||
case SORT_LIST:
|
||||
order = ORDER_LIST;
|
||||
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";
|
||||
case SORT_LIST:
|
||||
return "cdl_id";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String sortGroup(String column) {
|
||||
return "datetime(" + column + " / 1000, 'unixepoch', 'localtime', 'start of day')";
|
||||
}
|
||||
|
||||
public static String orderSelectForSortTypeRecursive(int sortType, boolean grouping) {
|
||||
return switch (sortType) {
|
||||
case GROUP_NONE -> "1";
|
||||
case SORT_ALPHA -> "UPPER(tasks.title)";
|
||||
case SORT_DUE -> grouping ? sortGroup(GROUP_DUE_DATE) : SORT_DUE_DATE;
|
||||
case SORT_START -> grouping ? sortGroup(GROUP_START_DATE) : SORT_START_DATE;
|
||||
case SORT_IMPORTANCE -> "tasks.importance";
|
||||
case SORT_MODIFIED -> grouping ? sortGroup("tasks.modified") : "tasks.modified";
|
||||
case SORT_CREATED -> grouping ? sortGroup("tasks.created") : "tasks.created";
|
||||
case SORT_GTASKS -> "tasks.`order`";
|
||||
case SORT_CALDAV -> CALDAV_ORDER_COLUMN;
|
||||
case SORT_LIST -> "CASE WHEN cdl_order = -1 THEN cdl_name ELSE cdl_order END";
|
||||
case SORT_COMPLETED -> "tasks.completed";
|
||||
default -> "(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";
|
||||
};
|
||||
}
|
||||
|
||||
public static Order orderForGroupTypeRecursive(int groupMode, boolean ascending) {
|
||||
return ascending
|
||||
? Order.asc("primary_group")
|
||||
: Order.desc("primary_group");
|
||||
}
|
||||
|
||||
public static Order orderForSortTypeRecursive(
|
||||
int sortMode,
|
||||
boolean primaryAscending,
|
||||
int secondaryMode,
|
||||
boolean secondaryAscending
|
||||
) {
|
||||
Order order = primaryAscending || sortMode == SORT_GTASKS || sortMode == SORT_CALDAV
|
||||
? Order.asc("primary_sort")
|
||||
: Order.desc("primary_sort");
|
||||
order.addSecondaryExpression(
|
||||
secondaryAscending || secondaryMode == SORT_GTASKS || secondaryMode == SORT_CALDAV
|
||||
? Order.asc("secondary_sort")
|
||||
: Order.desc("secondary_sort")
|
||||
);
|
||||
if (sortMode != SORT_ALPHA) {
|
||||
order.addSecondaryExpression(Order.asc("sort_title"));
|
||||
}
|
||||
return order;
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.api
|
||||
|
||||
import org.tasks.time.DateTimeUtils2.currentTimeMillis
|
||||
import org.tasks.time.ONE_DAY
|
||||
import org.tasks.time.endOfDay
|
||||
import org.tasks.time.noon
|
||||
|
||||
/**
|
||||
* PermaSql allows for creating SQL statements that can be saved and used later without dates
|
||||
* getting stale. It also allows these values to be used in
|
||||
*
|
||||
* @author Tim Su <tim></tim>@todoroo.com>
|
||||
*/
|
||||
object PermaSql {
|
||||
// --- placeholder strings
|
||||
/** value to be replaced by end of day as long */
|
||||
const val VALUE_EOD: String = "EOD()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by noon today as long */
|
||||
const val VALUE_NOON: String = "NOON()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by end of day yesterday as long */
|
||||
const val VALUE_EOD_YESTERDAY: String = "EODY()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by end of day tomorrow as long */
|
||||
const val VALUE_EOD_TOMORROW: String = "EODT()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by end of day day after tomorrow as long */
|
||||
const val VALUE_EOD_DAY_AFTER: String = "EODTT()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by end of day next week as long */
|
||||
const val VALUE_EOD_NEXT_WEEK: String = "EODW()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by approximate end of day next month as long */
|
||||
const val VALUE_EOD_NEXT_MONTH: String = "EODM()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced with the current time as long */
|
||||
const val VALUE_NOW: String = "NOW()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by noon yesterday as long */
|
||||
private const val VALUE_NOON_YESTERDAY = "NOONY()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by noon tomorrow as long */
|
||||
private const val VALUE_NOON_TOMORROW = "NOONT()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by noon day after tomorrow as long */
|
||||
private const val VALUE_NOON_DAY_AFTER = "NOONTT()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by noon next week as long */
|
||||
private const val VALUE_NOON_NEXT_WEEK = "NOONW()" // $NON-NLS-1$
|
||||
|
||||
/** value to be replaced by approximate noon next month as long */
|
||||
private const val VALUE_NOON_NEXT_MONTH = "NOONM()" // $NON-NLS-1$
|
||||
|
||||
/** Replace placeholder strings with actual */
|
||||
fun replacePlaceholdersForQuery(value: String): String {
|
||||
var value = value
|
||||
if (value.contains(VALUE_NOW)) {
|
||||
value = value.replace(VALUE_NOW, currentTimeMillis().toString())
|
||||
}
|
||||
if (value.contains(VALUE_EOD)
|
||||
|| value.contains(VALUE_EOD_DAY_AFTER)
|
||||
|| value.contains(VALUE_EOD_NEXT_WEEK)
|
||||
|| value.contains(VALUE_EOD_TOMORROW)
|
||||
|| value.contains(VALUE_EOD_YESTERDAY)
|
||||
|| value.contains(VALUE_EOD_NEXT_MONTH)
|
||||
) {
|
||||
value = replaceEodValues(value)
|
||||
}
|
||||
if (value.contains(VALUE_NOON)
|
||||
|| value.contains(VALUE_NOON_DAY_AFTER)
|
||||
|| value.contains(VALUE_NOON_NEXT_WEEK)
|
||||
|| value.contains(VALUE_NOON_TOMORROW)
|
||||
|| value.contains(VALUE_NOON_YESTERDAY)
|
||||
|| value.contains(VALUE_NOON_NEXT_MONTH)
|
||||
) {
|
||||
value = replaceNoonValues(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
fun replacePlaceholdersForNewTask(value: String): String {
|
||||
var value = value
|
||||
if (value.contains(VALUE_NOW)) {
|
||||
value = value.replace(VALUE_NOW, currentTimeMillis().toString())
|
||||
}
|
||||
if (value.contains(VALUE_EOD)
|
||||
|| value.contains(VALUE_EOD_DAY_AFTER)
|
||||
|| value.contains(VALUE_EOD_NEXT_WEEK)
|
||||
|| value.contains(VALUE_EOD_TOMORROW)
|
||||
|| value.contains(VALUE_EOD_YESTERDAY)
|
||||
|| value.contains(VALUE_EOD_NEXT_MONTH)
|
||||
) {
|
||||
value = replaceEodValues(value, currentTimeMillis().noon())
|
||||
}
|
||||
if (value.contains(VALUE_NOON)
|
||||
|| value.contains(VALUE_NOON_DAY_AFTER)
|
||||
|| value.contains(VALUE_NOON_NEXT_WEEK)
|
||||
|| value.contains(VALUE_NOON_TOMORROW)
|
||||
|| value.contains(VALUE_NOON_YESTERDAY)
|
||||
|| value.contains(VALUE_NOON_NEXT_MONTH)
|
||||
) {
|
||||
value = replaceNoonValues(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
private fun replaceEodValues(
|
||||
value: String,
|
||||
dateTime: Long = currentTimeMillis().endOfDay()
|
||||
): String {
|
||||
var value = value
|
||||
value = value.replace(VALUE_EOD_YESTERDAY, (dateTime - ONE_DAY).toString())
|
||||
value = value.replace(VALUE_EOD, dateTime.toString())
|
||||
value = value.replace(VALUE_EOD_TOMORROW, (dateTime + ONE_DAY).toString())
|
||||
value = value.replace(VALUE_EOD_DAY_AFTER, (dateTime + 2 * ONE_DAY).toString())
|
||||
value = value.replace(VALUE_EOD_NEXT_WEEK, (dateTime + 7 * ONE_DAY).toString())
|
||||
value = value.replace(VALUE_EOD_NEXT_MONTH, (dateTime + 30 * ONE_DAY).toString())
|
||||
return value
|
||||
}
|
||||
|
||||
private fun replaceNoonValues(value: String): String {
|
||||
var value = value
|
||||
val time = currentTimeMillis().noon()
|
||||
value = value.replace(VALUE_NOON_YESTERDAY, (time - ONE_DAY).toString())
|
||||
value = value.replace(VALUE_NOON, time.toString())
|
||||
value = value.replace(VALUE_NOON_TOMORROW, (time + ONE_DAY).toString())
|
||||
value = value.replace(VALUE_NOON_DAY_AFTER, (time + 2 * ONE_DAY).toString())
|
||||
value = value.replace(VALUE_NOON_NEXT_WEEK, (time + 7 * ONE_DAY).toString())
|
||||
value = value.replace(VALUE_NOON_NEXT_MONTH, (time + 30 * ONE_DAY).toString())
|
||||
return value
|
||||
}
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
package com.todoroo.astrid.core
|
||||
|
||||
import org.tasks.data.dao.APPLE_EPOCH
|
||||
import org.tasks.data.entity.CaldavCalendar
|
||||
import org.tasks.data.entity.Task
|
||||
import org.tasks.data.sql.Functions.upper
|
||||
import org.tasks.data.sql.Order
|
||||
import org.tasks.data.sql.Order.Companion.asc
|
||||
import org.tasks.data.sql.Order.Companion.desc
|
||||
import org.tasks.data.sql.OrderType
|
||||
import org.tasks.db.QueryUtils.showCompleted
|
||||
import org.tasks.db.QueryUtils.showHidden
|
||||
import org.tasks.preferences.QueryPreferences
|
||||
|
||||
object SortHelper {
|
||||
const val GROUP_NONE: Int = -1
|
||||
const val SORT_AUTO: Int = 0
|
||||
const val SORT_ALPHA: Int = 1
|
||||
const val SORT_DUE: Int = 2
|
||||
const val SORT_IMPORTANCE: Int = 3
|
||||
const val SORT_MODIFIED: Int = 4
|
||||
const val SORT_CREATED: Int = 5
|
||||
const val SORT_GTASKS: Int = 6
|
||||
const val SORT_CALDAV: Int = 7
|
||||
const val SORT_START: Int = 8
|
||||
const val SORT_LIST: Int = 9
|
||||
const val SORT_COMPLETED: Int = 10
|
||||
const val SORT_MANUAL: Int = 11
|
||||
|
||||
private const val CALDAV_ORDER_COLUMN: String =
|
||||
"IFNULL(tasks.`order`, (tasks.created - $APPLE_EPOCH) / 1000)"
|
||||
|
||||
private const val ADJUSTED_DUE_DATE =
|
||||
"(CASE WHEN (dueDate / 1000) % 60 > 0 THEN dueDate ELSE (dueDate + 43140000) END)"
|
||||
private const val ADJUSTED_START_DATE =
|
||||
"(CASE WHEN (hideUntil / 1000) % 60 > 0 THEN hideUntil ELSE (hideUntil + 86399000) END)"
|
||||
|
||||
private const val NO_DATE = 3538339200000L
|
||||
|
||||
private const val GROUP_DUE_DATE = ("((CASE WHEN (tasks.dueDate=0) THEN " + NO_DATE + " ELSE "
|
||||
+ "tasks.dueDate END)+tasks.importance * 1000)")
|
||||
|
||||
private val SORT_DUE_DATE = ("((CASE WHEN (tasks.dueDate=0) THEN " + NO_DATE + " ELSE "
|
||||
+ ADJUSTED_DUE_DATE.replace("dueDate", "tasks.dueDate")
|
||||
+ " END)+tasks.importance * 1000)")
|
||||
|
||||
private const val GROUP_START_DATE =
|
||||
("((CASE WHEN (tasks.hideUntil=0) THEN " + NO_DATE + " ELSE "
|
||||
+ "tasks.hideUntil END)+tasks.importance * 1000)")
|
||||
|
||||
private val SORT_START_DATE = ("((CASE WHEN (tasks.hideUntil=0) THEN " + NO_DATE + " ELSE "
|
||||
+ ADJUSTED_START_DATE.replace("hideUntil", "tasks.hideUntil")
|
||||
+ " END)+tasks.importance * 1000)")
|
||||
|
||||
private val ORDER_TITLE = asc(upper(Task.TITLE))
|
||||
private val ORDER_LIST = asc(upper(CaldavCalendar.ORDER))
|
||||
.addSecondaryExpression(asc(CaldavCalendar.NAME))
|
||||
|
||||
/** Takes a SQL query, and if there isn't already an order, creates an order. */
|
||||
fun adjustQueryForFlagsAndSort(
|
||||
preferences: QueryPreferences, originalSql: String?, sort: Int
|
||||
): String {
|
||||
// sort
|
||||
var originalSql = originalSql
|
||||
if (originalSql == null) {
|
||||
originalSql = ""
|
||||
}
|
||||
if (!originalSql.contains("ORDER BY", ignoreCase = true)) {
|
||||
var order = orderForSortType(sort)
|
||||
|
||||
if (order.orderType == OrderType.ASC != preferences.sortAscending) {
|
||||
order = order.reverse()
|
||||
}
|
||||
originalSql += " ORDER BY $order"
|
||||
}
|
||||
|
||||
return adjustQueryForFlags(preferences, originalSql)
|
||||
}
|
||||
|
||||
fun adjustQueryForFlags(preferences: QueryPreferences, originalSql: String): String {
|
||||
var adjustedSql = originalSql
|
||||
|
||||
// flags
|
||||
if (preferences.showCompleted) {
|
||||
adjustedSql = showCompleted(adjustedSql)
|
||||
}
|
||||
if (preferences.showHidden) {
|
||||
adjustedSql = showHidden(adjustedSql)
|
||||
}
|
||||
|
||||
return adjustedSql
|
||||
}
|
||||
|
||||
private fun orderForSortType(sortType: Int): Order {
|
||||
val order = when (sortType) {
|
||||
SORT_ALPHA -> ORDER_TITLE
|
||||
SORT_DUE -> asc(
|
||||
"(CASE WHEN (dueDate=0) THEN (strftime('%s','now')*1000)*2 ELSE "
|
||||
+ ADJUSTED_DUE_DATE
|
||||
+ " END)+importance"
|
||||
)
|
||||
|
||||
SORT_START -> asc(
|
||||
"(CASE WHEN (hideUntil=0) THEN (strftime('%s','now')*1000)*2 ELSE "
|
||||
+ ADJUSTED_START_DATE
|
||||
+ " END)+importance"
|
||||
)
|
||||
|
||||
SORT_IMPORTANCE -> asc("importance")
|
||||
SORT_MODIFIED -> desc(Task.MODIFICATION_DATE)
|
||||
SORT_CREATED -> desc(Task.CREATION_DATE)
|
||||
SORT_LIST -> ORDER_LIST
|
||||
else -> 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
|
||||
}
|
||||
|
||||
fun getSortGroup(sortType: Int): String? {
|
||||
return when (sortType) {
|
||||
SORT_DUE -> "tasks.dueDate"
|
||||
SORT_START -> "tasks.hideUntil"
|
||||
SORT_IMPORTANCE -> "tasks.importance"
|
||||
SORT_MODIFIED -> "tasks.modified"
|
||||
SORT_CREATED -> "tasks.created"
|
||||
SORT_LIST -> "cdl_id"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun sortGroup(column: String): String {
|
||||
return "datetime($column / 1000, 'unixepoch', 'localtime', 'start of day')"
|
||||
}
|
||||
|
||||
fun orderSelectForSortTypeRecursive(sortType: Int, grouping: Boolean): String {
|
||||
return when (sortType) {
|
||||
GROUP_NONE -> "1"
|
||||
SORT_ALPHA -> "UPPER(tasks.title)"
|
||||
SORT_DUE -> if (grouping) sortGroup(GROUP_DUE_DATE) else SORT_DUE_DATE
|
||||
SORT_START -> if (grouping) sortGroup(GROUP_START_DATE) else SORT_START_DATE
|
||||
SORT_IMPORTANCE -> "tasks.importance"
|
||||
SORT_MODIFIED -> if (grouping) sortGroup("tasks.modified") else "tasks.modified"
|
||||
SORT_CREATED -> if (grouping) sortGroup("tasks.created") else "tasks.created"
|
||||
SORT_GTASKS -> "tasks.`order`"
|
||||
SORT_CALDAV -> CALDAV_ORDER_COLUMN
|
||||
SORT_LIST -> "CASE WHEN cdl_order = -1 THEN cdl_name ELSE cdl_order END"
|
||||
SORT_COMPLETED -> "tasks.completed"
|
||||
else -> ("(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")
|
||||
}
|
||||
}
|
||||
|
||||
fun orderForGroupTypeRecursive(groupMode: Int, ascending: Boolean): Order {
|
||||
return if (ascending
|
||||
) asc("primary_group")
|
||||
else desc("primary_group")
|
||||
}
|
||||
|
||||
fun orderForSortTypeRecursive(
|
||||
sortMode: Int,
|
||||
primaryAscending: Boolean,
|
||||
secondaryMode: Int,
|
||||
secondaryAscending: Boolean
|
||||
): Order {
|
||||
val order = if (primaryAscending || sortMode == SORT_GTASKS || sortMode == SORT_CALDAV
|
||||
) asc("primary_sort")
|
||||
else desc("primary_sort")
|
||||
order.addSecondaryExpression(
|
||||
if (secondaryAscending || secondaryMode == SORT_GTASKS || secondaryMode == SORT_CALDAV
|
||||
) asc("secondary_sort")
|
||||
else desc("secondary_sort")
|
||||
)
|
||||
if (sortMode != SORT_ALPHA) {
|
||||
order.addSecondaryExpression(asc("sort_title"))
|
||||
}
|
||||
return order
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package org.tasks.time
|
||||
|
||||
/** Represents a single hour */
|
||||
const val ONE_HOUR: Long = 3600000L
|
||||
|
||||
/** Represents a single day */
|
||||
const val ONE_DAY: Long = 24 * ONE_HOUR
|
||||
|
||||
/** Represents a single week */
|
||||
const val ONE_WEEK: Long = 7 * ONE_DAY
|
||||
|
||||
/** Represents a single minute */
|
||||
const val ONE_MINUTE: Long = 60000L
|
Loading…
Reference in New Issue