package org.dodgybits.shuffle.android.core.model.persistence.selector; import static org.dodgybits.shuffle.android.core.model.persistence.selector.Flag.ignored; import static org.dodgybits.shuffle.android.core.model.persistence.selector.Flag.no; import static org.dodgybits.shuffle.android.core.model.persistence.selector.Flag.yes; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import org.dodgybits.shuffle.android.core.model.Id; import org.dodgybits.shuffle.android.core.util.StringUtils; import org.dodgybits.shuffle.android.persistence.provider.TaskProvider; import org.dodgybits.shuffle.android.preference.model.ListPreferenceSettings; import org.dodgybits.shuffle.android.preference.model.Preferences; import android.text.format.DateUtils; import android.util.Log; public class TaskSelector extends AbstractEntitySelector { private static final String cTag = "TaskSelector"; private PredefinedQuery mPredefined; private List<Id> mProjects; private List<Id> mContexts; private Flag mComplete = ignored; private Flag mPending = ignored; private TaskSelector() { } public final PredefinedQuery getPredefinedQuery() { return mPredefined; } public final List<Id> getProjects() { return mProjects; } public final List<Id> getContexts() { return mContexts; } public final Flag getComplete() { return mComplete; } public final Flag getPending() { return mPending; } public final String getSelection(android.content.Context context) { List<String> expressions = getSelectionExpressions(context); String selection = StringUtils.join(expressions, " AND "); Log.d(cTag, selection); return selection; } @Override protected List<String> getSelectionExpressions(android.content.Context context) { List<String> expressions = super.getSelectionExpressions(context); if (mPredefined != null) { expressions.add(predefinedSelection(context)); } addActiveExpression(expressions); addDeletedExpression(expressions); addPendingExpression(expressions); addListExpression(expressions, TaskProvider.Tasks.PROJECT_ID, mProjects); addListExpression(expressions, TaskProvider.Tasks.CONTEXT_ID, mContexts); addFlagExpression(expressions, TaskProvider.Tasks.COMPLETE, mComplete); return expressions; } private void addActiveExpression(List<String> expressions) { if (mActive == yes) { // A task is active if it is active and both project and context are active. String expression = "(active = 1 " + "AND (projectId is null OR projectId IN (select p._id from project p where p.active = 1)) " + "AND (contextId is null OR contextId IN (select c._id from context c where c.active = 1)) " + ")"; expressions.add(expression); } else if (mActive == no) { // task is inactive if it is inactive or project in active or context is inactive String expression = "(active = 0 " + "OR (projectId is not null AND projectId IN (select p._id from project p where p.active = 0)) " + "OR (contextId is not null AND contextId IN (select c._id from context c where c.active = 0)) " + ")"; expressions.add(expression); } } private void addDeletedExpression(List<String> expressions) { if (mDeleted == yes) { // task is deleted if it is deleted or project is deleted or context is deleted String expression = "(deleted = 1 " + "OR (projectId is not null AND projectId IN (select p._id from project p where p.deleted = 1)) " + "OR (contextId is not null AND contextId IN (select c._id from context c where c.deleted = 1)) " + ")"; expressions.add(expression); } else if (mDeleted == no) { // task is not deleted if it is not deleted and project is not deleted and context is not deleted String expression = "(deleted = 0 " + "AND (projectId is null OR projectId IN (select p._id from project p where p.deleted = 0)) " + "AND (contextId is null OR contextId IN (select c._id from context c where c.deleted = 0)) " + ")"; expressions.add(expression); } } private void addPendingExpression(List<String> expressions) { long now = System.currentTimeMillis(); if (mPending == yes) { String expression = "(start > " + now + ")"; expressions.add(expression); } else if (mPending == no) { String expression = "(start <= " + now + ")"; expressions.add(expression); } } private String predefinedSelection(android.content.Context context) { String result; long now = System.currentTimeMillis(); switch (mPredefined) { case nextTasks: result = "((complete = 0) AND " + " (start < " + now + ") AND " + " ((projectId is null) OR " + " (projectId IN (select p._id from project p where p.parallel = 1)) OR " + " (task._id = (select t2._id FROM task t2 WHERE " + " t2.projectId = task.projectId AND t2.complete = 0 " + " ORDER BY due ASC, displayOrder ASC limit 1))" + "))"; break; case inbox: long lastCleanMS = Preferences.getLastInboxClean(context); result = "((projectId is null AND contextId is null) OR (created > " + lastCleanMS + "))"; break; case tickler: result = "((complete = 0) AND (active = 0))"; break; default: long startMS = 0L; long endOfToday = getEndDate(); long endOfTomorrow = endOfToday + DateUtils.DAY_IN_MILLIS; result = "(complete = 0" + " AND (due > " + startMS + ")" + " AND ( (due < " + endOfToday + ") OR" + "( allDay = 1 AND due < " + endOfTomorrow + " ) ))"; break; } return result; } private long getEndDate() { long endMS = 0L; Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); switch (mPredefined) { case dueToday: cal.add(Calendar.DAY_OF_YEAR, 1); endMS = cal.getTimeInMillis(); break; case dueNextWeek: cal.add(Calendar.DAY_OF_YEAR, 7); endMS = cal.getTimeInMillis(); break; case dueNextMonth: cal.add(Calendar.MONTH, 1); endMS = cal.getTimeInMillis(); break; } if (Log.isLoggable(cTag, Log.INFO)) { Log.i(cTag, "Due date ends " + endMS); } return endMS; } public final String[] getSelectionArgs() { List<String> args = new ArrayList<String>(); addIdListArgs(args, mProjects); addIdListArgs(args, mContexts); Log.d(cTag,args.toString()); return args.size() > 0 ? args.toArray(new String[0]): null; } @Override public final String toString() { return String.format( "[TaskSelector predefined=%1$s projects=%2$s contexts='%3$s' " + "complete=%4$s sortOrder=%5$s active=%6$s deleted=%7$s pending=%8$s]", mPredefined, mProjects, mContexts, mComplete, mSortOrder, mActive, mDeleted, mPending); } public static Builder newBuilder() { return Builder.create(); } public static class Builder extends AbstractBuilder<TaskSelector> { private Builder() { } private static Builder create() { Builder builder = new Builder(); builder.mResult = new TaskSelector(); return builder; } public PredefinedQuery getPredefined() { return mResult.mPredefined; } public Builder setPredefined(PredefinedQuery value) { mResult.mPredefined = value; return this; } public List<Id> getProjects() { return mResult.mProjects; } public Builder setProjects(List<Id> value) { mResult.mProjects = value; return this; } public List<Id> getContexts() { return mResult.mContexts; } public Builder setContexts(List<Id> value) { mResult.mContexts = value; return this; } public Flag getComplete() { return mResult.mComplete; } public Builder setComplete(Flag value) { mResult.mComplete = value; return this; } public Flag getPending() { return mResult.mPending; } public Builder setPending(Flag value) { mResult.mPending = value; return this; } public Builder mergeFrom(TaskSelector query) { super.mergeFrom(query); setPredefined(query.mPredefined); setProjects(query.mProjects); setContexts(query.mContexts); setComplete(query.mComplete); setPending(query.mPending); return this; } public Builder applyListPreferences(android.content.Context context, ListPreferenceSettings settings) { super.applyListPreferences(context, settings); setComplete(settings.getCompleted(context)); setPending(settings.getPending(context)); return this; } } public enum PredefinedQuery { nextTasks, dueToday, dueNextWeek, dueNextMonth, inbox, tickler } }