Locale plugin.

pull/14/head
Tim Su 15 years ago
parent b6bc9c1b00
commit e99e52b64f

@ -4,7 +4,7 @@
<classpathentry kind="src" path="src-legacy"/> <classpathentry kind="src" path="src-legacy"/>
<classpathentry kind="src" path="api-src"/> <classpathentry kind="src" path="api-src"/>
<classpathentry kind="src" path="common-src"/> <classpathentry kind="src" path="common-src"/>
<classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java|com/todoroo/astrid/rmilk/StartupReceiver.java|com/todoroo/astrid/backup/BackupService.java|com/todoroo/astrid/backup/TasksXmlExporter.java|com/todoroo/astrid/backup/TasksXmlImporter.java" kind="src" path="plugin-src"/> <classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java|com/todoroo/astrid/rmilk/StartupReceiver.java|com/todoroo/astrid/backup/TasksXmlExporter.java|com/todoroo/astrid/backup/TasksXmlImporter.java" kind="src" path="plugin-src"/>
<classpathentry kind="src" path="gen"/> <classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="lib" path="lib/commons-codec-1.3.jar"/> <classpathentry exported="true" kind="lib" path="lib/commons-codec-1.3.jar"/>

@ -79,7 +79,8 @@ public final class Filter extends FilterListItem {
QueryTemplate sqlQuery, ContentValues valuesForNewTasks) { QueryTemplate sqlQuery, ContentValues valuesForNewTasks) {
this.listingTitle = listingTitle; this.listingTitle = listingTitle;
this.title = title; this.title = title;
this.sqlQuery = sqlQuery.toString(); if(sqlQuery != null)
this.sqlQuery = sqlQuery.toString();
this.valuesForNewTasks = valuesForNewTasks; this.valuesForNewTasks = valuesForNewTasks;
} }

@ -4,8 +4,9 @@
<booleanAttribute key="ch.zork.quicklaunch" value="true"/> <booleanAttribute key="ch.zork.quicklaunch" value="true"/>
<stringAttribute key="ch.zork.quicklaunch.icon" value="14.gif"/> <stringAttribute key="ch.zork.quicklaunch.icon" value="14.gif"/>
<intAttribute key="ch.zork.quicklaunch.index" value="0"/> <intAttribute key="ch.zork.quicklaunch.index" value="0"/>
<stringAttribute key="ch.zork.quicklaunch.mode" value="run"/> <stringAttribute key="ch.zork.quicklaunch.mode" value="debug"/>
<intAttribute key="com.android.ide.eclipse.adt.action" value="0"/> <intAttribute key="com.android.ide.eclipse.adt.action" value="0"/>
<stringAttribute key="com.android.ide.eclipse.adt.avd" value="evo-8-google"/>
<stringAttribute key="com.android.ide.eclipse.adt.commandline" value="-scale 0.7"/> <stringAttribute key="com.android.ide.eclipse.adt.commandline" value="-scale 0.7"/>
<intAttribute key="com.android.ide.eclipse.adt.delay" value="0"/> <intAttribute key="com.android.ide.eclipse.adt.delay" value="0"/>
<booleanAttribute key="com.android.ide.eclipse.adt.nobootanim" value="true"/> <booleanAttribute key="com.android.ide.eclipse.adt.nobootanim" value="true"/>
@ -14,9 +15,11 @@
<booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/> <booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/astrid"/> <listEntry value="/astrid"/>
<listEntry value="/astrid/AndroidManifest.xml"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/> <listEntry value="4"/>
<listEntry value="1"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups"> <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>

@ -18,4 +18,11 @@ public final class Functions {
return new Field("UPPER(" + title.toString() + ")"); return new Field("UPPER(" + title.toString() + ")");
} }
/**
* @return SQL now (in milliseconds)
*/
public static Field now() {
return new Field("(strftime('%s','now')*1000)");
}
} }

Binary file not shown.

@ -4,7 +4,6 @@ import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -12,9 +11,6 @@ import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log;
import com.timsu.astrid.R;
/** /**
* Inspired heavily by SynchronizationService * Inspired heavily by SynchronizationService
@ -55,7 +51,7 @@ public class BackupService extends Service {
} }
private void startBackup(Context ctx) { private void startBackup(Context ctx) {
if (ctx == null || ctx.getResources() == null) { /*if (ctx == null || ctx.getResources() == null) {
return; return;
} }
try { try {
@ -84,11 +80,11 @@ public class BackupService extends Service {
ctx.getString(R.string.BPr_backup_desc_failure, ctx.getString(R.string.BPr_backup_desc_failure,
e.toString())); e.toString()));
} }
} }*/
} }
public static void scheduleService(Context ctx) { 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, PendingIntent pendingIntent = PendingIntent.getService(ctx, 0,
createAlarmIntent(ctx), PendingIntent.FLAG_UPDATE_CURRENT); createAlarmIntent(ctx), PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pendingIntent); am.cancel(pendingIntent);
@ -96,7 +92,7 @@ public class BackupService extends Service {
return; return;
} }
am.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis() + BACKUP_OFFSET, am.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis() + BACKUP_OFFSET,
BACKUP_INTERVAL, pendingIntent); BACKUP_INTERVAL, pendingIntent);*/
} }
public static void unscheduleService(Context ctx) { public static void unscheduleService(Context ctx) {
@ -148,7 +144,7 @@ public class BackupService extends Service {
private BackupDirectorySetting backupDirectorySetting = new BackupDirectorySetting() { private BackupDirectorySetting backupDirectorySetting = new BackupDirectorySetting() {
public File getBackupDirectory() { public File getBackupDirectory() {
return TasksXmlExporter.getExportDirectory(); return null; //TasksXmlExporter.getExportDirectory();
} }
}; };

@ -50,7 +50,7 @@ public final class CoreFilterExposer extends BroadcastReceiver {
Filter alphabetical = new Filter(r.getString(R.string.BFE_Alphabetical), Filter alphabetical = new Filter(r.getString(R.string.BFE_Alphabetical),
r.getString(R.string.BFE_Alphabetical), r.getString(R.string.BFE_Alphabetical),
new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
TaskCriteria.isVisible(DateUtilities.now()))). TaskCriteria.isVisible())).
orderBy(Order.asc(Task.TITLE)), orderBy(Order.asc(Task.TITLE)),
null); null);
alphabetical.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_alpha)).getBitmap(); 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), Filter hidden = new Filter(r.getString(R.string.BFE_Hidden),
r.getString(R.string.BFE_Hidden), r.getString(R.string.BFE_Hidden),
new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
Criterion.not(TaskCriteria.isVisible(DateUtilities.now())))). Criterion.not(TaskCriteria.isVisible()))).
orderBy(Order.asc(Task.HIDE_UNTIL)), orderBy(Order.asc(Task.HIDE_UNTIL)),
hiddenValues); hiddenValues);
hidden.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clouds)).getBitmap(); hidden.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clouds)).getBitmap();
@ -74,7 +74,7 @@ public final class CoreFilterExposer extends BroadcastReceiver {
ContentValues completedValues = new ContentValues(); ContentValues completedValues = new ContentValues();
hiddenValues.put(Task.COMPLETION_DATE.name, DateUtilities.now()); hiddenValues.put(Task.COMPLETION_DATE.name, DateUtilities.now());
Filter completed = new Filter(r.getString(R.string.BFE_Completed), r.getString(R.string.BFE_Completed), 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)), Criterion.not(TaskCriteria.isDeleted()))). orderBy(Order.desc(Task.COMPLETION_DATE)),
completedValues); completedValues);
completed.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_check)).getBitmap(); 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) { public static Filter buildInboxFilter(Resources r) {
Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title), Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title),
new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
TaskCriteria.isVisible(DateUtilities.now()))), TaskCriteria.isVisible())),
null); null);
inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap(); inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap();
return inbox; return inbox;

