diff --git a/astrid/.classpath b/astrid/.classpath index 17e78b6c2..bc28b5eed 100644 --- a/astrid/.classpath +++ b/astrid/.classpath @@ -4,7 +4,7 @@ - + diff --git a/astrid/api-src/com/todoroo/astrid/api/Filter.java b/astrid/api-src/com/todoroo/astrid/api/Filter.java index 9c8c258c8..83083c6b5 100644 --- a/astrid/api-src/com/todoroo/astrid/api/Filter.java +++ b/astrid/api-src/com/todoroo/astrid/api/Filter.java @@ -79,7 +79,8 @@ public final class Filter extends FilterListItem { QueryTemplate sqlQuery, ContentValues valuesForNewTasks) { this.listingTitle = listingTitle; this.title = title; - this.sqlQuery = sqlQuery.toString(); + if(sqlQuery != null) + this.sqlQuery = sqlQuery.toString(); this.valuesForNewTasks = valuesForNewTasks; } diff --git a/astrid/astrid.launch b/astrid/astrid.launch index 0a7cd7209..72aa24a68 100644 --- a/astrid/astrid.launch +++ b/astrid/astrid.launch @@ -4,8 +4,9 @@ - + + @@ -14,9 +15,11 @@ + + diff --git a/astrid/common-src/com/todoroo/andlib/sql/Functions.java b/astrid/common-src/com/todoroo/andlib/sql/Functions.java index ca56da772..4099d3c72 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Functions.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Functions.java @@ -18,4 +18,11 @@ public final class Functions { return new Field("UPPER(" + title.toString() + ")"); } + /** + * @return SQL now (in milliseconds) + */ + public static Field now() { + return new Field("(strftime('%s','now')*1000)"); + } + } diff --git a/astrid/lib/locale_platform.jar b/astrid/lib/locale_platform.jar index a26346f28..cd7183aa0 100644 Binary files a/astrid/lib/locale_platform.jar and b/astrid/lib/locale_platform.jar differ diff --git a/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java b/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java index f693e6b89..36d48f794 100644 --- a/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java +++ b/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.FileFilter; import java.util.Arrays; import java.util.Comparator; -import java.util.Date; import android.app.AlarmManager; import android.app.PendingIntent; @@ -12,9 +11,6 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; -import android.util.Log; - -import com.timsu.astrid.R; /** * Inspired heavily by SynchronizationService @@ -55,7 +51,7 @@ public class BackupService extends Service { } private void startBackup(Context ctx) { - if (ctx == null || ctx.getResources() == null) { + /*if (ctx == null || ctx.getResources() == null) { return; } try { @@ -84,11 +80,11 @@ public class BackupService extends Service { ctx.getString(R.string.BPr_backup_desc_failure, e.toString())); } - } + }*/ } public static void scheduleService(Context ctx) { - AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); + /*AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); PendingIntent pendingIntent = PendingIntent.getService(ctx, 0, createAlarmIntent(ctx), PendingIntent.FLAG_UPDATE_CURRENT); am.cancel(pendingIntent); @@ -96,7 +92,7 @@ public class BackupService extends Service { return; } am.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis() + BACKUP_OFFSET, - BACKUP_INTERVAL, pendingIntent); + BACKUP_INTERVAL, pendingIntent);*/ } public static void unscheduleService(Context ctx) { @@ -148,7 +144,7 @@ public class BackupService extends Service { private BackupDirectorySetting backupDirectorySetting = new BackupDirectorySetting() { public File getBackupDirectory() { - return TasksXmlExporter.getExportDirectory(); + return null; //TasksXmlExporter.getExportDirectory(); } }; diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java index 2cf3e9457..0521feef3 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java @@ -50,7 +50,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { Filter alphabetical = new Filter(r.getString(R.string.BFE_Alphabetical), r.getString(R.string.BFE_Alphabetical), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), - TaskCriteria.isVisible(DateUtilities.now()))). + TaskCriteria.isVisible())). orderBy(Order.asc(Task.TITLE)), null); alphabetical.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_alpha)).getBitmap(); @@ -66,7 +66,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { Filter hidden = new Filter(r.getString(R.string.BFE_Hidden), r.getString(R.string.BFE_Hidden), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), - Criterion.not(TaskCriteria.isVisible(DateUtilities.now())))). + Criterion.not(TaskCriteria.isVisible()))). orderBy(Order.asc(Task.HIDE_UNTIL)), hiddenValues); hidden.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clouds)).getBitmap(); @@ -74,7 +74,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { ContentValues completedValues = new ContentValues(); hiddenValues.put(Task.COMPLETION_DATE.name, DateUtilities.now()); Filter completed = new Filter(r.getString(R.string.BFE_Completed), r.getString(R.string.BFE_Completed), - new QueryTemplate().where(Criterion.and(TaskCriteria.completedBefore(DateUtilities.now()), + new QueryTemplate().where(Criterion.and(TaskCriteria.completed(), Criterion.not(TaskCriteria.isDeleted()))). orderBy(Order.desc(Task.COMPLETION_DATE)), completedValues); completed.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_check)).getBitmap(); @@ -109,7 +109,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { public static Filter buildInboxFilter(Resources r) { Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), - TaskCriteria.isVisible(DateUtilities.now()))), + TaskCriteria.isVisible())), null); inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap(); return inbox; diff --git a/astrid/plugin-src/com/todoroo/astrid/locale/LocaleEditAlerts.java b/astrid/plugin-src/com/todoroo/astrid/locale/LocaleEditAlerts.java index 7b51c70cc..a4bc486be 100644 --- a/astrid/plugin-src/com/todoroo/astrid/locale/LocaleEditAlerts.java +++ b/astrid/plugin-src/com/todoroo/astrid/locale/LocaleEditAlerts.java @@ -1,104 +1,270 @@ package com.todoroo.astrid.locale; -import java.util.LinkedList; - import android.app.Activity; +import android.app.ExpandableListActivity; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.Window; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; +import android.widget.ExpandableListView; +import android.widget.LinearLayout; import android.widget.Spinner; -import android.widget.TextView; -import android.widget.AdapterView.OnItemSelectedListener; import com.flurry.android.FlurryAgent; import com.timsu.astrid.R; -import com.timsu.astrid.data.tag.TagController; -import com.timsu.astrid.data.tag.TagModelForView; +import com.todoroo.astrid.adapter.FilterAdapter; +import com.todoroo.astrid.api.Filter; +import com.todoroo.astrid.api.FilterListItem; +import com.todoroo.astrid.utility.Constants; +import com.twofortyfouram.SharedResources; /** * Activity to edit alerts from Locale * - * @author timsu + * @author Tim Su * */ -public final class LocaleEditAlerts extends Activity { +public final class LocaleEditAlerts extends ExpandableListActivity { + + // --- locale constants /** value for action type for tag alert */ @SuppressWarnings("nls") - public static final String ACTION_LOCALE_ALERT = "com.timsu.astrid.action.LOCALE_ALERT"; + public static final String ACTION_LOCALE_ALERT = "com.todoroo.astrid.action.LOCALE_ALERT"; - /** key name for tag id/name in bundle */ + /** key name for filter title in bundle */ @SuppressWarnings("nls") - public static final String KEY_TAG_ID = "tag"; + public static final String KEY_FILTER_TITLE = "title"; + /** key name for filter SQL in bundle */ @SuppressWarnings("nls") - public static final String KEY_TAG_NAME = "name"; + public static final String KEY_SQL = "sql"; - private LinkedList tags = null; - private String[] tagNames = null; + /** key name for interval (integer, # of seconds) */ + @SuppressWarnings("nls") + public static final String KEY_INTERVAL = "interval"; + + // --- activity constants + + /** + * Indices for interval setting + */ + public static final int INTERVAL_ONE_HOUR = 0; + public static final int INTERVAL_SIX_HOURS = 1; + public static final int INTERVAL_TWELVE_HOURS = 2; + public static final int INTERVAL_ONE_DAY = 3; + public static final int INTERVAL_THREE_DAYS = 4; + public static final int INTERVAL_ONE_WEEK = 5; + + /** + * Intervals in seconds + */ + public static final int[] INTERVALS = new int[] { + 3600, 6 * 3600, 12 * 3600, 24 * 3600, 3 * 24 * 3600, 7 * 24 * 3600 + }; + + /** + * Menu ID of the save item. + */ + private static final int MENU_SAVE = 1; + + /** + * Menu ID of the don't save item. + */ + private static final int MENU_DONT_SAVE = 2; + + // --- implementation + + FilterAdapter adapter = null; + Spinner interval = null; + + /** + * Flag boolean that can only be set to true via the "Don't Save" menu item in {@link #onMenuItemSelected(int, MenuItem)}. If + * true, then this {@code Activity} should return {@link Activity#RESULT_CANCELED} in {@link #finish()}. + *

+ * There is no need to save/restore this field's state when the {@code Activity} is paused. + */ + private boolean isCancelled = false; /** Called when the activity is first created. */ @Override - public void onCreate(Bundle savedInstanceState) - { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.locale_edit_alerts); - // Set up the breadcrumbs in the title bar - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.locale_ellipsizing_title); - - String breadcrumbString = getIntent().getStringExtra(com.twofortyfouram.Intent.EXTRA_STRING_BREADCRUMB); - if (breadcrumbString == null) - breadcrumbString = getString(R.string.locale_edit_alerts_title); - else - breadcrumbString = breadcrumbString + com.twofortyfouram.Intent.BREADCRUMB_SEPARATOR + getString(R.string.locale_edit_alerts_title); + /* + * Locale guarantees that the breadcrumb string will be present, but checking for null anyway makes your Activity more + * robust and re-usable + */ + final String breadcrumbString = getIntent().getStringExtra(com.twofortyfouram.Intent.EXTRA_STRING_BREADCRUMB); + if (breadcrumbString != null) + setTitle(String.format("%s%s%s", breadcrumbString, com.twofortyfouram.Intent.BREADCRUMB_SEPARATOR, //$NON-NLS-1$ + getString(R.string.locale_edit_alerts_title))); - ((TextView) findViewById(R.id.locale_ellipsizing_title_text)).setText(breadcrumbString); - setTitle(breadcrumbString); + /* + * Load the Locale background frame from Locale + */ + ((LinearLayout) findViewById(R.id.frame)).setBackgroundDrawable( + SharedResources.getDrawableResource(getPackageManager(), + SharedResources.DRAWABLE_LOCALE_BORDER)); + + // set up UI components + interval = (Spinner) findViewById(R.id.intervalSpinner); + interval.setSelection(INTERVAL_ONE_DAY); + String selectionToMatch = null; + + try { + /* + * if savedInstanceState == null, then we are entering the Activity directly from Locale and we need to check whether the + * Intent has forwarded a Bundle extra (e.g. whether we editing an old setting or creating a new one) + */ + if (savedInstanceState == null) + { + final Bundle forwardedBundle = getIntent().getBundleExtra(com.twofortyfouram.Intent.EXTRA_BUNDLE); + + /* + * the forwardedBundle would be null if this was a new setting + */ + if (forwardedBundle != null) + { + final int intervalValue = getIntent().getIntExtra(KEY_INTERVAL, INTERVALS[interval.getSelectedItemPosition()]); + for(int i = 0; i < INTERVALS.length; i++) { + if(intervalValue == INTERVALS[i]) { + interval.setSelection(i); + break; + } + } + selectionToMatch = getIntent().getStringExtra(KEY_SQL); + } + } + } catch (Exception e) { + selectionToMatch = null; + Log.e("astrid-locale", "Error loading bundle", e); //$NON-NLS-1$ //$NON-NLS-2$ + } - final Spinner tagSpinner = (Spinner) findViewById(R.id.spinner); + // if we match a selection, make it selected + final String finalSelection = selectionToMatch; + adapter = new FilterAdapter(this, getExpandableListView(), R.layout.filter_adapter_row) { + @Override + public void onReceiveFilter(FilterListItem item) { + if(finalSelection != null && item instanceof Filter && + finalSelection.equals(((Filter)item).sqlQuery)) + adapter.setSelection(item); + } + }; + adapter.filterStyle = R.style.TextAppearance_LEA_Filter; + adapter.headerStyle = R.style.TextAppearance_LEA_Header; + adapter.categoryStyle = R.style.TextAppearance_LEA_Category; + setListAdapter(adapter); + } - TagController tagController = new TagController(this); - tagController.open(); - tags = tagController.getAllTags(); - tagController.close(); + @Override + public boolean onChildClick(ExpandableListView parent, View v, + int groupPosition, int childPosition, long id) { + FilterListItem item = (FilterListItem) adapter.getChild(groupPosition, + childPosition); + if(item instanceof Filter) { + adapter.setSelection(item); + } + return true; + } - tagNames = new String[tags.size()]; - for(int i = 0; i < tags.size(); i++) - tagNames[i] = tags.get(i).getName(); + @Override + public void onGroupExpand(int groupPosition) { + FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition); + if(item instanceof Filter) { + adapter.setSelection(item); + } + } - ArrayAdapter tagAdapter = new ArrayAdapter( - this, android.R.layout.simple_spinner_item, tagNames); - tagAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - tagSpinner.setAdapter(tagAdapter); + @Override + public void onGroupCollapse(int groupPosition) { + onGroupExpand(groupPosition); + } - // Save the state into the return Intent whenever the field - tagSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView arg0, View arg1, - int arg2, long arg3) { - updateResult(); + /** + * Called when the {@code Activity} is being terminated. This method determines the state of the {@code Activity} and what + * sort of result should be returned to Locale. + */ + @Override + public void finish() + { + if (isCancelled) + setResult(RESULT_CANCELED); + else + { + final FilterListItem selected = adapter.getSelection(); + final int intervalIndex = interval.getSelectedItemPosition(); + + /* + * If the message is of 0 length, then there isn't a setting to save. + */ + if (selected == null) + { + /* + * Note: many settings will not need to use the RESULT_REMOVE result. This is only needed for settings that have + * an "invalid" state that shouldn't be saved. For example, an saving empty Toast message doesn't make sense. The + * Ringer Volume setting doesn't have such an "invalid" state, and therefore doesn't use this result code + */ + setResult(com.twofortyfouram.Intent.RESULT_REMOVE); } - - @Override - public void onNothingSelected(AdapterView arg0) { - // do nothing + else + { + /* + * This is the return Intent, into which we'll put all the required extras + */ + final Intent returnIntent = new Intent(ACTION_LOCALE_ALERT); + + /* + * This extra is the data to ourselves: either for the Activity or the BroadcastReceiver. Note that anything + * placed in this bundle must be available to Locale's class loader. So storing String, int, and other basic + * objects will work just fine. You cannot store an object that only exists in your project, as Locale will be + * unable to serialize it. + */ + final Bundle storeAndForwardExtras = new Bundle(); + + Filter filterItem = (Filter) selected; + storeAndForwardExtras.putString(KEY_FILTER_TITLE, filterItem.title); + storeAndForwardExtras.putString(KEY_SQL, filterItem.sqlQuery); + storeAndForwardExtras.putInt(KEY_INTERVAL, INTERVALS[intervalIndex]); + + returnIntent.putExtra(com.twofortyfouram.Intent.EXTRA_BUNDLE, storeAndForwardExtras); + + /* + * This is the blurb concisely describing what your setting's state is. This is simply used for display in the UI. + */ + if (filterItem.title.length() > com.twofortyfouram.Intent.MAXIMUM_BLURB_LENGTH) + returnIntent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BLURB, filterItem.title.substring(0, com.twofortyfouram.Intent.MAXIMUM_BLURB_LENGTH)); + else + returnIntent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BLURB, filterItem.title); + + setResult(RESULT_OK, returnIntent); } + } - }); - } + super.finish(); + } + + // --- boring stuff + + @Override + protected void onResume() { + super.onResume(); + adapter.registerRecevier(); + } + + @Override + protected void onPause() { + super.onPause(); + adapter.unregisterRecevier(); + } @Override protected void onStart() { super.onStart(); - - // set up flurry FlurryAgent.onStartSession(this, Constants.FLURRY_KEY); } @@ -108,86 +274,50 @@ public final class LocaleEditAlerts extends Activity { FlurryAgent.onEndSession(this); } - /** - * Private helper method to persist the Toast message in the return {@code Intent}. - */ - private void updateResult() { - // no tags, so it's not possible to save - if(tagNames.length == 0) { - setResult(com.twofortyfouram.Intent.RESULT_REMOVE); - return; - } + /** + * {@inheritDoc} + */ + @Override + public boolean onCreateOptionsMenu(final Menu menu) + { + super.onCreateOptionsMenu(menu); - final int index = ((Spinner) findViewById(R.id.spinner)).getSelectedItemPosition(); - final String tagName = tagNames[index]; - final TagModelForView tag = tags.get(index); + final PackageManager manager = getPackageManager(); /* - * If the message is of 0 length, then there isn't a setting to save + * We are dynamically loading resources from Locale's APK. This will only work if Locale is actually installed */ - if (tagName == null) { - setResult(com.twofortyfouram.Intent.RESULT_REMOVE); - } else { - final Intent intent = new Intent(); - intent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_ACTION_FIRE, - ACTION_LOCALE_ALERT); - intent.putExtra(KEY_TAG_ID, tag.getTagIdentifier().getId()); - intent.putExtra(KEY_TAG_NAME, tagName); - intent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BLURB, tagName); - setResult(RESULT_OK, intent); + menu.add(0, MENU_DONT_SAVE, 0, SharedResources.getTextResource(manager, SharedResources.STRING_MENU_DONTSAVE)) + .setIcon(SharedResources.getDrawableResource(manager, SharedResources.DRAWABLE_MENU_DONTSAVE)).getItemId(); + + menu.add(0, MENU_SAVE, 0, SharedResources.getTextResource(manager, SharedResources.STRING_MENU_SAVE)) + .setIcon(SharedResources.getDrawableResource(manager, SharedResources.DRAWABLE_MENU_SAVE)).getItemId(); + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onMenuItemSelected(final int featureId, final MenuItem item) + { + switch (item.getItemId()) + { + case MENU_SAVE: + { + finish(); + return true; + } + case MENU_DONT_SAVE: + { + isCancelled = true; + finish(); + return true; + } } - } - /** - * {@inheritDoc} - */ - @Override - public boolean onCreateOptionsMenu(final Menu menu) { - super.onCreateOptionsMenu(menu); - - getMenuInflater().inflate(R.menu.locale_edit_alerts, menu); - - menu.findItem(R.id.menu_save).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() - { - public boolean onMenuItemClick(final MenuItem item) - { - updateResult(); - finish(); - return true; - } - }); - - menu.findItem(R.id.menu_dontsave).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() - { - public boolean onMenuItemClick(final MenuItem item) - { - setResult(RESULT_CANCELED); - finish(); - return true; - } - }); - - menu.findItem(R.id.menu_help).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() - { - public boolean onMenuItemClick(final MenuItem item) - { - - final Intent helpIntent = new Intent(com.twofortyfouram.Intent.ACTION_HELP); - helpIntent.putExtra("com.twofortyfouram.locale.intent.extra.HELP_URL", "http://www.androidlocale.com/app_data/toast/1.0/help_toast.htm"); //$NON-NLS-1$ //$NON-NLS-2$ - - // Set up the breadcrumbs in the title bar - String breadcrumbString = getIntent().getStringExtra(com.twofortyfouram.Intent.EXTRA_STRING_BREADCRUMB); - if (breadcrumbString == null) - helpIntent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BREADCRUMB, getString(R.string.locale_edit_alerts_title)); - else - helpIntent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BREADCRUMB, breadcrumbString + com.twofortyfouram.Intent.BREADCRUMB_SEPARATOR - + getString(R.string.locale_edit_alerts_title)); - - startActivity(helpIntent); - return true; - } - }); - - return true; - } + return super.onOptionsItemSelected(item); + } + } \ No newline at end of file diff --git a/astrid/plugin-src/com/todoroo/astrid/locale/LocaleReceiver.java b/astrid/plugin-src/com/todoroo/astrid/locale/LocaleReceiver.java index 735d70e17..40ee2c1de 100644 --- a/astrid/plugin-src/com/todoroo/astrid/locale/LocaleReceiver.java +++ b/astrid/plugin-src/com/todoroo/astrid/locale/LocaleReceiver.java @@ -1,8 +1,7 @@ package com.todoroo.astrid.locale; -import java.util.HashSet; -import java.util.LinkedList; - +import android.app.Notification; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -10,10 +9,18 @@ import android.content.res.Resources; import android.util.Log; import com.timsu.astrid.R; -import com.timsu.astrid.data.TaskController; -import com.timsu.astrid.data.TaskIdentifier; -import com.timsu.astrid.data.tag.TagController; -import com.timsu.astrid.data.tag.TagIdentifier; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.service.NotificationManager; +import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.activity.ShortcutActivity; +import com.todoroo.astrid.api.Filter; +import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.service.TaskService; +import com.todoroo.astrid.utility.Constants; +import com.todoroo.astrid.utility.Preferences; /** * Receiver is activated when Locale conditions are triggered @@ -23,56 +30,67 @@ import com.timsu.astrid.data.tag.TagIdentifier; */ public class LocaleReceiver extends BroadcastReceiver { - /** minimum amount of time between two notifications */ - private static final long MIN_NOTIFY_INTERVAL = 12*3600*1000L; + @Autowired + private TaskService taskService; + + /** + * Create a preference key for storing / retrieving last interval time + * @param filterTitle + * @param interval + * @return + */ + private String makePreferenceKey(String filterTitle, int interval) { + return "LOCALE:" + filterTitle + interval; //$NON-NLS-1$ + } + @SuppressWarnings("nls") @Override /** Called when the system is started up */ public void onReceive(Context context, Intent intent) { try { if (LocaleEditAlerts.ACTION_LOCALE_ALERT.equals(intent.getAction())) { - final long tagId = intent.getLongExtra(LocaleEditAlerts.KEY_TAG_ID, 0); - final String tagName = intent.getStringExtra(LocaleEditAlerts.KEY_TAG_NAME); - if(tagId == 0) { - Log.w("astrid-locale", "Invalid tag identifier in alert"); - return; - } + final String title = intent.getStringExtra(LocaleEditAlerts.KEY_FILTER_TITLE); + final String sql = intent.getStringExtra(LocaleEditAlerts.KEY_SQL); + final int interval = intent.getIntExtra(LocaleEditAlerts.KEY_INTERVAL, 24*3600); // check if we've already made a notification recently - if(System.currentTimeMillis() - Preferences. - getLocaleLastAlertTime(context, tagId) < MIN_NOTIFY_INTERVAL) { - Log.w("astrid-locale", "Too soon, need " + (MIN_NOTIFY_INTERVAL - System.currentTimeMillis() + Preferences. - getLocaleLastAlertTime(context, tagId))/1000L + " more seconds"); + String preferenceKey = makePreferenceKey(title, interval); + long lastNotifyTime = Preferences.getLong(preferenceKey, 0); + if(DateUtilities.now() - lastNotifyTime < interval * 1000L) { + Log.i("astrid-locale", title + ": Too soon, need " + (interval + - (DateUtilities.now() - lastNotifyTime)/1000) + " more seconds"); return; } // find out if we have active tasks with this tag - TaskController taskController = new TaskController(context); - taskController.open(); - TagController tagController = new TagController(context); - tagController.open(); + DependencyInjectionService.getInstance().inject(this); + Filter filter = new Filter(title, title, null, null); + filter.sqlQuery = sql; + TodorooCursor cursor = taskService.fetchFiltered(filter, Task.ID); try { - HashSet activeTasks = taskController.getActiveVisibleTaskIdentifiers(); - LinkedList tasks = tagController.getTaggedTasks( - new TagIdentifier(tagId)); -// int count = TagListSubActivity.countActiveTasks(activeTasks, tasks); - int count = 0; - if(count > 0) { - Resources r = context.getResources(); -// String reminder = r.getString(R.string.notif_tagNotification). - String reminder = "$NUM of $TAG". - replace("$NUM", r.getQuantityString(R.plurals.Ntasks, count, count)). - replace("$TAG", tagName); -// ReminderService.showTagNotification(context, tagId, reminder); + if(cursor.getCount() == 0) + return; + Resources r = context.getResources(); + String reminder = r.getString(R.string.locale_notification). + replace("$NUM", r.getQuantityString(R.plurals.Ntasks, + cursor.getCount(), cursor.getCount())). + replace("$FILTER", title); + + // show a reminder + Intent notifyIntent = ShortcutActivity.createIntent(filter); + notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pendingIntent = PendingIntent.getActivity(context, + Constants.NOTIFICATION_TIMER, notifyIntent, 0); + Notification notification = new Notification( + R.drawable.timers_notification, reminder, System.currentTimeMillis()); + notification.setLatestEventInfo(context, r.getString(R.string.locale_edit_alerts_title), + reminder, pendingIntent); - Preferences.setLocaleLastAlertTime(context, tagId, - System.currentTimeMillis()); - } else { - Log.w("astrid-locale", "Locale Notification, but no tasks to show"); - } + NotificationManager nm = new AndroidNotificationManager(context); + nm.notify(Constants.NOTIFICATION_TIMER, notification); + Preferences.setLong(preferenceKey, DateUtilities.now()); } finally { - taskController.close(); - tagController.close(); + cursor.close(); } } } catch (Exception e) { diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkFilterExposer.java index c0ce129b0..2178ffb95 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkFilterExposer.java @@ -11,7 +11,6 @@ import android.content.Intent; import com.timsu.astrid.R; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.QueryTemplate; -import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterCategory; @@ -44,7 +43,7 @@ public class MilkFilterExposer extends BroadcastReceiver { MilkDataService.METADATA_JOIN).where(Criterion.and( MetadataCriteria.withKey(MilkTask.METADATA_KEY), TaskCriteria.isActive(), - TaskCriteria.isVisible(DateUtilities.now()), + TaskCriteria.isVisible(), MilkTask.LIST_ID.eq(list.id))), values); diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java index a17fadd32..e8f01fba6 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java @@ -22,7 +22,6 @@ import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Query; -import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.SoftHashMap; import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.TaskDao; @@ -267,7 +266,7 @@ public final class MilkDataService { // read all list counts TodorooCursor cursor = metadataDao.query(Query.select(MilkTask.LIST_ID, COUNT). join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))). - where(Criterion.and(TaskCriteria.isVisible(DateUtilities.now()), TaskCriteria.isActive())). + where(Criterion.and(TaskCriteria.isVisible(), TaskCriteria.isActive())). groupBy(MilkTask.LIST_ID)); try { for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java index f0d089156..21cd47712 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java @@ -12,7 +12,6 @@ import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.QueryTemplate; -import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; @@ -95,7 +94,7 @@ public final class TagService { return new QueryTemplate().where(Criterion.and( Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).where(MetadataCriteria.withKey(KEY)))), TaskCriteria.isActive(), - TaskCriteria.isVisible(DateUtilities.now()))); + TaskCriteria.isVisible())); } /** diff --git a/astrid/res/drawable/filter_selected_icon.png b/astrid/res/drawable/filter_selected_icon.png new file mode 100644 index 000000000..e90f6312a Binary files /dev/null and b/astrid/res/drawable/filter_selected_icon.png differ diff --git a/astrid/res/layout/filter_adapter_row.xml b/astrid/res/layout/filter_adapter_row.xml new file mode 100644 index 000000000..2115ec3fe --- /dev/null +++ b/astrid/res/layout/filter_adapter_row.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + diff --git a/astrid/res/layout/locale_edit_alerts.xml b/astrid/res/layout/locale_edit_alerts.xml index ca985939b..082a194c1 100644 --- a/astrid/res/layout/locale_edit_alerts.xml +++ b/astrid/res/layout/locale_edit_alerts.xml @@ -5,32 +5,52 @@ android:layout_width="fill_parent" android:layout_height="fill_parent"> - - + + android:layout_weight="1"> - + + + + + + + + + + \ No newline at end of file diff --git a/astrid/res/layout/locale_ellipsizing_title.xml b/astrid/res/layout/locale_ellipsizing_title.xml deleted file mode 100644 index 5a513acea..000000000 --- a/astrid/res/layout/locale_ellipsizing_title.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - \ No newline at end of file diff --git a/astrid/res/layout/task_adapter_row.xml b/astrid/res/layout/task_adapter_row.xml index bad4d561a..a46138de7 100644 --- a/astrid/res/layout/task_adapter_row.xml +++ b/astrid/res/layout/task_adapter_row.xml @@ -35,8 +35,6 @@ @@ -44,8 +42,6 @@ -

- - - - \ No newline at end of file diff --git a/astrid/res/values/strings-filters.xml b/astrid/res/values/strings-filters.xml index 8fcfbd469..77f5a901d 100644 --- a/astrid/res/values/strings-filters.xml +++ b/astrid/res/values/strings-filters.xml @@ -8,7 +8,7 @@ Active Tasks - Astrid: Home + Active Tasks Search diff --git a/astrid/res/values/strings-locale.xml b/astrid/res/values/strings-locale.xml index d39e9b3e1..bf9eaa99f 100644 --- a/astrid/res/values/strings-locale.xml +++ b/astrid/res/values/strings-locale.xml @@ -2,22 +2,54 @@ - + - Astrid Tag Alert + Astrid Filter Alert Astrid will send you a reminder - when you have uncompleted tasks in the following filter: + when you have any tasks in the following filter: Filter: + + + Limit notifications to: + + + + once an hour + once every six hours + once every twelve hours + once a day + once every three days + once a week + + + + You have $NUM tasks in $FILTER + + + + + + + diff --git a/astrid/res/values/styles-3.0.xml b/astrid/res/values/styles-3.0.xml index bd9f72c4a..4be7a183d 100644 --- a/astrid/res/values/styles-3.0.xml +++ b/astrid/res/values/styles-3.0.xml @@ -2,7 +2,7 @@ - + - + - + @@ -62,25 +62,24 @@ #ffee5555 - + - - - - +