Backup preferences, backup as a service

pull/14/head
Tim Su 14 years ago
parent 4ad61000a8
commit abb753d2ee

@ -201,6 +201,19 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.todoroo.astrid.backup.BackupPreferences"
android:label="@string/backup_BPr_header">
<intent-filter>
<action android:name="com.todoroo.astrid.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name="com.todoroo.astrid.backup.BackupStartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- notes --> <!-- notes -->

@ -9,6 +9,8 @@ import android.os.Parcelable;
import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.sql.QueryTemplate;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/** /**
* A <code>FilterListFilter</code> allows users to display tasks that have * A <code>FilterListFilter</code> allows users to display tasks that have
* something in common. * something in common.
@ -36,6 +38,7 @@ public final class Filter extends FilterListItem {
* <p> * <p>
* e.g "Tasks With Notes" * e.g "Tasks With Notes"
*/ */
@CheckForNull
public String title; public String title;
/** /**
@ -53,6 +56,7 @@ public final class Filter extends FilterListItem {
* metadata.value = 'b' GROUP BY tasks.id ORDER BY tasks.title"</code> * metadata.value = 'b' GROUP BY tasks.id ORDER BY tasks.title"</code>
* </ul> * </ul>
*/ */
@CheckForNull
public String sqlQuery; public String sqlQuery;
/** /**
@ -61,6 +65,7 @@ public final class Filter extends FilterListItem {
* tasks they create should also be tagged 'ABC'. If set to null, no * tasks they create should also be tagged 'ABC'. If set to null, no
* additional values will be stored for a task. * additional values will be stored for a task.
*/ */
@CheckForNull
public ContentValues valuesForNewTasks = null; public ContentValues valuesForNewTasks = null;
/** /**

@ -5,6 +5,7 @@ package com.todoroo.astrid.api;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/** /**
* A <code>FilterCategory</code> groups common {@link Filter}s and allows * A <code>FilterCategory</code> groups common {@link Filter}s and allows
@ -18,6 +19,7 @@ public class FilterCategory extends FilterListItem {
/** /**
* {@link Filter}s contained by this category * {@link Filter}s contained by this category
*/ */
@CheckForNull
public Filter[] children; public Filter[] children;
/** /**

@ -7,6 +7,7 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/** /**
* Represents an item displayed by Astrid's FilterListActivity * Represents an item displayed by Astrid's FilterListActivity
@ -19,17 +20,20 @@ abstract public class FilterListItem implements Parcelable {
/** /**
* Title of this item displayed on the Filters page * Title of this item displayed on the Filters page
*/ */
@CheckForNull
public String listingTitle = null; public String listingTitle = null;
/** /**
* Bitmap for icon used on listing page. <code>null</code> => no icon * Bitmap for icon used on listing page. <code>null</code> => no icon
*/ */
@CheckForNull
public Bitmap listingIcon = null; public Bitmap listingIcon = null;
/** /**
* Context Menu labels. The context menu will be displayed when users * Context Menu labels. The context menu will be displayed when users
* long-press on this filter list item. * long-press on this filter list item.
*/ */
@CheckForNull
public String contextMenuLabels[] = new String[0]; public String contextMenuLabels[] = new String[0];
/** /**
@ -37,6 +41,7 @@ abstract public class FilterListItem implements Parcelable {
* content menu label is invoked. This array must be the same size as * content menu label is invoked. This array must be the same size as
* the contextMenuLabels array. * the contextMenuLabels array.
*/ */
@CheckForNull
public Intent contextMenuIntents[] = new Intent[0]; public Intent contextMenuIntents[] = new Intent[0];
// --- parcelable helpers // --- parcelable helpers