@ -1,104 +1,270 @@
package com.todoroo.astrid.locale; package com.todoroo.astrid.locale;
import java.util.LinkedList;
import android.app.Activity; import android.app.Activity;
import android.app.ExpandableListActivity;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.Window; import android.widget.ExpandableListView;
import android.widget.AdapterView; import android.widget.LinearLayout;
import android.widget.ArrayAdapter;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;
import com.flurry.android.FlurryAgent; import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.data.tag.TagController; import com.todoroo.astrid.adapter.FilterAdapter;
import com.timsu.astrid.data.tag.TagModelForView; 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 * Activity to edit alerts from Locale
* *
* @author timsu * @author Tim Su <tim@todoroo.com>
* *
*/ */
public final class LocaleEditAlerts extends Activity { public final class LocaleEditAlerts extends ExpandableListActivity {
// --- locale constants
/** value for action type for tag alert */ /** value for action type for tag alert */
@SuppressWarnings("nls") @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") @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") @SuppressWarnings("nls")
public static final String KEY_TAG_NAME = "name"; public static final String KEY_SQL = "sql";
private LinkedList<TagModelForView> tags = null; /** key name for interval (integer, # of seconds) */
private String[] tagNames = null; @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()}.
* <p>
* 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. */ /** Called when the activity is first created. */
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState) {
{
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.locale_edit_alerts); 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); * Locale guarantees that the breadcrumb string will be present, but checking for null anyway makes your Activity more
* robust and re-usable
String breadcrumbString = getIntent().getStringExtra(com.twofortyfouram.Intent.EXTRA_STRING_BREADCRUMB); */
if (breadcrumbString == null) final String breadcrumbString = getIntent().getStringExtra(com.twofortyfouram.Intent.EXTRA_STRING_BREADCRUMB);
breadcrumbString = getString(R.string.locale_edit_alerts_title); if (breadcrumbString != null)
else setTitle(String.format("%s%s%s", breadcrumbString, com.twofortyfouram.Intent.BREADCRUMB_SEPARATOR, //$NON-NLS-1$
breadcrumbString = breadcrumbString + com.twofortyfouram.Intent.BREADCRUMB_SEPARATOR + getString(R.string.locale_edit_alerts_title); 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); @Override
tagController.open(); public boolean onChildClick(ExpandableListView parent, View v,
tags = tagController.getAllTags(); int groupPosition, int childPosition, long id) {
tagController.close(); FilterListItem item = (FilterListItem) adapter.getChild(groupPosition,
childPosition);
if(item instanceof Filter) {
adapter.setSelection(item);
}
return true;
}
tagNames = new String[tags.size()]; @Override
for(int i = 0; i < tags.size(); i++) public void onGroupExpand(int groupPosition) {
tagNames[i] = tags.get(i).getName(); FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition);
if(item instanceof Filter) {
adapter.setSelection(item);
}
}
ArrayAdapter<String> tagAdapter = new ArrayAdapter<String>( @Override
this, android.R.layout.simple_spinner_item, tagNames); public void onGroupCollapse(int groupPosition) {
tagAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); onGroupExpand(groupPosition);
tagSpinner.setAdapter(tagAdapter); }
// Save the state into the return Intent whenever the field /**
tagSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { * Called when the {@code Activity} is being terminated. This method determines the state of the {@code Activity} and what
@Override * sort of result should be returned to <i>Locale</i>.
public void onItemSelected(AdapterView<?> arg0, View arg1, */
int arg2, long arg3) { @Override
updateResult(); 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);
} }
else
@Override {
public void onNothingSelected(AdapterView<?> arg0) { /*
// do nothing * 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 @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
// set up flurry
FlurryAgent.onStartSession(this, Constants.FLURRY_KEY); FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
} }
@ -108,86 +274,50 @@ public final class LocaleEditAlerts extends Activity {
FlurryAgent.onEndSession(this); FlurryAgent.onEndSession(this);
} }
/** /**
* Private helper method to persist the Toast message in the return {@code Intent}. * {@inheritDoc}
*/ */
private void updateResult() { @Override
// no tags, so it's not possible to save public boolean onCreateOptionsMenu(final Menu menu)
if(tagNames.length == 0) { {
setResult(com.twofortyfouram.Intent.RESULT_REMOVE); super.onCreateOptionsMenu(menu);
return;
}
final int index = ((Spinner) findViewById(R.id.spinner)).getSelectedItemPosition(); final PackageManager manager = getPackageManager();
final String tagName = tagNames[index];
final TagModelForView tag = tags.get(index);
/* /*
* 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) { menu.add(0, MENU_DONT_SAVE, 0, SharedResources.getTextResource(manager, SharedResources.STRING_MENU_DONTSAVE))
setResult(com.twofortyfouram.Intent.RESULT_REMOVE); .setIcon(SharedResources.getDrawableResource(manager, SharedResources.DRAWABLE_MENU_DONTSAVE)).getItemId();
} else {
final Intent intent = new Intent(); menu.add(0, MENU_SAVE, 0, SharedResources.getTextResource(manager, SharedResources.STRING_MENU_SAVE))
intent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_ACTION_FIRE, .setIcon(SharedResources.getDrawableResource(manager, SharedResources.DRAWABLE_MENU_SAVE)).getItemId();
ACTION_LOCALE_ALERT);
intent.putExtra(KEY_TAG_ID, tag.getTagIdentifier().getId()); return true;
intent.putExtra(KEY_TAG_NAME, tagName); }
intent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BLURB, tagName);
setResult(RESULT_OK, intent); /**
* {@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;
}
} }
}
/** return super.onOptionsItemSelected(item);
* {@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;
}
} }

@ -1,8 +1,7 @@
package com.todoroo.astrid.locale; package com.todoroo.astrid.locale;
import java.util.HashSet; import android.app.Notification;
import java.util.LinkedList; import android.app.PendingIntent;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -10,10 +9,18 @@ import android.content.res.Resources;
import android.util.Log; import android.util.Log;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.data.TaskController; import com.todoroo.andlib.data.TodorooCursor;
import com.timsu.astrid.data.TaskIdentifier; import com.todoroo.andlib.service.Autowired;
import com.timsu.astrid.data.tag.TagController; import com.todoroo.andlib.service.DependencyInjectionService;
import com.timsu.astrid.data.tag.TagIdentifier; 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 * Receiver is activated when Locale conditions are triggered
@ -23,56 +30,67 @@ import com.timsu.astrid.data.tag.TagIdentifier;
*/ */
public class LocaleReceiver extends BroadcastReceiver { public class LocaleReceiver extends BroadcastReceiver {
/** minimum amount of time between two notifications */ @Autowired
private static final long MIN_NOTIFY_INTERVAL = 12*3600*1000L; 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 @Override
/** Called when the system is started up */ /** Called when the system is started up */
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
try { try {
if (LocaleEditAlerts.ACTION_LOCALE_ALERT.equals(intent.getAction())) { if (LocaleEditAlerts.ACTION_LOCALE_ALERT.equals(intent.getAction())) {
final long tagId = intent.getLongExtra(LocaleEditAlerts.KEY_TAG_ID, 0); final String title = intent.getStringExtra(LocaleEditAlerts.KEY_FILTER_TITLE);
final String tagName = intent.getStringExtra(LocaleEditAlerts.KEY_TAG_NAME); final String sql = intent.getStringExtra(LocaleEditAlerts.KEY_SQL);
if(tagId == 0) { final int interval = intent.getIntExtra(LocaleEditAlerts.KEY_INTERVAL, 24*3600);
Log.w("astrid-locale", "Invalid tag identifier in alert");
return;
}
// check if we've already made a notification recently // check if we've already made a notification recently
if(System.currentTimeMillis() - Preferences. String preferenceKey = makePreferenceKey(title, interval);
getLocaleLastAlertTime(context, tagId) < MIN_NOTIFY_INTERVAL) { long lastNotifyTime = Preferences.getLong(preferenceKey, 0);
Log.w("astrid-locale", "Too soon, need " + (MIN_NOTIFY_INTERVAL - System.currentTimeMillis() + Preferences. if(DateUtilities.now() - lastNotifyTime < interval * 1000L) {
getLocaleLastAlertTime(context, tagId))/1000L + " more seconds"); Log.i("astrid-locale", title + ": Too soon, need " + (interval
- (DateUtilities.now() - lastNotifyTime)/1000) + " more seconds");
return; return;
} }
// find out if we have active tasks with this tag // find out if we have active tasks with this tag
TaskController taskController = new TaskController(context); DependencyInjectionService.getInstance().inject(this);
taskController.open(); Filter filter = new Filter(title, title, null, null);
TagController tagController = new TagController(context); filter.sqlQuery = sql;
tagController.open(); TodorooCursor<Task> cursor = taskService.fetchFiltered(filter, Task.ID);
try { try {
HashSet<TaskIdentifier> activeTasks = taskController.getActiveVisibleTaskIdentifiers(); if(cursor.getCount() == 0)
LinkedList<TaskIdentifier> tasks = tagController.getTaggedTasks( return;
new TagIdentifier(tagId)); Resources r = context.getResources();
// int count = TagListSubActivity.countActiveTasks(activeTasks, tasks); String reminder = r.getString(R.string.locale_notification).
int count = 0; replace("$NUM", r.getQuantityString(R.plurals.Ntasks,
if(count > 0) { cursor.getCount(), cursor.getCount())).
Resources r = context.getResources(); replace("$FILTER", title);
// String reminder = r.getString(R.string.notif_tagNotification).
String reminder = "$NUM of $TAG". // show a reminder
replace("$NUM", r.getQuantityString(R.plurals.Ntasks, count, count)). Intent notifyIntent = ShortcutActivity.createIntent(filter);
replace("$TAG", tagName); notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// ReminderService.showTagNotification(context, tagId, reminder); 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, NotificationManager nm = new AndroidNotificationManager(context);
System.currentTimeMillis()); nm.notify(Constants.NOTIFICATION_TIMER, notification);
} else { Preferences.setLong(preferenceKey, DateUtilities.now());
Log.w("astrid-locale", "Locale Notification, but no tasks to show");
}
} finally { } finally {
taskController.close(); cursor.close();
tagController.close();
} }
} }
} catch (Exception e) { } catch (Exception e) {

@ -11,7 +11,6 @@ import android.content.Intent;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterCategory; import com.todoroo.astrid.api.FilterCategory;
@ -44,7 +43,7 @@ public class MilkFilterExposer extends BroadcastReceiver {
MilkDataService.METADATA_JOIN).where(Criterion.and( MilkDataService.METADATA_JOIN).where(Criterion.and(
MetadataCriteria.withKey(MilkTask.METADATA_KEY), MetadataCriteria.withKey(MilkTask.METADATA_KEY),
TaskCriteria.isActive(), TaskCriteria.isActive(),
TaskCriteria.isVisible(DateUtilities.now()), TaskCriteria.isVisible(),
MilkTask.LIST_ID.eq(list.id))), MilkTask.LIST_ID.eq(list.id))),
values); values);

@ -22,7 +22,6 @@ import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.SoftHashMap; import com.todoroo.andlib.utility.SoftHashMap;
import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
@ -267,7 +266,7 @@ public final class MilkDataService {
// read all list counts // read all list counts
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(MilkTask.LIST_ID, COUNT). TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(MilkTask.LIST_ID, COUNT).
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))). 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)); groupBy(MilkTask.LIST_ID));
try { try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {

@ -12,7 +12,6 @@ import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
@ -95,7 +94,7 @@ public final class TagService {
return new QueryTemplate().where(Criterion.and( return new QueryTemplate().where(Criterion.and(
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).where(MetadataCriteria.withKey(KEY)))), Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).where(MetadataCriteria.withKey(KEY)))),
TaskCriteria.isActive(), TaskCriteria.isActive(),
TaskCriteria.isVisible(DateUtilities.now()))); TaskCriteria.isVisible()));
} }
/** /**

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/list_selector_background"
android:paddingTop="4dip"
android:paddingBottom="4dip"
android:paddingLeft="4dip"
android:paddingRight="6dip"
android:orientation="horizontal">
<!-- expander icon -->
<ImageView android:id="@+id/expander"
android:layout_width="32dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:scaleType="fitCenter"
android:visibility="gone"/>
<!-- filter icon -->
<ImageView android:id="@+id/icon"
android:layout_width="32dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:scaleType="fitCenter"
android:visibility="gone"/>
<!-- filter name -->
<TextView android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:paddingLeft="5dip"
android:gravity="center_vertical"/>
<!-- selected -->
<ImageView android:id="@+id/selected"
android:layout_width="24dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:src="@drawable/filter_selected_icon"
android:scaleType="fitCenter"
android:visibility="gone"/>
</LinearLayout>

@ -5,32 +5,52 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<TextView <!-- At runtime, we'll load the Locale background frame -->
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/locale_edit_intro" />
<LinearLayout <LinearLayout
android:id="@+id/frame"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/locale_border" android:layout_weight="1">
android:layout_margin="10dip">
<TextView <TextView
android:paddingTop="5dip"
android:paddingBottom="10dip"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/locale_pick_filter" android:paddingBottom="10px"
android:gravity="center"
android:text="@string/locale_edit_intro"
style="@style/TextAppearance.Locale_Label" /> style="@style/TextAppearance.Locale_Label" />
<Spinner <FrameLayout
android:paddingBottom="10dip" android:layout_width="fill_parent"
android:id="@+id/spinner" android:layout_height="fill_parent"
android:layout_width="fill_parent" android:layout_weight="100"
android:layout_height="wrap_content" /> android:layout_margin="10px"
android:background="@drawable/locale_border">
<ExpandableListView android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="vertical"
android:cacheColorHint="@android:color/white"/>
</FrameLayout>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/locale_interval_label"
style="@style/TextAppearance.Locale_Label" />
<Spinner android:id="@+id/intervalSpinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingBottom="10dip"
android:entries="@array/locale_interval" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/screen"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/locale_ellipsizing_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_gravity="center_vertical"
android:textColor="#FFFFFF"
android:textStyle="bold"
android:ellipsize="start"
android:singleLine="true" />
</RelativeLayout>

@ -35,8 +35,6 @@
<TextView android:id="@+id/title" <TextView android:id="@+id/title"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/completeBox"
style="@style/TextAppearance.TAd_ItemTitle" style="@style/TextAppearance.TAd_ItemTitle"
android:gravity="center_vertical"/> android:gravity="center_vertical"/>
@ -44,8 +42,6 @@
<TextView android:id="@+id/dueDate" <TextView android:id="@+id/dueDate"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_toRightOf="@id/completeBox"
android:singleLine="true"/> android:singleLine="true"/>
<TextView android:id="@+id/details" <TextView android:id="@+id/details"
@ -70,8 +66,6 @@
<LinearLayout android:id="@+id/actions" <LinearLayout android:id="@+id/actions"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/details"
android:layout_alignParentLeft="true"
android:background="#4499bbcc" android:background="#4499bbcc"
android:visibility="gone" android:visibility="gone"
android:paddingTop="4dip" android:paddingTop="4dip"

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item
android:title="Help"
android:icon="@drawable/icon_help_color_40"
android:id="@+id/menu_help" />
<item
android:title="Don't Save"
android:icon="@drawable/icon_dontsave_color_40"
android:id="@+id/menu_dontsave" />
<item
android:title="Save"
android:icon="@drawable/icon_save_color_40"
android:id="@+id/menu_save" />
</menu>

@ -8,7 +8,7 @@
<string name="BFE_Active">Active Tasks</string> <string name="BFE_Active">Active Tasks</string>
<!-- Title for Active Tasks Filter (what user sees when first opening the app) --> <!-- Title for Active Tasks Filter (what user sees when first opening the app) -->
<string name="BFE_Active_title">Astrid: Home</string> <string name="BFE_Active_title">Active Tasks</string>
<!-- Search --> <!-- Search -->
<string name="BFE_Search">Search</string> <string name="BFE_Search">Search</string>

@ -2,22 +2,54 @@
<!-- See the file "LICENSE" for the full license governing this code. --> <!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Resources for built-in locale plug-in --> <!-- Resources for built-in locale plug-in -->
<!-- Locale Alert Editing Window Title --> <!-- Locale Alert Editing Window Title -->
<string name="locale_edit_alerts_title">Astrid Tag Alert</string> <string name="locale_edit_alerts_title">Astrid Filter Alert</string>
<!-- Locale Window Help --> <!-- Locale Window Help -->
<string name="locale_edit_intro">Astrid will send you a reminder <string name="locale_edit_intro">Astrid will send you a reminder
when you have uncompleted tasks in the following filter:</string> when you have any tasks in the following filter:</string>
<!-- Locale Window Filter Picker UI --> <!-- Locale Window Filter Picker UI -->
<string name="locale_pick_filter">Filter:</string> <string name="locale_pick_filter">Filter:</string>
<!-- Locale Window Interval Label -->
<string name="locale_interval_label">Limit notifications to:</string>
<!-- Locale Window Interval Values -->
<string-array name="locale_interval">
<item>once an hour</item>
<item>once every six hours</item>
<item>once every twelve hours</item>
<item>once a day</item>
<item>once every three days</item>
<item>once a week</item>
</string-array>
<!-- Notification -->
<string name="locale_notification">You have $NUM tasks in $FILTER</string>
<!-- Locale style --> <!-- Locale style -->
<style name="TextAppearance.Locale_Label"> <style name="TextAppearance.Locale_Label">
<item name="android:textSize">16sp</item> <item name="android:textSize">16sp</item>
<item name="android:textColor">@android:color/black</item> <item name="android:textColor">@android:color/black</item>
</style> </style>
<!-- Locale filter list styles -->
<style name="TextAppearance.LEA_Filter">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="TextAppearance.LEA_Header" parent="TextAppearance.LEA_Filter">
<item name="android:textSize">13sp</item>
<item name="android:textColor">#ffdddddd</item>
</style>
<style name="TextAppearance.LEA_Category" parent="TextAppearance.LEA_Filter">
<item name="android:textSize">13sp</item>
<item name="android:textStyle">bold|italic</item>
</style>
</resources> </resources>

@ -2,7 +2,7 @@
<!-- See the file "LICENSE" for the full license governing this code. --> <!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ============================ General ============================= --> <!-- ========================================================== General -->
<style name="TextAppearance" parent="android:TextAppearance" /> <style name="TextAppearance" parent="android:TextAppearance" />
@ -16,7 +16,7 @@
<item name="android:windowBackground">@null</item> <item name="android:windowBackground">@null</item>
</style> </style>
<!--========================= TaskListActivity ======================== --> <!--=============================================== TaskListActivity == -->
<style name="TextAppearance.TLA_NoItems"> <style name="TextAppearance.TLA_NoItems">
<item name="android:textSize">20sp</item> <item name="android:textSize">20sp</item>
@ -33,7 +33,7 @@
<item name="android:textColor">#000000</item> <item name="android:textColor">#000000</item>
</style> </style>
<!-- =========================== TaskAdapter =========================== --> <!-- ==================================================== TaskAdapter == -->
<style name="TextAppearance.TAd_ItemTitle"> <style name="TextAppearance.TAd_ItemTitle">
</style> </style>
@ -62,25 +62,24 @@
<item name="android:textColor">#ffee5555</item> <item name="android:textColor">#ffee5555</item>
</style> </style>
<!-- ======================== FilterListAdapter ======================== --> <!-- ============================================== FilterListAdapter == -->
<style name="TextAppearance.FLA_Filter" parent="TextAppearance.TAd_ItemTitle"> <style name="TextAppearance.FLA_Filter">
<item name="android:textSize">20sp</item> <item name="android:textSize">20sp</item>
<item name="android:paddingLeft">10dip</item>
</style> </style>
<style name="TextAppearance.FLA_Header" parent="TextAppearance.TAd_ItemTitle"> <style name="TextAppearance.FLA_Header" parent="TextAppearance.FLA_Filter">
<item name="android:textSize">18sp</item> <item name="android:textSize">18sp</item>
<item name="android:textColor">#ffdddddd</item> <item name="android:textColor">#ffdddddd</item>
</style> </style>
<style name="TextAppearance.FLA_Category" parent="TextAppearance.TAd_ItemTitle"> <style name="TextAppearance.FLA_Category" parent="TextAppearance.FLA_Filter">
<item name="android:textSize">18sp</item> <item name="android:textSize">18sp</item>
<item name="android:textColor">#ff7ada24</item> <item name="android:textColor">#ff7ada24</item>
<item name="android:textStyle">bold|italic</item> <item name="android:textStyle">bold|italic</item>
</style> </style>
<!-- ======================== FilterListAdapter ======================== --> <!-- ========================================================= Widget == -->
<style name="TextAppearance.Widget"> <style name="TextAppearance.Widget">
<item name="android:textSize">14sp</item> <item name="android:textSize">14sp</item>

@ -6,32 +6,28 @@ package com.todoroo.astrid.activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ExpandableListActivity; import android.app.ExpandableListActivity;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import com.flurry.android.FlurryAgent; import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R; import com.timsu.astrid.R;
@ -42,9 +38,7 @@ import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.adapter.FilterAdapter; import com.todoroo.astrid.adapter.FilterAdapter;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.api.SearchFilter; import com.todoroo.astrid.api.SearchFilter;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.model.Task;
@ -77,7 +71,6 @@ public class FilterListActivity extends ExpandableListActivity {
protected DialogUtilities dialogUtilities; protected DialogUtilities dialogUtilities;
FilterAdapter adapter = null; FilterAdapter adapter = null;
FilterReceiver filterReceiver = new FilterReceiver();
/* ====================================================================== /* ======================================================================
* ======================================================= initialization * ======================================================= initialization
@ -98,7 +91,6 @@ public class FilterListActivity extends ExpandableListActivity {
setTitle(R.string.FLA_title); setTitle(R.string.FLA_title);
setUpList(); setUpList();
getLists();
onNewIntent(getIntent()); onNewIntent(getIntent());
} }
@ -169,44 +161,13 @@ public class FilterListActivity extends ExpandableListActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
registerReceiver(filterReceiver, adapter.registerRecevier();
new IntentFilter(AstridApiConstants.BROADCAST_SEND_FILTERS));
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
unregisterReceiver(filterReceiver); adapter.unregisterRecevier();
}
/**
* Receiver which receives intents to add items to the filter list
*
* @author Tim Su <tim@todoroo.com>
*
*/
protected class FilterReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
final Parcelable[] filters = intent.getExtras().
getParcelableArray(AstridApiConstants.EXTRAS_RESPONSE);
for (Parcelable item : filters) {
adapter.add((FilterListItem)item);
}
adapter.notifyDataSetChanged();
runOnUiThread(new Runnable() {
@Override
public void run() {
expandList(filters);
}
});
} catch (Exception e) {
exceptionService.reportError("receive-filter-" + //$NON-NLS-1$
intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON), e);
}
}
} }
/* ====================================================================== /* ======================================================================
@ -215,39 +176,11 @@ public class FilterListActivity extends ExpandableListActivity {
/** Sets up the coach list adapter */ /** Sets up the coach list adapter */
protected void setUpList() { protected void setUpList() {
adapter = new FilterAdapter(this); adapter = new FilterAdapter(this, getExpandableListView(),
R.layout.filter_adapter_row);
setListAdapter(adapter); setListAdapter(adapter);
registerForContextMenu(getExpandableListView()); registerForContextMenu(getExpandableListView());
getExpandableListView().setGroupIndicator(
getResources().getDrawable(R.drawable.expander_group));
}
/**
* Expand the first category filter in this group
* @param filters
*/
protected void expandList(Parcelable[] filters) {
ExpandableListView list = getExpandableListView();
for(Parcelable filter : filters) {
if(filter instanceof FilterCategory) {
for(int i = 0; i < adapter.getGroupCount(); i++)
if(adapter.getGroup(i) == filter) {
list.expandGroup(i);
return;
}
}
}
}
/**
* Broadcast a request for lists. The request is sent to every
* application registered to listen for this broadcast. Each application
* can then add lists to this activity
*/
protected void getLists() {
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_FILTERS);
sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
} }
/* ====================================================================== /* ======================================================================

@ -6,35 +6,62 @@ package com.todoroo.astrid.adapter;
import java.util.ArrayList; import java.util.ArrayList;
import android.app.Activity; import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.os.Parcelable;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.view.Gravity; import android.util.Log;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseExpandableListAdapter; import android.widget.BaseExpandableListAdapter;
import android.widget.FrameLayout; import android.widget.ExpandableListView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.ImageView.ScaleType;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.FilterCategory; import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListHeader; import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
public class FilterAdapter extends BaseExpandableListAdapter { public class FilterAdapter extends BaseExpandableListAdapter {
private final ArrayList<FilterListItem> items; // --- style constants
public int filterStyle = R.style.TextAppearance_FLA_Filter;
public int categoryStyle = R.style.TextAppearance_FLA_Category;
public int headerStyle = R.style.TextAppearance_FLA_Header;
// --- instance variables
protected final Activity activity; protected final Activity activity;
protected final ExpandableListView listView;
private final ArrayList<FilterListItem> items;
private final DisplayMetrics metrics = new DisplayMetrics(); private final DisplayMetrics metrics = new DisplayMetrics();
private final FilterReceiver filterReceiver = new FilterReceiver();
private final int layout;
private final LayoutInflater inflater;
public FilterAdapter(Activity activity) { public FilterAdapter(Activity activity, ExpandableListView listView,
int rowLayout) {
super(); super();
this.activity = activity; this.activity = activity;
this.items = new ArrayList<FilterListItem>(); this.items = new ArrayList<FilterListItem>();
this.listView = listView;
this.layout = rowLayout;
inflater = (LayoutInflater) activity.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
listView.setGroupIndicator(
activity.getResources().getDrawable(R.drawable.expander_group));
getLists();
} }
public boolean hasStableIds() { public boolean hasStableIds() {
@ -49,6 +76,35 @@ public class FilterAdapter extends BaseExpandableListAdapter {
items.clear(); items.clear();
} }
/**
* Create or reuse a view
* @param convertView
* @param parent
* @return
*/
protected View newView(View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = inflater.inflate(layout, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.view = convertView;
viewHolder.expander = (ImageView)convertView.findViewById(R.id.expander);
viewHolder.icon = (ImageView)convertView.findViewById(R.id.icon);
viewHolder.name = (TextView)convertView.findViewById(R.id.name);
viewHolder.selected = (ImageView)convertView.findViewById(R.id.selected);
convertView.setTag(viewHolder);
}
return convertView;
}
private class ViewHolder {
public FilterListItem item;
public ImageView expander;
public ImageView icon;
public TextView name;
public ImageView selected;
public View view;
}
/* ====================================================================== /* ======================================================================
* ========================================================== child nodes * ========================================================== child nodes
* ====================================================================== */ * ====================================================================== */
@ -74,9 +130,13 @@ public class FilterAdapter extends BaseExpandableListAdapter {
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) { View convertView, ViewGroup parent) {
FilterListItem item = (FilterListItem)getChild(groupPosition, childPosition);
View textView = getStandardView(item, true); convertView = newView(convertView, parent);
return textView; ViewHolder viewHolder = (ViewHolder) convertView.getTag();
viewHolder.item = (FilterListItem)getChild(groupPosition, childPosition);
populateView(viewHolder, true, false);
return convertView;
} }
/* ====================================================================== /* ======================================================================
@ -97,8 +157,11 @@ public class FilterAdapter extends BaseExpandableListAdapter {
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) { ViewGroup parent) {
View view = getView((FilterListItem)getGroup(groupPosition), false, isExpanded); convertView = newView(convertView, parent);
return view; ViewHolder viewHolder = (ViewHolder) convertView.getTag();
viewHolder.item = (FilterListItem) getGroup(groupPosition);
populateView(viewHolder, false, isExpanded);
return convertView;
} }
public boolean isChildSelectable(int groupPosition, int childPosition) { public boolean isChildSelectable(int groupPosition, int childPosition) {
@ -106,110 +169,156 @@ public class FilterAdapter extends BaseExpandableListAdapter {
} }
/* ====================================================================== /* ======================================================================
* ================================================================ views * ============================================================ selection
* ====================================================================== */ * ====================================================================== */
public View getView(FilterListItem item, boolean isChild, boolean isExpanded) { private FilterListItem selection = null;
if(item instanceof FilterListHeader)
return getHeaderView((FilterListHeader)item, isChild);
else if(item instanceof FilterCategory)
return getCategoryView((FilterCategory)item, isExpanded);
else
return getStandardView(item, isChild);
}
public View getCategoryView(FilterCategory filter, boolean isExpanded) {
AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
FrameLayout layout = new FrameLayout(activity); /**
layout.setLayoutParams(lp); * Sets the selected item to this one
* @param picked
ImageView image = new ImageView(activity); */
if(isExpanded) public void setSelection(FilterListItem picked) {
image.setImageResource(R.drawable.expander_ic_maximized); if(picked == selection)
selection = null;
else else
image.setImageResource(R.drawable.expander_ic_minimized); selection = picked;
FrameLayout.LayoutParams expansionImageLayout = new FrameLayout.LayoutParams( int scroll = listView.getScrollY();
32, 32); notifyDataSetInvalidated();
expansionImageLayout.gravity = Gravity.CENTER_VERTICAL; listView.scrollTo(0, scroll);
image.setPadding(5, 0, 5, 0); }
image.setLayoutParams(expansionImageLayout);
image.setScaleType(ScaleType.FIT_CENTER);
layout.addView(image);
TextView textView = new TextView(activity);
textView.setGravity(Gravity.CENTER_VERTICAL);
textView.setText(filter.listingTitle);
textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Category);
View view = augmentView(textView, filter); /**
view.setPadding((int) (33 * metrics.density), 5, 0, 5); * Gets the currently selected item
FrameLayout.LayoutParams rowLayout = new FrameLayout.LayoutParams( * @return null if no item is to be selected
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); */
rowLayout.gravity = Gravity.CENTER_VERTICAL; public FilterListItem getSelection() {
view.setLayoutParams(rowLayout); return selection;
}
layout.addView(view); /* ======================================================================
* ============================================================= receiver
* ====================================================================== */
return layout; /**
* Receiver which receives intents to add items to the filter list
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class FilterReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
final Parcelable[] filters = intent.getExtras().
getParcelableArray(AstridApiConstants.EXTRAS_RESPONSE);
for (Parcelable item : filters) {
add((FilterListItem)item);
onReceiveFilter((FilterListItem)item);
}
notifyDataSetChanged();
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
expandList(filters);
}
});
} catch (Exception e) {
Log.e("receive-filter-" + //$NON-NLS-1$
intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON),
e.toString(), e);
}
}
} }
/** /**
* Decorate textview and add an image if the filter requests it * Expand the first category filter in this group
* @param textView * @param filters
* @param filter
* @return final view ready to be added
*/ */
private View augmentView(TextView textView, FilterListItem filter) { protected void expandList(Parcelable[] filters) {
if(filter.listingIcon != null) { for(Parcelable filter : filters) {
LinearLayout layout = new LinearLayout(activity); if(filter instanceof FilterCategory) {
layout.setGravity(textView.getGravity()); for(int i = 0; i < getGroupCount(); i++)
layout.setOrientation(LinearLayout.HORIZONTAL); if(getGroup(i) == filter) {
listView.expandGroup(i);
ImageView icon = new ImageView(activity); return;
icon.setImageBitmap(filter.listingIcon); }
icon.setScaleType(ScaleType.CENTER); }
icon.setPadding(0, 0, 15, 0);
layout.addView(icon);
layout.addView(textView);
return layout;
} }
return textView;
} }
public View getStandardView(FilterListItem filter, boolean isChild) { /**
AbsListView.LayoutParams lp = new AbsListView.LayoutParams( * Broadcast a request for lists. The request is sent to every
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); * application registered to listen for this broadcast. Each application
* can then add lists to this activity
*/
protected void getLists() {
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_FILTERS);
activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
TextView textView = new TextView(activity); /**
textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); * Call this method from your activity's onResume() method
textView.setText(filter.listingTitle); */
textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Filter); public void registerRecevier() {
activity.registerReceiver(filterReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_FILTERS));
}
View view = augmentView(textView, filter); /**
view.setBackgroundDrawable(null); * Call this method from your activity's onResume() method
view.setLayoutParams(lp); */
view.setPadding((int) ((isChild ? 27 : 7) * metrics.density), 8, 0, 8); public void unregisterRecevier() {
activity.unregisterReceiver(filterReceiver);
}
return view; /**
* Called when an item comes through. Override if you like
* @param item
*/
public void onReceiveFilter(FilterListItem item) {
// do nothing
} }
public View getHeaderView(FilterListHeader header, boolean isChild) { /* ======================================================================
AbsListView.LayoutParams lp = new AbsListView.LayoutParams( * ================================================================ views
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); * ====================================================================== */
TextView textView = new TextView(activity); public void populateView(ViewHolder viewHolder, boolean isChild, boolean isExpanded) {
textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); FilterListItem filter = viewHolder.item;
textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Header);
textView.setText(header.listingTitle); viewHolder.view.setBackgroundResource(0);
viewHolder.expander.setVisibility(View.GONE);
if(viewHolder.item instanceof FilterListHeader) {
viewHolder.name.setTextAppearance(activity, headerStyle);
viewHolder.view.setBackgroundResource(R.drawable.edit_titlebar);
viewHolder.view.setPadding((int) ((isChild ? 33 : 7) * metrics.density), 5, 0, 5);
} else if(viewHolder.item instanceof FilterCategory) {
viewHolder.expander.setVisibility(View.VISIBLE);
if(isExpanded)
viewHolder.expander.setImageResource(R.drawable.expander_ic_maximized);
else
viewHolder.expander.setImageResource(R.drawable.expander_ic_minimized);
viewHolder.name.setTextAppearance(activity, categoryStyle);
viewHolder.view.setPadding((int)(7 * metrics.density), 8, 0, 8);
} else {
viewHolder.name.setTextAppearance(activity, filterStyle);
viewHolder.view.setPadding((int) ((isChild ? 27 : 7) * metrics.density), 8, 0, 8);
}
viewHolder.icon.setVisibility(filter.listingIcon != null ? View.VISIBLE : View.GONE);
viewHolder.icon.setImageBitmap(filter.listingIcon);
View view = augmentView(textView, header); viewHolder.name.setText(filter.listingTitle);
view.setBackgroundResource(R.drawable.edit_titlebar);
view.setLayoutParams(lp);
view.setPadding((int) ((isChild ? 33 : 7) * metrics.density), 5, 0, 5);
return view; // selection
if(selection == viewHolder.item) {
viewHolder.selected.setVisibility(View.VISIBLE);
viewHolder.view.setBackgroundColor(Color.rgb(128, 230, 0));
} else
viewHolder.selected.setVisibility(View.GONE);
} }
} }

