diff --git a/art/scroll_arrows.xcf b/art/scroll_arrows.xcf
new file mode 100644
index 000000000..d764f631c
Binary files /dev/null and b/art/scroll_arrows.xcf differ
diff --git a/art/speech_bubble.xcf b/art/speech_bubble.xcf
new file mode 100644
index 000000000..406c38661
Binary files /dev/null and b/art/speech_bubble.xcf differ
diff --git a/art/widget-4x4.xcf b/art/widget-4x4.xcf
new file mode 100644
index 000000000..61c50ebe9
Binary files /dev/null and b/art/widget-4x4.xcf differ
diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index c4e9549b4..78ec5bf91 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -124,6 +124,12 @@
+
+
+
+
+
+
@@ -136,6 +142,15 @@
+
+
+
+
+
+
@@ -146,6 +161,7 @@
+
diff --git a/astrid/astrid.launch b/astrid/astrid.launch
index 8df8ec564..3f96633e8 100644
--- a/astrid/astrid.launch
+++ b/astrid/astrid.launch
@@ -7,7 +7,6 @@
-
diff --git a/astrid/res/drawable-hdpi/checkbox.png b/astrid/res/drawable-hdpi/checkbox.png
new file mode 100644
index 000000000..0896428ab
Binary files /dev/null and b/astrid/res/drawable-hdpi/checkbox.png differ
diff --git a/astrid/res/drawable-mdpi/checkbox.png b/astrid/res/drawable-mdpi/checkbox.png
new file mode 100644
index 000000000..7a7496e33
Binary files /dev/null and b/astrid/res/drawable-mdpi/checkbox.png differ
diff --git a/astrid/res/drawable-mdpi/importance_1.png b/astrid/res/drawable-mdpi/importance_1.png
new file mode 100644
index 000000000..a00c2384a
Binary files /dev/null and b/astrid/res/drawable-mdpi/importance_1.png differ
diff --git a/astrid/res/drawable-mdpi/importance_2.png b/astrid/res/drawable-mdpi/importance_2.png
new file mode 100644
index 000000000..76e6071f6
Binary files /dev/null and b/astrid/res/drawable-mdpi/importance_2.png differ
diff --git a/astrid/res/drawable-mdpi/importance_3.png b/astrid/res/drawable-mdpi/importance_3.png
new file mode 100644
index 000000000..94923810d
Binary files /dev/null and b/astrid/res/drawable-mdpi/importance_3.png differ
diff --git a/astrid/res/drawable-mdpi/importance_4.png b/astrid/res/drawable-mdpi/importance_4.png
new file mode 100644
index 000000000..b8e9ccc72
Binary files /dev/null and b/astrid/res/drawable-mdpi/importance_4.png differ
diff --git a/astrid/res/drawable-mdpi/importance_5.png b/astrid/res/drawable-mdpi/importance_5.png
new file mode 100644
index 000000000..740521ab6
Binary files /dev/null and b/astrid/res/drawable-mdpi/importance_5.png differ
diff --git a/astrid/res/drawable-mdpi/importance_6.png b/astrid/res/drawable-mdpi/importance_6.png
new file mode 100644
index 000000000..894670918
Binary files /dev/null and b/astrid/res/drawable-mdpi/importance_6.png differ
diff --git a/astrid/res/drawable-mdpi/scroll_down.png b/astrid/res/drawable-mdpi/scroll_down.png
new file mode 100644
index 000000000..ae728580f
Binary files /dev/null and b/astrid/res/drawable-mdpi/scroll_down.png differ
diff --git a/astrid/res/drawable-mdpi/scroll_down_disabled.png b/astrid/res/drawable-mdpi/scroll_down_disabled.png
new file mode 100644
index 000000000..92fe7303b
Binary files /dev/null and b/astrid/res/drawable-mdpi/scroll_down_disabled.png differ
diff --git a/astrid/res/drawable-mdpi/scroll_up.png b/astrid/res/drawable-mdpi/scroll_up.png
new file mode 100644
index 000000000..8b4b67be1
Binary files /dev/null and b/astrid/res/drawable-mdpi/scroll_up.png differ
diff --git a/astrid/res/drawable-mdpi/scroll_up_disabled.png b/astrid/res/drawable-mdpi/scroll_up_disabled.png
new file mode 100644
index 000000000..f4db7df3c
Binary files /dev/null and b/astrid/res/drawable-mdpi/scroll_up_disabled.png differ
diff --git a/astrid/res/drawable-mdpi/speech_bubble.9.png b/astrid/res/drawable-mdpi/speech_bubble.9.png
new file mode 100644
index 000000000..9ed7badab
Binary files /dev/null and b/astrid/res/drawable-mdpi/speech_bubble.9.png differ
diff --git a/astrid/res/drawable-mdpi/widget_bg_44_black.9.png b/astrid/res/drawable-mdpi/widget_bg_44_black.9.png
new file mode 100644
index 000000000..1998b933f
Binary files /dev/null and b/astrid/res/drawable-mdpi/widget_bg_44_black.9.png differ
diff --git a/astrid/res/drawable-mdpi/widget_bg_44_blue.9.png b/astrid/res/drawable-mdpi/widget_bg_44_blue.9.png
new file mode 100644
index 000000000..811075a96
Binary files /dev/null and b/astrid/res/drawable-mdpi/widget_bg_44_blue.9.png differ
diff --git a/astrid/res/drawable-mdpi/widget_bg_44_red.9.png b/astrid/res/drawable-mdpi/widget_bg_44_red.9.png
new file mode 100644
index 000000000..a4b598bd7
Binary files /dev/null and b/astrid/res/drawable-mdpi/widget_bg_44_red.9.png differ
diff --git a/astrid/res/drawable-mdpi/widget_bg_44_white.9.png b/astrid/res/drawable-mdpi/widget_bg_44_white.9.png
new file mode 100644
index 000000000..0f391d9e1
Binary files /dev/null and b/astrid/res/drawable-mdpi/widget_bg_44_white.9.png differ
diff --git a/astrid/res/drawable-mdpi/widget_task_bg_black.9.png b/astrid/res/drawable-mdpi/widget_task_bg_black.9.png
new file mode 100644
index 000000000..85c90843f
Binary files /dev/null and b/astrid/res/drawable-mdpi/widget_task_bg_black.9.png differ
diff --git a/astrid/res/drawable/icon_plus.png b/astrid/res/drawable/icon_plus.png
new file mode 100644
index 000000000..fa6bfd062
Binary files /dev/null and b/astrid/res/drawable/icon_plus.png differ
diff --git a/astrid/res/layout-land/widget_power_42.xml b/astrid/res/layout-land/widget_power_42.xml
new file mode 100644
index 000000000..1168b0cab
--- /dev/null
+++ b/astrid/res/layout-land/widget_power_42.xml
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/layout-land/widget_power_44.xml b/astrid/res/layout-land/widget_power_44.xml
new file mode 100644
index 000000000..47e2be52d
--- /dev/null
+++ b/astrid/res/layout-land/widget_power_44.xml
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/layout/power_widget_configure.xml b/astrid/res/layout/power_widget_configure.xml
new file mode 100644
index 000000000..eb3c23a2c
--- /dev/null
+++ b/astrid/res/layout/power_widget_configure.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/layout/widget_power_42.xml b/astrid/res/layout/widget_power_42.xml
new file mode 100644
index 000000000..74d2a663e
--- /dev/null
+++ b/astrid/res/layout/widget_power_42.xml
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/layout/widget_power_44.xml b/astrid/res/layout/widget_power_44.xml
new file mode 100644
index 000000000..782e1692c
--- /dev/null
+++ b/astrid/res/layout/widget_power_44.xml
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/layout/widget_power_task.xml b/astrid/res/layout/widget_power_task.xml
new file mode 100644
index 000000000..a40ed0f82
--- /dev/null
+++ b/astrid/res/layout/widget_power_task.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/values/strings-widget.xml b/astrid/res/values/strings-widget.xml
new file mode 100644
index 000000000..07fe5857b
--- /dev/null
+++ b/astrid/res/values/strings-widget.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ Astrid (Power Pack 4x2)
+ Astrid (Power Pack 4x4)
+
+ Configure Widget
+
+ Widget color
+ Show calendar events
+ Hide encouragements
+ Select Filter
+
+ Due:
+ Past Due:
+
+
+ - Ready to work?
+
+
diff --git a/astrid/res/xml/power_widget_42_provider_info.xml b/astrid/res/xml/power_widget_42_provider_info.xml
new file mode 100644
index 000000000..33202fa70
--- /dev/null
+++ b/astrid/res/xml/power_widget_42_provider_info.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/astrid/res/xml/power_widget_44_provider_info.xml b/astrid/res/xml/power_widget_44_provider_info.xml
new file mode 100644
index 000000000..686c2c78e
--- /dev/null
+++ b/astrid/res/xml/power_widget_44_provider_info.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java b/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java
index 456000fc8..6be91578d 100644
--- a/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java
@@ -119,6 +119,7 @@ public class ShortcutActivity extends Activity {
new QueryTemplate().where(Task.ID.eq(extras.getLong(TOKEN_SINGLE_TASK, -1))), null);
Intent taskListIntent = new Intent(this, TaskListActivity.class);
+ taskListIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
taskListIntent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
startActivity(taskListIntent);
}
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
index a7ebfde02..0f664961a 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -87,6 +87,7 @@ import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Flags;
import com.todoroo.astrid.utility.Preferences;
+import com.todoroo.astrid.widget.PowerWidget;
import com.todoroo.astrid.widget.TasksWidget;
/**
@@ -994,6 +995,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
Preferences.setInt(SortSelectionActivity.PREF_SORT_SORT, sort);
ContextManager.getContext().startService(new Intent(ContextManager.getContext(),
TasksWidget.UpdateService.class));
+ ContextManager.getContext().startService(new Intent(ContextManager.getContext(),
+ PowerWidget.UpdateService.class));
}
setUpTaskList();
diff --git a/astrid/src/com/todoroo/astrid/dao/TaskDao.java b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
index c1dbdd5c6..efbad7180 100644
--- a/astrid/src/com/todoroo/astrid/dao/TaskDao.java
+++ b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
@@ -24,6 +24,7 @@ import com.todoroo.astrid.provider.Astrid2TaskProvider;
import com.todoroo.astrid.reminders.Notifications;
import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.utility.Preferences;
+import com.todoroo.astrid.widget.PowerWidget;
import com.todoroo.astrid.widget.TasksWidget;
/**
@@ -236,6 +237,7 @@ public class TaskDao extends DatabaseDao {
Astrid2TaskProvider.notifyDatabaseModification();
TasksWidget.updateWidgets(ContextManager.getContext());
+ PowerWidget.updateWidgets(ContextManager.getContext());
}
/**
diff --git a/astrid/src/com/todoroo/astrid/widget/ConfigurePowerWidgetActivity.java b/astrid/src/com/todoroo/astrid/widget/ConfigurePowerWidgetActivity.java
new file mode 100644
index 000000000..aecdb6d68
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/widget/ConfigurePowerWidgetActivity.java
@@ -0,0 +1,200 @@
+package com.todoroo.astrid.widget;
+
+import android.app.ExpandableListActivity;
+import android.appwidget.AppWidgetManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ExpandableListView;
+import android.widget.Spinner;
+
+import com.flurry.android.FlurryAgent;
+import com.timsu.astrid.R;
+import com.todoroo.andlib.utility.AndroidUtilities;
+import com.todoroo.astrid.adapter.FilterAdapter;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.FilterCategory;
+import com.todoroo.astrid.api.FilterListItem;
+import com.todoroo.astrid.utility.Constants;
+import com.todoroo.astrid.utility.Preferences;
+
+/**
+ * Configure options for the Power Pack widget. Select a color, filter to use, enable/disable encouragements.
+ *
+ * @author jwong
+ *
+ */
+public class ConfigurePowerWidgetActivity extends ExpandableListActivity {
+
+
+ int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ FilterAdapter adapter = null;
+
+ String[] colors = new String[]{
+ "Black",
+ "Blue",
+ "Red",
+ "White"
+ };
+
+ public ConfigurePowerWidgetActivity() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Set the result to CANCELED. This will cause the widget host to cancel
+ // out of the widget placement if they press the back button.
+ setResult(RESULT_CANCELED);
+
+ // Set the view layout resource to use.
+ setContentView(R.layout.power_widget_configure);
+
+ setTitle(R.string.PPW_configure_title);
+
+ // Find the widget id from the intent.
+ Intent intent = getIntent();
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ mAppWidgetId = extras.getInt(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ // If they gave us an intent without the widget id, just bail.
+ if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ }
+
+ ArrayAdapter colorAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, colors);
+ colorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ Spinner colorSpinner = (Spinner) findViewById(R.id.PPW_color);
+ colorSpinner.setAdapter(colorAdapter);
+
+ // set up ui
+ adapter = new FilterAdapter(this, getExpandableListView(),
+ R.layout.filter_adapter_row, true);
+ setListAdapter(adapter);
+
+ Button button = (Button)findViewById(R.id.ok);
+ button.setOnClickListener(mOnClickListener);
+
+ FlurryAgent.onEvent("power-widget-config"); //$NON-NLS-1$
+ }
+
+ View.OnClickListener mOnClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ final Context context = ConfigurePowerWidgetActivity.this;
+
+
+ Spinner colorSpinner = (Spinner) findViewById(R.id.PPW_color);
+ int colorPos = colorSpinner.getSelectedItemPosition();
+ String color = colors[colorPos];
+
+ // removed calendar option
+// CheckBox chk_enableCalendar = (CheckBox) findViewById(R.id.PPW_enable_calendar);
+// boolean enableCalendar = chk_enableCalendar.isChecked();
+ boolean enableCalendar = false;
+
+ CheckBox chk_disableEncouragements = (CheckBox) findViewById(R.id.PPW_disable_encouragements);
+ boolean disableEncouragements = chk_disableEncouragements.isChecked();
+
+ // Save configuration options
+ saveConfiguration(adapter.getSelection(), color, enableCalendar, !disableEncouragements);
+
+ // Push widget update to surface with newly set prefix
+ PowerWidget.updateAppWidget(context, mAppWidgetId);
+
+ // Make sure we pass back the original appWidgetId
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_OK, resultValue);
+ finish();
+ }
+ };
+
+ @Override
+ public boolean onChildClick(ExpandableListView parent, View v,
+ int groupPosition, int childPosition, long id) {
+ FilterListItem item = (FilterListItem) adapter.getChild(groupPosition,
+ childPosition);
+ if(item instanceof Filter) {
+ adapter.setSelection(item);
+ }
+ return true;
+ }
+
+ @Override
+ public void onGroupExpand(int groupPosition) {
+ FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition);
+ if(item instanceof Filter)
+ adapter.setSelection(item);
+ else if(item instanceof FilterCategory)
+ adapter.saveExpansionSetting((FilterCategory) item, true);
+ }
+
+ @Override
+ public void onGroupCollapse(int groupPosition) {
+ FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition);
+ if(item instanceof Filter)
+ adapter.setSelection(item);
+ else if(item instanceof FilterCategory)
+ adapter.saveExpansionSetting((FilterCategory) item, false);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ adapter.registerRecevier();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ adapter.unregisterRecevier();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ FlurryAgent.onEndSession(this);
+ }
+
+ private void saveConfiguration(FilterListItem filterListItem, String color, boolean enableCalendar, boolean enableEncouragements){
+ DisplayMetrics metrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
+ String sql = null, contentValuesString = null, title = null;
+
+ if(filterListItem != null && filterListItem instanceof Filter) {
+ sql = ((Filter)filterListItem).sqlQuery;
+ ContentValues values = ((Filter)filterListItem).valuesForNewTasks;
+ if(values != null)
+ contentValuesString = AndroidUtilities.contentValuesToSerializedString(values);
+ title = ((Filter)filterListItem).title;
+ }
+
+ Preferences.setString(PowerWidget.PREF_TITLE + mAppWidgetId, title);
+ Preferences.setString(PowerWidget.PREF_SQL + mAppWidgetId, sql);
+ Preferences.setString(PowerWidget.PREF_VALUES + mAppWidgetId, contentValuesString);
+
+ Preferences.setString(PowerWidget.PREF_COLOR + mAppWidgetId, color);
+ Preferences.setBoolean(PowerWidget.PREF_ENABLE_CALENDAR + mAppWidgetId, enableCalendar);
+ Preferences.setBoolean(PowerWidget.PREF_ENCOURAGEMENTS + mAppWidgetId, enableEncouragements);
+ }
+}
diff --git a/astrid/src/com/todoroo/astrid/widget/PowerWidget.java b/astrid/src/com/todoroo/astrid/widget/PowerWidget.java
new file mode 100644
index 000000000..796942e54
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/widget/PowerWidget.java
@@ -0,0 +1,411 @@
+package com.todoroo.astrid.widget;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.IBinder;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.utility.AndroidUtilities;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.activity.ShortcutActivity;
+import com.todoroo.astrid.activity.SortSelectionActivity;
+import com.todoroo.astrid.activity.TaskEditActivity;
+import com.todoroo.astrid.activity.TaskListActivity;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.core.CoreFilterExposer;
+import com.todoroo.astrid.dao.Database;
+import com.todoroo.astrid.data.Task;
+import com.todoroo.astrid.service.AstridDependencyInjector;
+import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.utility.Preferences;
+
+/**
+ * Power Pack widget. Supports 4x4 size. Configured via
+ * ConfigurePowerWidgetActivity when widget is added to homescreen.
+ *
+ * @author jwong (jwong@dayspring-tech.com)
+ *
+ */
+@SuppressWarnings("nls")
+public class PowerWidget extends AppWidgetProvider {
+ static final String LOG_TAG = "PowerWidget";
+
+ static {
+ AstridDependencyInjector.initialize();
+ }
+
+
+ static final long ENCOURAGEMENT_CYCLE_TIME = 1000 * 60 * 60 * 4; // 4 hours
+
+ static final String ACTION_MARK_COMPLETE = "com.timsu.astrid.widget.ACTION_MARK_COMPLETE"; //$NON-NLS-1$
+ static final String ACTION_SCROLL_UP = "com.timsu.astrid.widget.ACTION_SCROLL_UP"; //$NON-NLS-1$
+ static final String ACTION_SCROLL_DOWN = "com.timsu.astrid.widget.ACTION_SCROLL_DOWN"; //$NON-NLS-1$
+
+ // Prefix for Shared Preferences
+ static final String PREF_COLOR = "powerwidget-color-"; //$NON-NLS-1$
+ static final String PREF_ENABLE_CALENDAR = "powerwidget-enableCalendar-"; //$NON-NLS-1$
+ static final String PREF_ENCOURAGEMENTS = "powerwidget-enableEncouragements-"; //$NON-NLS-1$
+ static final String PREF_TITLE = "powerwidget-title-"; //$NON-NLS-1$
+ static final String PREF_SQL = "powerwidget-sql-"; //$NON-NLS-1$
+ static final String PREF_VALUES = "powerwidget-values-"; //$NON-NLS-1$
+ static final String PREF_ENCOURAGEMENT_LAST_ROTATION_TIME = "powerwidget-encouragementRotationTime-"; //$NON-NLS-1$
+ static final String PREF_ENCOURAGEMENT_CURRENT = "powerwidget-encouragementCurrent-"; //$NON-NLS-1$
+
+ public final static String APP_WIDGET_IDS = "com.timsu.astrid.APP_WIDGET_IDS"; //$NON-NLS-1$
+ public final static String COMPLETED_TASK_ID = "com.timsu.astrid.COMPLETED_TASK_ID"; //$NON-NLS-1$
+ public final static String EXTRA_SCROLL_OFFSET = "com.timsu.astrid.EXTRA_SCROLL_OFFSET"; //$NON-NLS-1$
+
+ public final static int[] IMPORTANCE_DRAWABLES = { R.drawable.importance_1, R.drawable.importance_2, R.drawable.importance_3, R.drawable.importance_4, R.drawable.importance_5, R.drawable.importance_6 };
+
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+
+ try {
+ super.onUpdate(context, appWidgetManager, appWidgetIds);
+
+ // Start in service to prevent Application Not Responding timeout
+ Intent updateIntent = new Intent(context, UpdateService.class);
+ updateIntent.putExtra(APP_WIDGET_IDS, appWidgetIds);
+ context.startService(updateIntent);
+ } catch (SecurityException e) {
+ // :(
+ }
+ }
+
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_MARK_COMPLETE.equals(intent.getAction())){
+ long taskId = intent.getLongExtra(COMPLETED_TASK_ID, -1);
+
+ Intent updateIntent = new Intent(context, UpdateService.class);
+ updateIntent.putExtra(COMPLETED_TASK_ID, taskId);
+ context.startService(updateIntent);
+ } else if (ACTION_SCROLL_UP.equals(intent.getAction()) || ACTION_SCROLL_DOWN.equals(intent.getAction())){
+ int id = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
+ if (id != AppWidgetManager.INVALID_APPWIDGET_ID){
+ int scrollOffset = intent.getIntExtra(EXTRA_SCROLL_OFFSET, 0);
+ Intent updateIntent = new Intent(context, UpdateService.class);
+ updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id);
+ updateIntent.putExtra(EXTRA_SCROLL_OFFSET, scrollOffset);
+ context.startService(updateIntent);
+ }
+ } else {
+ super.onReceive(context, intent);
+ }
+ }
+
+
+
+ /**
+ * Update all widgets
+ * @param id
+ */
+ public static void updateWidgets(Context context) {
+ context.startService(new Intent(ContextManager.getContext(),
+ UpdateService.class));
+ }
+
+ /**
+ * Update widget with the given id
+ * @param id
+ */
+ public static void updateAppWidget(Context context, int appWidgetId){
+ Intent updateIntent = new Intent(context, UpdateService.class);
+ updateIntent.putExtra(APP_WIDGET_IDS, new int[]{ appWidgetId });
+ context.startService(updateIntent);
+ }
+
+ public static class UpdateService extends Service {
+
+ @Autowired
+ Database database;
+
+ @Autowired
+ TaskService taskService;
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ ContextManager.setContext(this);
+
+ if (intent != null){
+ long taskId = intent.getLongExtra(COMPLETED_TASK_ID, -1);
+ if (taskId > 0){
+ Task task = taskService.fetchById(taskId, Task.PROPERTIES);
+ if (task != null){
+ taskService.setComplete(task, true);
+ }
+ }
+ }
+
+ AppWidgetManager manager = AppWidgetManager.getInstance(this);
+
+ int scrollOffset = 0;
+ int[] appWidgetIds = null;
+ int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ if (intent != null){
+ scrollOffset = intent.getIntExtra(EXTRA_SCROLL_OFFSET, 0);
+ appWidgetIds = intent.getIntArrayExtra(APP_WIDGET_IDS);
+ appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){
+ RemoteViews views = buildUpdate(this, appWidgetId, scrollOffset);
+ manager.updateAppWidget(appWidgetId, views);
+ } else {
+ if (appWidgetIds == null){
+ appWidgetIds = manager.getAppWidgetIds(new ComponentName(this, PowerWidget.class));
+ }
+ for (int id : appWidgetIds) {
+ RemoteViews views = buildUpdate(this, id, scrollOffset);
+ manager.updateAppWidget(id, views);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ public RemoteViews buildUpdate(Context context, int appWidgetId, int scrollOffset) {
+ DependencyInjectionService.getInstance().inject(this);
+
+ RemoteViews views = new RemoteViews(context.getPackageName(),
+ R.layout.widget_power_44);
+
+ String color = Preferences.getStringValue(PowerWidget.PREF_COLOR + appWidgetId);
+
+ int widgetBackground = R.id.widget_bg_black;
+ int textColor = Color.WHITE, overdueColor = context.getResources().getColor(R.color.task_list_overdue);
+ if ("Black".equals(color)){
+ widgetBackground = R.id.widget_bg_black;
+ textColor = Color.WHITE;
+ overdueColor = context.getResources().getColor(R.color.task_list_overdue);
+ } else if ("Blue".equals(color)){
+ widgetBackground = R.id.widget_bg_blue;
+ textColor = Color.WHITE;
+ overdueColor = context.getResources().getColor(R.color.task_list_overdue);
+ } else if ("Red".equals(color)){
+ widgetBackground = R.id.widget_bg_red;
+ textColor = Color.WHITE;
+ overdueColor = context.getResources().getColor(R.color.task_list_overdue);
+ } else if ("White".equals(color)){
+ widgetBackground = R.id.widget_bg_white;
+ textColor = Color.BLACK;
+ overdueColor = context.getResources().getColor(R.color.task_list_overdue);
+ } else {
+ widgetBackground = R.id.widget_bg_black;
+ textColor = Color.WHITE;
+ overdueColor = context.getResources().getColor(R.color.task_list_overdue);
+ }
+ views.setViewVisibility(widgetBackground, View.VISIBLE);
+
+ // set encouragement
+ boolean showEncouragements = Preferences.getBoolean(PowerWidget.PREF_ENCOURAGEMENTS + appWidgetId, true);
+ long lastRotation = Preferences.getLong(PowerWidget.PREF_ENCOURAGEMENT_LAST_ROTATION_TIME + appWidgetId, 0);
+ if (showEncouragements){
+ // is it time to update the encouragement?
+ if (System.currentTimeMillis() - lastRotation > ENCOURAGEMENT_CYCLE_TIME){
+ String[] encouragements = context.getResources().getStringArray(R.array.PPW_encouragements);
+ int encouragementIdx = (int)Math.floor(Math.random() * encouragements.length);
+ Preferences.setString(PowerWidget.PREF_ENCOURAGEMENT_CURRENT + appWidgetId, encouragements[encouragementIdx]);
+ Preferences.setLong(PowerWidget.PREF_ENCOURAGEMENT_LAST_ROTATION_TIME + appWidgetId, System.currentTimeMillis());
+ }
+ views.setTextViewText(R.id.encouragement_text, Preferences.getStringValue(PowerWidget.PREF_ENCOURAGEMENT_CURRENT + appWidgetId));
+ views.setViewVisibility(R.id.speech_bubble, View.VISIBLE);
+ views.setViewVisibility(R.id.icon, View.VISIBLE);
+ } else {
+ views.setViewVisibility(R.id.speech_bubble, View.GONE);
+ views.setViewVisibility(R.id.icon, View.GONE);
+ }
+
+ // clear task list before adding
+ views.removeAllViews(R.id.task_list);
+
+ TodorooCursor cursor = null;
+ Filter filter = null;
+ try {
+ filter = getFilter(appWidgetId);
+ views.setTextViewText(R.id.widget_title, filter.title);
+ views.setTextColor(R.id.widget_title, textColor);
+
+ // create intent to add a new task
+ Intent editIntent = new Intent(context, TaskEditActivity.class);
+ editIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ if (filter.valuesForNewTasks != null) {
+ String values = AndroidUtilities.contentValuesToSerializedString(filter.valuesForNewTasks);
+ editIntent.putExtra(TaskEditActivity.TOKEN_VALUES, values);
+ editIntent.setType(values);
+ } else {
+ editIntent.setType("createNew");
+ }
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, editIntent, 0);
+ if (showEncouragements){
+ // if encouragements are showing, use icon as plus
+ views.setViewVisibility(R.id.button_plus, View.GONE);
+ views.setOnClickPendingIntent(R.id.icon, pendingIntent);
+ } else {
+ views.setViewVisibility(R.id.button_plus, View.VISIBLE);
+ views.setOnClickPendingIntent(R.id.button_plus, pendingIntent);
+ }
+
+ Intent listIntent = new Intent(context, TaskListActivity.class);
+ listIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ listIntent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
+ listIntent.setType(filter.sqlQuery);
+ PendingIntent pListIntent = PendingIntent.getActivity(context, 0,
+ listIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ views.setOnClickPendingIntent(R.id.widget_title, pListIntent);
+
+ int flags = Preferences.getInt(SortSelectionActivity.PREF_SORT_FLAGS, 0);
+ int sort = Preferences.getInt(SortSelectionActivity.PREF_SORT_SORT, 0);
+ String query = SortSelectionActivity.adjustQueryForFlagsAndSort(
+ filter.sqlQuery, flags, sort);
+
+ database.openForReading();
+ cursor = taskService.fetchFiltered(query, null, Task.ID, Task.TITLE, Task.DUE_DATE, Task.IMPORTANCE);
+
+ // adjust bounds of scrolling
+ if (scrollOffset < 0){
+ scrollOffset = 0;
+ } else if (scrollOffset >= cursor.getCount()){
+ scrollOffset = cursor.getCount() - 1 ;
+ }
+
+ Task task = new Task();
+ for (int i = scrollOffset; i < cursor.getCount(); i++) {
+ cursor.moveToPosition(i);
+ task.readFromCursor(cursor);
+
+ String textContent = "";
+ int titleColor = textColor;
+ int dueString = R.string.PPW_due;
+ if(task.hasDueDate() && task.getValue(Task.DUE_DATE) < DateUtilities.now()){
+ titleColor = overdueColor;
+ dueString = R.string.PPW_past_due;
+ }
+
+ textContent = task.getValue(Task.TITLE);
+
+ String dateValue = "";
+ if(dueString != 0 && task.hasDueDate()) {
+ dateValue = getString(dueString) + "\n" +
+ DateUtils.getRelativeTimeSpanString(task.getValue(Task.DUE_DATE));
+ }
+
+ long taskId = task.getValue(Task.ID);
+
+ Intent viewTaskIntent = new Intent(context, ShortcutActivity.class);
+ viewTaskIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ viewTaskIntent.putExtra(ShortcutActivity.TOKEN_SINGLE_TASK, taskId);
+ viewTaskIntent.setType(ShortcutActivity.TOKEN_SINGLE_TASK + taskId);
+ Log.d(LOG_TAG, "viewTaskIntent type: "+ShortcutActivity.TOKEN_SINGLE_TASK + taskId);
+ PendingIntent pEditTask = PendingIntent.getActivity(context, 0, viewTaskIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+ Intent markCompleteIntent = new Intent(context, PowerWidget.class);
+ markCompleteIntent.setAction(ACTION_MARK_COMPLETE);
+ markCompleteIntent.putExtra(COMPLETED_TASK_ID, taskId);
+ markCompleteIntent.setType(COMPLETED_TASK_ID + taskId);
+ PendingIntent pMarkCompleteIntent = PendingIntent.getBroadcast(context, 0, markCompleteIntent, 0);
+
+ // create a new task view
+ RemoteViews taskLine = new RemoteViews(context.getPackageName(), R.layout.widget_power_task);
+ // set importance marker
+ taskLine.setImageViewResource(R.id.importance, IMPORTANCE_DRAWABLES[task.getValue(Task.IMPORTANCE)]);
+ // set click listener for checkbox
+ taskLine.setOnClickPendingIntent(R.id.checkbox, pMarkCompleteIntent);
+ // set task title
+ taskLine.setTextViewText(R.id.task_title, textContent);
+ taskLine.setTextColor(R.id.task_title, titleColor);
+ // set due date
+ taskLine.setTextViewText(R.id.task_due, dateValue);
+ taskLine.setTextColor(R.id.task_due, titleColor);
+ // set click listener for text content
+ taskLine.setOnClickPendingIntent(R.id.task_edit_region, pEditTask);
+
+ // add this task to the widget's task list
+ views.addView(R.id.task_list, taskLine);
+ }
+
+
+ // create intent to scroll up
+ Intent scrollUpIntent = new Intent(context, PowerWidget.class);
+ scrollUpIntent.setAction(ACTION_SCROLL_UP);
+ scrollUpIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ scrollUpIntent.putExtra(EXTRA_SCROLL_OFFSET, scrollOffset-1);
+ scrollUpIntent.setType(AppWidgetManager.EXTRA_APPWIDGET_ID + appWidgetId);
+ PendingIntent pScrollUpIntent = PendingIntent.getBroadcast(context, 0, scrollUpIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ views.setOnClickPendingIntent(R.id.scroll_up, pScrollUpIntent);
+ if (scrollOffset-1 < 0){
+ // show disabled up button
+ views.setImageViewResource(R.id.scroll_up, R.drawable.scroll_up_disabled);
+ } else {
+ views.setImageViewResource(R.id.scroll_up, R.drawable.scroll_up);
+ }
+
+ // create intent to scroll down
+ Intent scrollDownIntent = new Intent(context, PowerWidget.class);
+ scrollDownIntent.setAction(ACTION_SCROLL_DOWN);
+ scrollDownIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ scrollDownIntent.putExtra(EXTRA_SCROLL_OFFSET, scrollOffset+1);
+ scrollDownIntent.setType(AppWidgetManager.EXTRA_APPWIDGET_ID + appWidgetId);
+ PendingIntent pScrollDownIntent = PendingIntent.getBroadcast(context, 0, scrollDownIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ views.setOnClickPendingIntent(R.id.scroll_down, pScrollDownIntent);
+ if (scrollOffset+1 >= cursor.getCount()){
+ // show disabled down button
+ views.setImageViewResource(R.id.scroll_down, R.drawable.scroll_down_disabled);
+ } else {
+ views.setImageViewResource(R.id.scroll_down, R.drawable.scroll_down);
+ }
+
+
+ } catch (Exception e) {
+ // can happen if database is not ready
+ Log.e("WIDGET-UPDATE", "Error updating widget", e);
+ } finally {
+ if(cursor != null)
+ cursor.close();
+ }
+
+
+
+ return views;
+ }
+
+ private Filter getFilter(int widgetId) {
+ // base our filter off the inbox filter, replace stuff if we have it
+ Filter filter = CoreFilterExposer.buildInboxFilter(getResources());
+ String sql = Preferences.getStringValue(PREF_SQL + widgetId);
+ if(sql != null)
+ filter.sqlQuery = sql;
+ String title = Preferences.getStringValue(PREF_TITLE + widgetId);
+ if(title != null)
+ filter.title = title;
+ String contentValues = Preferences.getStringValue(PREF_VALUES + widgetId);
+ if(contentValues != null)
+ filter.valuesForNewTasks = AndroidUtilities.contentValuesFromSerializedString(contentValues);
+
+ return filter;
+ }
+
+ }
+
+}