@ -7,6 +7,7 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import android.widget.RemoteViews.RemoteView; import android.widget.RemoteViews.RemoteView;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/** /**
* Represents a line of text displayed in the Task List * Represents a line of text displayed in the Task List
@ -29,6 +30,7 @@ public final class TaskDecoration implements Parcelable {
/** /**
* {@link RemoteView} decoration * {@link RemoteView} decoration
*/ */
@CheckForNull
public RemoteViews decoration = null; public RemoteViews decoration = null;
/** /**

@ -0,0 +1,125 @@
package com.todoroo.astrid.backup;
import java.util.Date;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.view.View;
import android.view.ViewGroup.OnHierarchyChangeListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.widget.TodorooPreferences;
import com.todoroo.astrid.utility.Preferences;
/**
* Displays synchronization preferences and an action panel so users can
* initiate actions from the menu.
*
* @author timsu
*
*/
public class BackupPreferences extends TodorooPreferences {
static final String PREF_BACKUP_LAST_DATE = "backupDate"; //$NON-NLS-1$
static final String PREF_BACKUP_LAST_ERROR = "backupError"; //$NON-NLS-1$
@Autowired
private DialogUtilities dialogUtilities;
private int statusColor = Color.BLACK;
@Override
public int getPreferenceResource() {
return R.xml.preferences_backup;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getListView().setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
@Override
public void onChildViewRemoved(View parent, View child) {
//
}
@Override
public void onChildViewAdded(View parent, View child) {
View view = findViewById(R.id.status);
if(view != null)
view.setBackgroundColor(statusColor);
}
});
}
@Override
protected void onPause() {
super.onPause();
BackupService.scheduleService(this);
}
/**
*
* @param resource
* if null, updates all resources
*/
@Override
public void updatePreferences(Preference preference, Object value) {
final Resources r = getResources();
// auto
if (r.getString(R.string.backup_BPr_auto_key).equals(
preference.getKey())) {
if (value != null && !(Boolean)value)
preference.setSummary(R.string.backup_BPr_auto_disabled);
else
preference.setSummary(R.string.backup_BPr_auto_enabled);
}
// status
else if (r.getString(R.string.backup_BPr_status_key).equals(preference.getKey())) {
String status;
String subtitle = ""; //$NON-NLS-1$
// last backup was error
final long last = Preferences.getLong(PREF_BACKUP_LAST_DATE, 0);
final String error = Preferences.getStringValue(PREF_BACKUP_LAST_ERROR);
if(error != null) {
status = r.getString(R.string.backup_status_failed);
subtitle = r.getString(R.string.backup_status_failed_subtitle);
statusColor = Color.rgb(100, 0, 0);
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
dialogUtilities.okDialog(BackupPreferences.this, error, null);
return true;
}
});
} else if(last > 0) {
status = r.getString(R.string.backup_status_success,
DateUtilities.getDateWithTimeFormat(BackupPreferences.this).
format(new Date(last)));
statusColor = Color.rgb(0, 100, 0);
preference.setOnPreferenceClickListener(null);
} else {
status = r.getString(R.string.backup_status_never);
statusColor = Color.rgb(0, 0, 100);
preference.setOnPreferenceClickListener(null);
}
preference.setTitle(status);
preference.setSummary(subtitle);
View view = findViewById(R.id.status);
if(view != null)
view.setBackgroundColor(statusColor);
}
}
}