@ -16,6 +16,7 @@ import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
@ -71,9 +72,9 @@ public class TaskDao extends GenericDao<Task> {
Task.DELETION_DATE.eq(0)); Task.DELETION_DATE.eq(0));
} }
/** @return tasks that are not hidden at given unixtime */ /** @return tasks that are not hidden at current time */
public static Criterion isVisible(long time) { public static Criterion isVisible() {
return Task.HIDE_UNTIL.lt(time); return Task.HIDE_UNTIL.lt(Functions.now());
} }
/** @return tasks that have a due date */ /** @return tasks that have a due date */
@ -82,18 +83,18 @@ public class TaskDao extends GenericDao<Task> {
} }
/** @return tasks that are due before a certain unixtime */ /** @return tasks that are due before a certain unixtime */
public static Criterion dueBefore(long time) { public static Criterion dueBeforeNow() {
return Criterion.and(Task.DUE_DATE.gt(0), Task.DUE_DATE.lt(time)); return Criterion.and(Task.DUE_DATE.gt(0), Task.DUE_DATE.lt(Functions.now()));
} }
/** @return tasks that are due after a certain unixtime */ /** @return tasks that are due after a certain unixtime */
public static Criterion dueAfter(long time) { public static Criterion dueAfterNow() {
return Task.DUE_DATE.gt(time); return Task.DUE_DATE.gt(Functions.now());
} }
/** @return tasks completed before a given unixtime */ /** @return tasks completed before a given unixtime */
public static Criterion completedBefore(long time) { public static Criterion completed() {
return Criterion.and(Task.COMPLETION_DATE.gt(0), Task.COMPLETION_DATE.lt(time)); return Criterion.and(Task.COMPLETION_DATE.gt(0), Task.COMPLETION_DATE.lt(Functions.now()));
} }
/** @return tasks that have a blank or null title */ /** @return tasks that have a blank or null title */

@ -21,6 +21,7 @@ import android.util.Log;
import com.google.ical.values.Frequency; import com.google.ical.values.Frequency;
import com.google.ical.values.RRule; import com.google.ical.values.RRule;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.utilities.LegacyTasksXmlExporter;
import com.todoroo.andlib.data.AbstractModel; import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.GenericDao; import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
@ -32,7 +33,6 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.alarms.Alarm; import com.todoroo.astrid.alarms.Alarm;
import com.todoroo.astrid.alarms.AlarmDatabase; import com.todoroo.astrid.alarms.AlarmDatabase;
import com.todoroo.astrid.backup.TasksXmlExporter;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
@ -42,6 +42,7 @@ import com.todoroo.astrid.rmilk.data.MilkTask;
import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
@SuppressWarnings("deprecation")
public class Astrid2To3UpgradeHelper { public class Astrid2To3UpgradeHelper {
@Autowired @Autowired
@ -119,13 +120,7 @@ public class Astrid2To3UpgradeHelper {
dialog = dialogUtilities.progressDialog(context, context.getString(R.string.DLG_wait)); dialog = dialogUtilities.progressDialog(context, context.getString(R.string.DLG_wait));
// initiate a backup // initiate a backup
try { legacyBackup();
TasksXmlExporter exporter = new TasksXmlExporter(true);
exporter.setContext(ContextManager.getContext());
exporter.exportTasks(TasksXmlExporter.getExportDirectory());
} catch (Exception e) {
// unable to create a backup before upgrading :(
}
database.openForWriting(); database.openForWriting();
@ -192,6 +187,19 @@ public class Astrid2To3UpgradeHelper {
// --- database upgrade helpers // --- database upgrade helpers
/**
* Create a legacy backup file
*/
private void legacyBackup() {
try {
LegacyTasksXmlExporter exporter = new LegacyTasksXmlExporter(true);
exporter.setContext(ContextManager.getContext());
exporter.exportTasks(LegacyTasksXmlExporter.getExportDirectory());
} catch (Exception e) {
// unable to create a backup before upgrading :(
}
}
protected static final class UpgradeVisitorContainer<TYPE extends AbstractModel> { protected static final class UpgradeVisitorContainer<TYPE extends AbstractModel> {
public int columnIndex; public int columnIndex;
public Cursor cursor; public Cursor cursor;

@ -8,8 +8,8 @@ import android.app.AlertDialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -19,7 +19,6 @@ import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.service.ExceptionService.TodorooUncaughtExceptionHandler; import com.todoroo.andlib.service.ExceptionService.TodorooUncaughtExceptionHandler;
import com.todoroo.astrid.backup.BackupService;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.reminders.ReminderService; import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
@ -104,9 +103,6 @@ public class StartupService {
am.setInexactRepeating(AlarmManager.RTC, 0, am.setInexactRepeating(AlarmManager.RTC, 0,
Constants.WIDGET_UPDATE_INTERVAL, pendingIntent); Constants.WIDGET_UPDATE_INTERVAL, pendingIntent);
// start backup service
BackupService.scheduleService(context);
database.openForWriting(); database.openForWriting();
taskService.cleanup(); taskService.cleanup();
} }

@ -83,7 +83,7 @@ public class Preferences {
return PreferenceManager.getDefaultSharedPreferences(context); return PreferenceManager.getDefaultSharedPreferences(context);
} }
// --- preference fetching // --- preference fetching (string)
/** Gets an string value from a string preference. Returns null /** Gets an string value from a string preference. Returns null
* if the value is not set * if the value is not set
@ -167,6 +167,28 @@ public class Preferences {
} }
} }
/**
* Sets string preference
*/
public static void setString(int keyResource, String newValue) {
Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit();
editor.putString(context.getString(keyResource), newValue);
editor.commit();
}
/**
* Sets string preference from integer value
*/
public static void setStringFromInteger(int keyResource, int newValue) {
Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit();
editor.putString(context.getString(keyResource), Integer.toString(newValue));
editor.commit();
}
// --- preference fetching (boolean)
/** Gets a boolean preference (e.g. a CheckBoxPreference setting) /** Gets a boolean preference (e.g. a CheckBoxPreference setting)
* *
* @param key * @param key
@ -200,23 +222,31 @@ public class Preferences {
editor.commit(); editor.commit();
} }
/** // --- preference fetching (long)
* Sets string preference
/** Gets a long preference
*
* @param key
* @param defValue
* @return default if value is unset otherwise the value
*/ */
public static void setString(int keyResource, String newValue) { public static long getLong(String key, long defValue) {
Context context = ContextManager.getContext(); Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit(); return getPrefs(context).getLong(key, defValue);
editor.putString(context.getString(keyResource), newValue);
editor.commit();
} }
/** /**
* Sets string preference from integer value * Sets long preference
* @param key
* @param value
*/ */
public static void setStringFromInteger(int keyResource, int newValue) { public static void setLong(String key, long value) {
Context context = ContextManager.getContext(); Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit(); Editor editor = getPrefs(context).edit();
editor.putString(context.getString(keyResource), Integer.toString(newValue)); editor.putLong(key, value);
editor.commit(); editor.commit();
} }
} }

@ -131,24 +131,24 @@ public class TaskDaoTests extends DatabaseTestCase {
// check due before / after // check due before / after
cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria. cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria.
dueBefore(DateUtilities.now()))); dueBeforeNow()));
cursor.moveToNext(); cursor.moveToNext();
assertEquals(1, cursor.getCount()); assertEquals(1, cursor.getCount());
cursor.close(); cursor.close();
cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria. cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria.
dueAfter(DateUtilities.now()))); dueAfterNow()));
assertEquals(1, cursor.getCount()); assertEquals(1, cursor.getCount());
cursor.close(); cursor.close();
// check completed before // check completed before
cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria. cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria.
completedBefore(DateUtilities.now()))); completed()));
assertEquals(1, cursor.getCount()); assertEquals(1, cursor.getCount());
cursor.close(); cursor.close();
// check is visible // check is visible
cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria. cursor = taskDao.query(Query.select(TITLES).where(TaskCriteria.
isVisible(DateUtilities.now()))); isVisible()));
assertEquals(5, cursor.getCount()); assertEquals(5, cursor.getCount());
cursor.close(); cursor.close();
} }

Loading…
Cancel
Save