@ -13,11 +13,16 @@ import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import com.timsu.astrid.R;
import com.todoroo.astrid.utility.Preferences;
/** /**
* Inspired heavily by SynchronizationService * Inspired heavily by SynchronizationService
*/ */
public class BackupService extends Service { public class BackupService extends Service {
// --- constants for backup
/** /**
* when after phone starts to start first back up * when after phone starts to start first back up
*/ */
@ -27,13 +32,10 @@ public class BackupService extends Service {
* how often to back up * how often to back up
*/ */
private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_DAY; private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_DAY;
public static final String BACKUP_ACTION = "backup"; public static final String BACKUP_ACTION = "backup"; //$NON-NLS-1$
public static final String BACKUP_FILE_NAME_REGEX = "auto\\.[-\\d]+\\.xml"; public static final String BACKUP_FILE_NAME_REGEX = "auto\\.[-\\d]+\\.xml"; //$NON-NLS-1$
private static final int DAYS_TO_KEEP_BACKUP = 7; private static final int DAYS_TO_KEEP_BACKUP = 7;
static final String PREF_BACKUP_LAST_ERROR = "backupError";
static final String PREF_BACKUP_LAST_DATE = "backupDate";
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return null; return null;
@ -48,66 +50,56 @@ public class BackupService extends Service {
/** /**
* Test hook for backup * Test hook for backup
* @param ctx * @param context
*/ */
public void testBackup(Context ctx) { public void testBackup(Context context) {
startBackup(ctx); startBackup(context);
} }
private void startBackup(Context ctx) { private void startBackup(Context context) {
/*if (ctx == null || ctx.getResources() == null) { if (context == null || context.getResources() == null) {
return; return;
} }
try { try {
if (!Preferences.isBackupEnabled(ctx)) { if (!Preferences.getBoolean(R.string.backup_BPr_auto_key, true)) {
return; return;
} }
try { try {
deleteOldBackups(); deleteOldBackups();
} catch (Exception e) { } catch (Exception e) {
Log.e("error-deleting", "Error deleting old backups: " + e); Log.e("error-deleting", "Error deleting old backups", e); //$NON-NLS-1$ //$NON-NLS-2$
} }
TasksXmlExporter exporter = new TasksXmlExporter(true);
exporter.setContext(ctx); TasksXmlExporter.exportTasks(context, true, null);
exporter.exportTasks(backupDirectorySetting.getBackupDirectory());
Preferences.setBackupSummary(ctx,
ctx.getString(R.string.BPr_backup_desc_success,
BackupDateUtilities.getFormattedDate(ctx, new Date())));
} catch (Exception e) { } catch (Exception e) {
// unable to backup. Log.e("error-backup", "Error starting backups", e); //$NON-NLS-1$ //$NON-NLS-2$
if (e == null || e.getMessage() == null) { Preferences.setString(BackupPreferences.PREF_BACKUP_LAST_ERROR, e.toString());
Preferences.setBackupSummary(ctx, }
ctx.getString(R.string.BPr_backup_desc_failure_null));
} else {
Preferences.setBackupSummary(ctx,
ctx.getString(R.string.BPr_backup_desc_failure,
e.toString()));
}
}*/
} }
public static void scheduleService(Context ctx) { public static void scheduleService(Context context) {
/*AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(ctx, 0, PendingIntent pendingIntent = PendingIntent.getService(context, 0,
createAlarmIntent(ctx), PendingIntent.FLAG_UPDATE_CURRENT); createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pendingIntent); am.cancel(pendingIntent);
if (!Preferences.isBackupEnabled(ctx)) { if (!Preferences.getBoolean(R.string.backup_BPr_auto_key, true)) {
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 context) {
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(ctx, 0, PendingIntent pendingIntent = PendingIntent.getService(context, 0,
createAlarmIntent(ctx), PendingIntent.FLAG_UPDATE_CURRENT); createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pendingIntent); am.cancel(pendingIntent);
} }
private static Intent createAlarmIntent(Context ctx) { private static Intent createAlarmIntent(Context context) {
Intent intent = new Intent(ctx, BackupService.class); Intent intent = new Intent(context, BackupService.class);
intent.setAction(BACKUP_ACTION); intent.setAction(BACKUP_ACTION);
return intent; return intent;
} }

@ -0,0 +1,26 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.backup;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.astrid.service.AstridDependencyInjector;
public class BackupStartupReceiver extends BroadcastReceiver {
static {
AstridDependencyInjector.initialize();
}
@Override
/** Called when device is restarted */
public void onReceive(final Context context, Intent intent) {
ContextManager.setContext(context);
BackupService.scheduleService(context);
}
}

@ -91,8 +91,9 @@ public class TasksXmlExporter {
String output = setupFile(BackupConstants.getExportDirectory(), String output = setupFile(BackupConstants.getExportDirectory(),
isService); isService);
doTasksExport(output); doTasksExport(output);
Preferences.setLong(BackupService.PREF_BACKUP_LAST_DATE, Preferences.setLong(BackupPreferences.PREF_BACKUP_LAST_DATE,
DateUtilities.now()); DateUtilities.now());
Preferences.setString(BackupPreferences.PREF_BACKUP_LAST_ERROR, null);
if (!isService) if (!isService)
displayToast(output); displayToast(output);
@ -102,7 +103,7 @@ public class TasksXmlExporter {
context.getString(R.string.backup_TXI_error), e); context.getString(R.string.backup_TXI_error), e);
else { else {
exceptionService.reportError("background-backup", e); //$NON-NLS-1$ exceptionService.reportError("background-backup", e); //$NON-NLS-1$
Preferences.setString(BackupService.PREF_BACKUP_LAST_ERROR, e.toString()); Preferences.setString(BackupPreferences.PREF_BACKUP_LAST_ERROR, e.toString());
} }
} finally { } finally {
if(runAfterExport != null) if(runAfterExport != null)

@ -154,6 +154,8 @@ public final class LocaleEditAlerts extends ExpandableListActivity {
adapter.setSelection(item); adapter.setSelection(item);
} else if(item instanceof FilterCategory) { } else if(item instanceof FilterCategory) {
Filter[] filters = ((FilterCategory)item).children; Filter[] filters = ((FilterCategory)item).children;
if(filters == null)
return;
for(Filter filter : filters) for(Filter filter : filters)
if(finalSelection.equals(filter.sqlQuery)) { if(finalSelection.equals(filter.sqlQuery)) {
adapter.setSelection(filter); adapter.setSelection(filter);
@ -243,7 +245,7 @@ public final class LocaleEditAlerts extends ExpandableListActivity {
/* /*
* This is the blurb concisely describing what your setting's state is. This is simply used for display in the UI. * 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) if (filterItem.title != null && 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)); returnIntent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BLURB, filterItem.title.substring(0, com.twofortyfouram.Intent.MAXIMUM_BLURB_LENGTH));
else else
returnIntent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BLURB, filterItem.title); returnIntent.putExtra(com.twofortyfouram.Intent.EXTRA_STRING_BLURB, filterItem.title);

@ -20,6 +20,37 @@
<!-- backup success message (%s -> date) --> <!-- backup success message (%s -> date) -->
<string name="backup_BPr_backup_desc_success">Latest backup was on %s</string> <string name="backup_BPr_backup_desc_success">Latest backup was on %s</string>
<!-- ================================================= BackupPreferences == -->
<!-- Preferences Title -->
<string name="backup_BPr_header">Backups</string>
<!-- Status Group Label -->
<string name="backup_BPr_group_status">Status</string>
<!-- Preference Key (do not translate) -->
<string name="backup_BPr_status_key">backup_status</string>
<!-- Status: success (%s -> last date) -->
<string name="backup_status_success">Latest: %s</string>
<!-- Status: error -->
<string name="backup_status_failed">Last Backup Failed</string>
<!-- Status: error subtitle -->
<string name="backup_status_failed_subtitle">(tap to show error)</string>
<!-- Status: never backed up -->
<string name="backup_status_never">Never Backed Up!</string>
<!-- Options Group Label -->
<string name="backup_BPr_group_options">Options</string>
<!-- Synchronization Interval Title -->
<string name="backup_BPr_auto_title">Automatic Backups</string>
<!-- Synchronization Interval Description (when disabled) -->
<string name="backup_BPr_auto_disabled">Automatic Backups Disabled</string>
<!-- Synchronization Interval Description (when enabled) -->
<string name="backup_BPr_auto_enabled">Backup will occur daily</string>
<!-- Preference Key (do not translate) -->
<string name="backup_BPr_auto_key">backup</string>
<!-- ================================================= BackupActivity == --> <!-- ================================================= BackupActivity == -->
<!-- backup activity label --> <!-- backup activity label -->

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/backup_BPr_group_status">
<Preference
android:layout="@layout/status_preference"
android:key="@string/backup_BPr_status_key"
android:textSize="24sp"
android:gravity="center"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/backup_BPr_group_options">
<CheckBoxPreference
android:key="@string/backup_BPr_auto_key"
android:title="@string/backup_BPr_auto_title"
android:defaultValue="true" />
</PreferenceCategory>
</PreferenceScreen>

@ -6,7 +6,7 @@
android:title="@string/rmilk_MPr_group_status"> android:title="@string/rmilk_MPr_group_status">
<Preference <Preference
android:layout="@layout/rmilk_status_preference" android:layout="@layout/status_preference"
android:key="@string/rmilk_MPr_status_key" android:key="@string/rmilk_MPr_status_key"
android:textSize="24sp" android:textSize="24sp"
android:gravity="center"/> android:gravity="center"/>

@ -336,6 +336,8 @@ public class FilterListActivity extends ExpandableListActivity {
FrameLayout frameLayout = new FrameLayout(this); FrameLayout frameLayout = new FrameLayout(this);
frameLayout.setPadding(10, 0, 10, 0); frameLayout.setPadding(10, 0, 10, 0);
final EditText editText = new EditText(this); final EditText editText = new EditText(this);
if(filter.listingTitle == null)
filter.listingTitle = ""; //$NON-NLS-1$
editText.setText(filter.listingTitle. editText.setText(filter.listingTitle.
replaceAll("\\(\\d+\\)$", "").trim()); //$NON-NLS-1$ //$NON-NLS-2$ replaceAll("\\(\\d+\\)$", "").trim()); //$NON-NLS-1$ //$NON-NLS-2$
frameLayout.addView(editText, new FrameLayout.LayoutParams( frameLayout.addView(editText, new FrameLayout.LayoutParams(

@ -596,6 +596,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
} }
// create a custom cursor // create a custom cursor
if(filter.sqlQuery == null)
filter.sqlQuery = "";
if(!filter.sqlQuery.contains("WHERE")) if(!filter.sqlQuery.contains("WHERE"))
filter.sqlQuery += " WHERE " + TaskCriteria.byId(withCustomId); filter.sqlQuery += " WHERE " + TaskCriteria.byId(withCustomId);
else else

@ -110,7 +110,7 @@ public class FilterAdapter extends BaseExpandableListAdapter {
public Object getChild(int groupPosition, int childPosition) { public Object getChild(int groupPosition, int childPosition) {
FilterListItem item = items.get(groupPosition); FilterListItem item = items.get(groupPosition);
if(!(item instanceof FilterCategory)) if(!(item instanceof FilterCategory) || ((FilterCategory)item).children == null)
return null; return null;
return ((FilterCategory)item).children[childPosition]; return ((FilterCategory)item).children[childPosition];

Loading…
Cancel
Save