mirror of https://github.com/tasks/tasks
parent
a1c986d9e2
commit
2ad175c876
@ -0,0 +1,218 @@
|
|||||||
|
package com.todoroo.astrid.widget;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
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.Preferences;
|
||||||
|
import com.todoroo.astrid.actfm.TagViewFragment;
|
||||||
|
import com.todoroo.astrid.api.Filter;
|
||||||
|
import com.todoroo.astrid.api.FilterWithCustomIntent;
|
||||||
|
import com.todoroo.astrid.core.SortHelper;
|
||||||
|
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.service.ThemeService;
|
||||||
|
import com.todoroo.astrid.subtasks.SubtasksHelper;
|
||||||
|
import com.todoroo.astrid.utility.AstridPreferences;
|
||||||
|
import com.todoroo.astrid.utility.Constants;
|
||||||
|
|
||||||
|
import org.tasks.R;
|
||||||
|
import org.tasks.widget.WidgetHelper;
|
||||||
|
|
||||||
|
public class WidgetUpdateService extends Service {
|
||||||
|
|
||||||
|
static {
|
||||||
|
AstridDependencyInjector.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int NUM_VISIBLE_TASKS = 25;
|
||||||
|
|
||||||
|
public static final String EXTRA_WIDGET_ID = "widget_id"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
Database database;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
TaskService taskService;
|
||||||
|
|
||||||
|
private final WidgetHelper widgetHelper = new WidgetHelper();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart(final Intent intent, int startId) {
|
||||||
|
ContextManager.setContext(this);
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
startServiceInBackgroundThread(intent);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startServiceInBackgroundThread(Intent intent) {
|
||||||
|
ComponentName thisWidget = new ComponentName(this,
|
||||||
|
TasksWidget.class);
|
||||||
|
AppWidgetManager manager = AppWidgetManager.getInstance(this);
|
||||||
|
|
||||||
|
int extrasId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||||
|
if(intent != null) {
|
||||||
|
extrasId = intent.getIntExtra(EXTRA_WIDGET_ID, extrasId);
|
||||||
|
}
|
||||||
|
if(extrasId == AppWidgetManager.INVALID_APPWIDGET_ID) {
|
||||||
|
int[] ids;
|
||||||
|
try {
|
||||||
|
ids = manager.getAppWidgetIds(thisWidget);
|
||||||
|
for(int id : ids) {
|
||||||
|
RemoteViews updateViews = buildUpdate(this, id);
|
||||||
|
manager.updateAppWidget(id, updateViews);
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
// "System server dead" was sometimes thrown here by the OS. Abort if that happens
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RemoteViews updateViews = buildUpdate(this, extrasId);
|
||||||
|
manager.updateAppWidget(extrasId, updateViews);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoteViews buildUpdate(Context context, int widgetId) {
|
||||||
|
DependencyInjectionService.getInstance().inject(this);
|
||||||
|
|
||||||
|
RemoteViews views = getThemedRemoteViews(context);
|
||||||
|
|
||||||
|
int numberOfTasks = NUM_VISIBLE_TASKS;
|
||||||
|
|
||||||
|
TodorooCursor<Task> cursor = null;
|
||||||
|
Filter filter = null;
|
||||||
|
try {
|
||||||
|
filter = widgetHelper.getFilter(context, widgetId);
|
||||||
|
if (SubtasksHelper.isTagFilter(filter)) {
|
||||||
|
((FilterWithCustomIntent) filter).customTaskList = new ComponentName(context, TagViewFragment.class); // In case legacy widget was created with subtasks fragment
|
||||||
|
}
|
||||||
|
views.setTextViewText(R.id.widget_title, filter.title);
|
||||||
|
views.removeAllViews(R.id.taskbody);
|
||||||
|
|
||||||
|
SharedPreferences publicPrefs = AstridPreferences.getPublicPrefs(this);
|
||||||
|
int flags = publicPrefs.getInt(SortHelper.PREF_SORT_FLAGS, 0);
|
||||||
|
int sort = publicPrefs.getInt(SortHelper.PREF_SORT_SORT, 0);
|
||||||
|
String query = SortHelper.adjustQueryForFlagsAndSort(
|
||||||
|
filter.getSqlQuery(), flags, sort).replaceAll("LIMIT \\d+", "") + " LIMIT " + numberOfTasks;
|
||||||
|
|
||||||
|
String tagName = Preferences.getStringValue(WidgetConfigActivity.PREF_TITLE + widgetId);
|
||||||
|
query = SubtasksHelper.applySubtasksToWidgetFilter(filter, query, tagName, numberOfTasks);
|
||||||
|
|
||||||
|
database.openForReading();
|
||||||
|
cursor = taskService.fetchFiltered(query, null, Task.ID, Task.TITLE, Task.DUE_DATE, Task.COMPLETION_DATE);
|
||||||
|
Task task = new Task();
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < cursor.getCount() && i < numberOfTasks; i++) {
|
||||||
|
cursor.moveToPosition(i);
|
||||||
|
task.readFromCursor(cursor);
|
||||||
|
|
||||||
|
String textContent;
|
||||||
|
Resources r = context.getResources();
|
||||||
|
int textColor = r
|
||||||
|
.getColor(ThemeService.isDarkWidgetTheme() ? R.color.widget_text_color_dark : R.color.widget_text_color_light);
|
||||||
|
|
||||||
|
textContent = task.getValue(Task.TITLE);
|
||||||
|
|
||||||
|
if(task.isCompleted()) {
|
||||||
|
textColor = r.getColor(R.color.task_list_done);
|
||||||
|
} else if(task.hasDueDate() && task.isOverdue()) {
|
||||||
|
textColor = r.getColor(R.color.task_list_overdue);
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteViews row = new RemoteViews(Constants.PACKAGE, R.layout.widget_row);
|
||||||
|
|
||||||
|
row.setTextViewText(R.id.text, textContent);
|
||||||
|
row.setTextColor(R.id.text, textColor);
|
||||||
|
|
||||||
|
views.addView(R.id.taskbody, row);
|
||||||
|
|
||||||
|
RemoteViews separator = new RemoteViews(Constants.PACKAGE, R.layout.widget_separator);
|
||||||
|
boolean isLastRow = (i == cursor.getCount() - 1) || (i == numberOfTasks - 1);
|
||||||
|
if (!isLastRow) {
|
||||||
|
views.addView(R.id.taskbody, separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < numberOfTasks; i++) {
|
||||||
|
RemoteViews row = new RemoteViews(Constants.PACKAGE, R.layout.widget_row);
|
||||||
|
row.setViewVisibility(R.id.text, View.INVISIBLE);
|
||||||
|
views.addView(R.id.taskbody, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// can happen if database is not ready
|
||||||
|
Log.e("WIDGET-UPDATE", "Error updating widget", e);
|
||||||
|
} finally {
|
||||||
|
if(cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingIntent pListIntent = widgetHelper.getListIntent(context, filter, widgetId);
|
||||||
|
if (pListIntent != null) {
|
||||||
|
views.setOnClickPendingIntent(R.id.taskbody, pListIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingIntent pEditIntent = widgetHelper.getEditIntent(context, filter, widgetId);
|
||||||
|
if (pEditIntent != null) {
|
||||||
|
views.setOnClickPendingIntent(R.id.widget_button, pEditIntent);
|
||||||
|
views.setOnClickPendingIntent(R.id.widget_title, pEditIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The reason we use a bunch of different but almost identical layouts is that there is a bug with
|
||||||
|
* Android 2.1 (level 7) that doesn't allow setting backgrounds on remote views. I know it's lame,
|
||||||
|
* but I didn't see a better solution. Alternatively, we could disallow theming widgets on
|
||||||
|
* Android 2.1.
|
||||||
|
*/
|
||||||
|
private RemoteViews getThemedRemoteViews(Context context) {
|
||||||
|
String packageName = context.getPackageName();
|
||||||
|
Resources r = context.getResources();
|
||||||
|
int layout;
|
||||||
|
RemoteViews views;
|
||||||
|
|
||||||
|
int titleColor;
|
||||||
|
int buttonDrawable;
|
||||||
|
|
||||||
|
if (ThemeService.isDarkWidgetTheme()) {
|
||||||
|
layout = R.layout.widget_initialized_dark;
|
||||||
|
titleColor = r.getColor(R.color.widget_text_color_dark);
|
||||||
|
buttonDrawable = R.drawable.ic_action_new_light;
|
||||||
|
} else {
|
||||||
|
layout = R.layout.widget_initialized;
|
||||||
|
titleColor = r.getColor(R.color.widget_text_color_light);
|
||||||
|
buttonDrawable = R.drawable.ic_action_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
views = new RemoteViews(packageName, layout);
|
||||||
|
views.setTextColor(R.id.widget_title, titleColor);
|
||||||
|
views.setInt(R.id.widget_button, "setImageResource", buttonDrawable);
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package org.tasks.widget;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProvider;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.service.ContextManager;
|
||||||
|
|
||||||
|
import org.tasks.R;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public class ScrollableTasksWidget extends AppWidgetProvider {
|
||||||
|
|
||||||
|
private final WidgetHelper widgetHelper = new WidgetHelper();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||||
|
ContextManager.setContext(context);
|
||||||
|
super.onUpdate(context, appWidgetManager, appWidgetIds);
|
||||||
|
|
||||||
|
ComponentName thisWidget = new ComponentName(context, ScrollableTasksWidget.class);
|
||||||
|
int[] ids = appWidgetManager.getAppWidgetIds(thisWidget);
|
||||||
|
for (int id : ids) {
|
||||||
|
appWidgetManager.updateAppWidget(id, widgetHelper.createScrollableWidget(context, id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateWidgets(Context context) {
|
||||||
|
updateWidgets(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateWidget(Context context, int id) {
|
||||||
|
updateWidgets(context, new int[]{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateWidgets(Context context, int[] widgetIds) {
|
||||||
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||||
|
if (widgetIds == null) {
|
||||||
|
widgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, ScrollableTasksWidget.class));
|
||||||
|
}
|
||||||
|
appWidgetManager.notifyAppWidgetViewDataChanged(widgetIds, R.id.list_view);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,131 @@
|
|||||||
|
package org.tasks.widget;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
import android.widget.RemoteViewsService;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.service.Autowired;
|
||||||
|
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||||
|
import com.todoroo.astrid.dao.Database;
|
||||||
|
import com.todoroo.astrid.data.Task;
|
||||||
|
import com.todoroo.astrid.service.TaskService;
|
||||||
|
import com.todoroo.astrid.utility.Constants;
|
||||||
|
|
||||||
|
import org.tasks.R;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
Database database;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
TaskService taskService;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private String query;
|
||||||
|
private boolean dark;
|
||||||
|
|
||||||
|
private TodorooCursor<Task> cursor;
|
||||||
|
|
||||||
|
public ScrollableViewsFactory(Context context, String query, boolean dark) {
|
||||||
|
this.context = context;
|
||||||
|
this.query = query;
|
||||||
|
this.dark = dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
DependencyInjectionService.getInstance().inject(this);
|
||||||
|
|
||||||
|
database.openForReading();
|
||||||
|
cursor = getCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDataSetChanged() {
|
||||||
|
cursor = getCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return cursor.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteViews getViewAt(int position) {
|
||||||
|
return buildUpdate(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteViews getLoadingView() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getViewTypeCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return getTask(position).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasStableIds() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoteViews buildUpdate(int position) {
|
||||||
|
try {
|
||||||
|
Task task = getTask(position);
|
||||||
|
|
||||||
|
String textContent;
|
||||||
|
Resources r = context.getResources();
|
||||||
|
int textColor = r.getColor(dark ? R.color.widget_text_color_dark : R.color.widget_text_color_light);
|
||||||
|
|
||||||
|
textContent = task.getValue(Task.TITLE);
|
||||||
|
|
||||||
|
if (task.isCompleted()) {
|
||||||
|
textColor = r.getColor(R.color.task_list_done);
|
||||||
|
} else if (task.hasDueDate() && task.isOverdue()) {
|
||||||
|
textColor = r.getColor(R.color.task_list_overdue);
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteViews row = new RemoteViews(Constants.PACKAGE, R.layout.widget_row);
|
||||||
|
|
||||||
|
row.setTextViewText(R.id.text, textContent);
|
||||||
|
row.setTextColor(R.id.text, textColor);
|
||||||
|
|
||||||
|
row.setOnClickFillInIntent(R.id.text, new Intent());
|
||||||
|
|
||||||
|
return row;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// can happen if database is not ready
|
||||||
|
Log.e("WIDGET-UPDATE", "Error updating widget", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TodorooCursor<Task> getCursor() {
|
||||||
|
return taskService.fetchFiltered(query, null, Task.ID, Task.TITLE, Task.DUE_DATE, Task.COMPLETION_DATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task getTask(int position) {
|
||||||
|
cursor.moveToPosition(position);
|
||||||
|
return new Task(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
package org.tasks.widget;
|
||||||
|
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||||
|
import com.todoroo.andlib.utility.Preferences;
|
||||||
|
import com.todoroo.astrid.adapter.FilterAdapter;
|
||||||
|
import com.todoroo.astrid.api.Filter;
|
||||||
|
import com.todoroo.astrid.api.FilterListItem;
|
||||||
|
import com.todoroo.astrid.api.FilterWithCustomIntent;
|
||||||
|
import com.todoroo.astrid.service.ThemeService;
|
||||||
|
|
||||||
|
import org.tasks.R;
|
||||||
|
|
||||||
|
public class ScrollableWidgetConfigActivity extends ListActivity {
|
||||||
|
|
||||||
|
static final String PREF_TITLE = "widget-title-";
|
||||||
|
static final String PREF_SQL = "widget-sql-";
|
||||||
|
static final String PREF_VALUES = "widget-values-";
|
||||||
|
static final String PREF_CUSTOM_INTENT = "widget-intent-";
|
||||||
|
static final String PREF_CUSTOM_EXTRAS = "widget-extras-";
|
||||||
|
static final String PREF_TAG_ID = "widget-tag-id-";
|
||||||
|
|
||||||
|
int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||||
|
|
||||||
|
FilterAdapter adapter = null;
|
||||||
|
|
||||||
|
private final WidgetHelper widgetHelper = new WidgetHelper();
|
||||||
|
|
||||||
|
public void updateWidget() {
|
||||||
|
// setup view for new widget
|
||||||
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
|
||||||
|
appWidgetManager.updateAppWidget(mAppWidgetId, widgetHelper.createScrollableWidget(getApplicationContext(), mAppWidgetId));
|
||||||
|
ScrollableTasksWidget.updateWidget(this, mAppWidgetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
ThemeService.applyTheme(this);
|
||||||
|
ThemeService.setForceFilterInvert(true);
|
||||||
|
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.widget_config_activity);
|
||||||
|
|
||||||
|
setTitle(R.string.WCA_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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up ui
|
||||||
|
adapter = new FilterAdapter(this, getListView(),
|
||||||
|
R.layout.filter_adapter_row, true, true);
|
||||||
|
adapter.filterStyle = R.style.TextAppearance_FLA_Filter_Widget;
|
||||||
|
setListAdapter(adapter);
|
||||||
|
|
||||||
|
Button button = (Button)findViewById(R.id.ok);
|
||||||
|
button.setOnClickListener(mOnClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
View.OnClickListener mOnClickListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// Save configuration options
|
||||||
|
saveConfiguration(adapter.getSelection());
|
||||||
|
|
||||||
|
updateWidget();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
|
super.onListItemClick(l, v, position, id);
|
||||||
|
Filter item = adapter.getItem(position);
|
||||||
|
adapter.setSelection(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
adapter.registerRecevier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
adapter.unregisterRecevier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
ThemeService.setForceFilterInvert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveConfiguration(FilterListItem filterListItem){
|
||||||
|
DisplayMetrics metrics = new DisplayMetrics();
|
||||||
|
getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||||
|
|
||||||
|
String sql = null, contentValuesString = null, title = null;
|
||||||
|
|
||||||
|
if(filterListItem != null && filterListItem instanceof Filter) {
|
||||||
|
sql = ((Filter)filterListItem).getSqlQuery();
|
||||||
|
ContentValues values = ((Filter)filterListItem).valuesForNewTasks;
|
||||||
|
if(values != null) {
|
||||||
|
contentValuesString = AndroidUtilities.contentValuesToSerializedString(values);
|
||||||
|
}
|
||||||
|
title = ((Filter)filterListItem).title;
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_TITLE + mAppWidgetId, title);
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_SQL + mAppWidgetId, sql);
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_VALUES + mAppWidgetId, contentValuesString);
|
||||||
|
|
||||||
|
if(filterListItem instanceof FilterWithCustomIntent) {
|
||||||
|
String flattenedName = ((FilterWithCustomIntent)filterListItem).customTaskList.flattenToString();
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_CUSTOM_INTENT + mAppWidgetId,
|
||||||
|
flattenedName);
|
||||||
|
String flattenedExtras = AndroidUtilities.bundleToSerializedString(((FilterWithCustomIntent)filterListItem).customExtras);
|
||||||
|
if (flattenedExtras != null) {
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_CUSTOM_EXTRAS + mAppWidgetId,
|
||||||
|
flattenedExtras);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package org.tasks.widget;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.RemoteViewsService;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public class ScrollableWidgetUpdateService extends RemoteViewsService {
|
||||||
|
|
||||||
|
public static final String QUERY_ID = "org.tasks.widget.query_id";
|
||||||
|
public static final String IS_DARK_THEME = "org.tasks.widget.is_dark_theme";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteViewsFactory onGetViewFactory(Intent intent) {
|
||||||
|
if (intent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle extras = intent.getExtras();
|
||||||
|
if (extras == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String queryId = extras.getString(QUERY_ID);
|
||||||
|
boolean isDarkTheme = extras.getBoolean(IS_DARK_THEME);
|
||||||
|
return new ScrollableViewsFactory(this, queryId, isDarkTheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,259 @@
|
|||||||
|
package org.tasks.widget;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.service.Autowired;
|
||||||
|
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||||
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||||
|
import com.todoroo.andlib.utility.DateUtilities;
|
||||||
|
import com.todoroo.andlib.utility.Preferences;
|
||||||
|
import com.todoroo.astrid.actfm.TagViewFragment;
|
||||||
|
import com.todoroo.astrid.activity.TaskEditActivity;
|
||||||
|
import com.todoroo.astrid.activity.TaskEditFragment;
|
||||||
|
import com.todoroo.astrid.activity.TaskListActivity;
|
||||||
|
import com.todoroo.astrid.activity.TaskListFragment;
|
||||||
|
import com.todoroo.astrid.api.Filter;
|
||||||
|
import com.todoroo.astrid.api.FilterWithCustomIntent;
|
||||||
|
import com.todoroo.astrid.api.PermaSql;
|
||||||
|
import com.todoroo.astrid.core.CoreFilterExposer;
|
||||||
|
import com.todoroo.astrid.core.SortHelper;
|
||||||
|
import com.todoroo.astrid.data.TagData;
|
||||||
|
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||||
|
import com.todoroo.astrid.service.TagDataService;
|
||||||
|
import com.todoroo.astrid.service.ThemeService;
|
||||||
|
import com.todoroo.astrid.subtasks.SubtasksHelper;
|
||||||
|
import com.todoroo.astrid.tags.TagFilterExposer;
|
||||||
|
import com.todoroo.astrid.utility.AstridPreferences;
|
||||||
|
import com.todoroo.astrid.utility.Constants;
|
||||||
|
import com.todoroo.astrid.widget.TasksWidget;
|
||||||
|
import com.todoroo.astrid.widget.WidgetConfigActivity;
|
||||||
|
import com.todoroo.astrid.widget.WidgetUpdateService;
|
||||||
|
|
||||||
|
import org.tasks.R;
|
||||||
|
|
||||||
|
public class WidgetHelper {
|
||||||
|
|
||||||
|
static {
|
||||||
|
AstridDependencyInjector.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long suppressUpdateFlag = 0; // Timestamp--don't update widgets if this flag is non-zero and now() is within 5 minutes
|
||||||
|
private static final long SUPPRESS_TIME = DateUtilities.ONE_MINUTE * 5;
|
||||||
|
|
||||||
|
public static void updateWidgets(Context context) {
|
||||||
|
if (suppressUpdateFlag > 0 && DateUtilities.now() - suppressUpdateFlag < SUPPRESS_TIME) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
suppressUpdateFlag = 0;
|
||||||
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
|
TasksWidget.updateWidgets(context);
|
||||||
|
} else {
|
||||||
|
ScrollableTasksWidget.updateWidgets(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startWidgetService(Context context) {
|
||||||
|
Class widgetServiceClass = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH
|
||||||
|
? WidgetUpdateService.class
|
||||||
|
: ScrollableWidgetUpdateService.class;
|
||||||
|
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
|
||||||
|
Intent intent = new Intent(context, widgetServiceClass);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getService(context,
|
||||||
|
0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
am.setInexactRepeating(AlarmManager.RTC, 0,
|
||||||
|
Constants.WIDGET_UPDATE_INTERVAL, pendingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void triggerUpdate(Context context) {
|
||||||
|
Class widgetClass = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH
|
||||||
|
? TasksWidget.class
|
||||||
|
: ScrollableTasksWidget.class;
|
||||||
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||||
|
ComponentName thisWidget = new ComponentName(context, widgetClass);
|
||||||
|
Intent intent = new Intent(context, widgetClass);
|
||||||
|
intent.setAction("android.appwidget.action.APPWIDGET_UPDATE");
|
||||||
|
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetManager.getAppWidgetIds(thisWidget));
|
||||||
|
context.sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
TagDataService tagDataService;
|
||||||
|
|
||||||
|
public WidgetHelper() {
|
||||||
|
DependencyInjectionService.getInstance().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public RemoteViews createScrollableWidget(Context context, int id) {
|
||||||
|
Filter filter = getFilter(context, id);
|
||||||
|
Intent rvIntent = new Intent(context, ScrollableWidgetUpdateService.class);
|
||||||
|
rvIntent.putExtra(ScrollableWidgetUpdateService.QUERY_ID, getQuery(context, filter, id));
|
||||||
|
rvIntent.putExtra(ScrollableWidgetUpdateService.IS_DARK_THEME, ThemeService.isDarkWidgetTheme());
|
||||||
|
rvIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id);
|
||||||
|
rvIntent.setData(Uri.parse(rvIntent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||||
|
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), ThemeService.isDarkWidgetTheme() ? R.layout.scrollable_widget_dark : R.layout.scrollable_widget_light);
|
||||||
|
remoteViews.setTextViewText(R.id.widget_title, filter.title);
|
||||||
|
remoteViews.setRemoteAdapter(R.id.list_view, rvIntent);
|
||||||
|
remoteViews.setEmptyView(R.id.list_view, R.id.empty_view);
|
||||||
|
PendingIntent listIntent = getListIntent(context, filter, id);
|
||||||
|
if(listIntent != null) {
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.widget_title, listIntent);
|
||||||
|
remoteViews.setPendingIntentTemplate(R.id.list_view, listIntent);
|
||||||
|
}
|
||||||
|
PendingIntent editIntent = getEditIntent(context, filter, id);
|
||||||
|
if (editIntent != null) {
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.widget_button, editIntent);
|
||||||
|
}
|
||||||
|
return remoteViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PendingIntent getListIntent(Context context, Filter filter, int widgetId) {
|
||||||
|
Intent listIntent = new Intent(context, TaskListActivity.class);
|
||||||
|
String customIntent = Preferences.getStringValue(WidgetConfigActivity.PREF_CUSTOM_INTENT
|
||||||
|
+ widgetId);
|
||||||
|
if(customIntent != null) {
|
||||||
|
String serializedExtras = Preferences.getStringValue(WidgetConfigActivity.PREF_CUSTOM_EXTRAS
|
||||||
|
+ widgetId);
|
||||||
|
Bundle extras = AndroidUtilities.bundleFromSerializedString(serializedExtras);
|
||||||
|
listIntent.putExtras(extras);
|
||||||
|
}
|
||||||
|
listIntent.putExtra(TaskListActivity.TOKEN_SOURCE, Constants.SOURCE_WIDGET);
|
||||||
|
listIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
if(filter != null) {
|
||||||
|
listIntent.putExtra(TaskListFragment.TOKEN_FILTER, filter);
|
||||||
|
listIntent.setAction("L" + widgetId + filter.getSqlQuery());
|
||||||
|
} else {
|
||||||
|
listIntent.setAction("L" + widgetId);
|
||||||
|
}
|
||||||
|
if (filter instanceof FilterWithCustomIntent) {
|
||||||
|
listIntent.putExtras(((FilterWithCustomIntent) filter).customExtras);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PendingIntent.getActivity(context, widgetId,
|
||||||
|
listIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PendingIntent getEditIntent(Context context, Filter filter, int id) {
|
||||||
|
Intent editIntent;
|
||||||
|
boolean tablet = AstridPreferences.useTabletLayout(context);
|
||||||
|
if (tablet) {
|
||||||
|
editIntent = new Intent(context, TaskListActivity.class);
|
||||||
|
editIntent.putExtra(TaskListActivity.OPEN_TASK, 0L);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
editIntent = new Intent(context, TaskEditActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
editIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
editIntent.putExtra(TaskEditFragment.OVERRIDE_FINISH_ANIM, false);
|
||||||
|
if(filter != null) {
|
||||||
|
editIntent.putExtra(TaskListFragment.TOKEN_FILTER, filter);
|
||||||
|
if (filter.valuesForNewTasks != null) {
|
||||||
|
String values = AndroidUtilities.contentValuesToSerializedString(filter.valuesForNewTasks);
|
||||||
|
values = PermaSql.replacePlaceholders(values);
|
||||||
|
editIntent.putExtra(TaskEditFragment.TOKEN_VALUES, values);
|
||||||
|
editIntent.setAction("E" + id + values);
|
||||||
|
}
|
||||||
|
if (tablet) {
|
||||||
|
if (filter instanceof FilterWithCustomIntent) {
|
||||||
|
Bundle customExtras = ((FilterWithCustomIntent) filter).customExtras;
|
||||||
|
editIntent.putExtras(customExtras);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
editIntent.setAction("E" + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PendingIntent.getActivity(context, -id, editIntent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Filter getFilter(Context context, int widgetId) {
|
||||||
|
|
||||||
|
// base our filter off the inbox filter, replace stuff if we have it
|
||||||
|
Filter filter = CoreFilterExposer.buildInboxFilter(context.getResources());
|
||||||
|
String sql = Preferences.getStringValue(ScrollableWidgetConfigActivity.PREF_SQL + widgetId);
|
||||||
|
if(sql != null) {
|
||||||
|
filter.setSqlQuery(sql);
|
||||||
|
}
|
||||||
|
String title = Preferences.getStringValue(ScrollableWidgetConfigActivity.PREF_TITLE + widgetId);
|
||||||
|
if(title != null) {
|
||||||
|
filter.title = title;
|
||||||
|
}
|
||||||
|
String contentValues = Preferences.getStringValue(ScrollableWidgetConfigActivity.PREF_VALUES + widgetId);
|
||||||
|
if(contentValues != null) {
|
||||||
|
filter.valuesForNewTasks = AndroidUtilities.contentValuesFromSerializedString(contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
String customComponent = Preferences.getStringValue(ScrollableWidgetConfigActivity.PREF_CUSTOM_INTENT
|
||||||
|
+ widgetId);
|
||||||
|
if (customComponent != null) {
|
||||||
|
ComponentName component = ComponentName.unflattenFromString(customComponent);
|
||||||
|
filter = new FilterWithCustomIntent(filter.title, filter.title, filter.getSqlQuery(), filter.valuesForNewTasks);
|
||||||
|
((FilterWithCustomIntent) filter).customTaskList = component;
|
||||||
|
String serializedExtras = Preferences.getStringValue(ScrollableWidgetConfigActivity.PREF_CUSTOM_EXTRAS
|
||||||
|
+ widgetId);
|
||||||
|
((FilterWithCustomIntent) filter).customExtras = AndroidUtilities.bundleFromSerializedString(serializedExtras);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate tagData
|
||||||
|
long id = Preferences.getLong(ScrollableWidgetConfigActivity.PREF_TAG_ID + widgetId, 0);
|
||||||
|
TagData tagData;
|
||||||
|
if (id > 0) {
|
||||||
|
tagData = tagDataService.fetchById(id, TagData.ID, TagData.NAME, TagData.TASK_COUNT, TagData.UUID, TagData.PICTURE, TagData.USER_ID, TagData.MEMBER_COUNT);
|
||||||
|
if (tagData != null && !tagData.getValue(TagData.NAME).equals(filter.title)) { // Tag has been renamed; rebuild filter
|
||||||
|
filter = TagFilterExposer.filterFromTagData(context, tagData);
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_SQL + widgetId, filter.getSqlQuery());
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_TITLE + widgetId, filter.title);
|
||||||
|
ContentValues newTaskValues = filter.valuesForNewTasks;
|
||||||
|
String contentValuesString = null;
|
||||||
|
if(newTaskValues != null) {
|
||||||
|
contentValuesString = AndroidUtilities.contentValuesToSerializedString(newTaskValues);
|
||||||
|
}
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_VALUES + widgetId, contentValuesString);
|
||||||
|
if (filter instanceof FilterWithCustomIntent) {
|
||||||
|
String flattenedExtras = AndroidUtilities.bundleToSerializedString(((FilterWithCustomIntent) filter).customExtras);
|
||||||
|
if (flattenedExtras != null) {
|
||||||
|
Preferences.setString(ScrollableWidgetConfigActivity.PREF_CUSTOM_EXTRAS + widgetId,
|
||||||
|
flattenedExtras);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tagData = tagDataService.getTagByName(filter.title, TagData.ID);
|
||||||
|
if (tagData != null) {
|
||||||
|
Preferences.setLong(ScrollableWidgetConfigActivity.PREF_TAG_ID + widgetId, tagData.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getQuery(Context context, Filter filter, int widgetId) {
|
||||||
|
if (SubtasksHelper.isTagFilter(filter)) {
|
||||||
|
((FilterWithCustomIntent) filter).customTaskList = new ComponentName(context, TagViewFragment.class); // In case legacy widget was created with subtasks fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferences publicPrefs = AstridPreferences.getPublicPrefs(context);
|
||||||
|
int flags = publicPrefs.getInt(SortHelper.PREF_SORT_FLAGS, 0);
|
||||||
|
int sort = publicPrefs.getInt(SortHelper.PREF_SORT_SORT, 0);
|
||||||
|
|
||||||
|
String query = SortHelper.adjustQueryForFlagsAndSort(
|
||||||
|
filter.getSqlQuery(), flags, sort).replaceAll("LIMIT \\d+", "");
|
||||||
|
|
||||||
|
String tagName = Preferences.getStringValue(ScrollableWidgetConfigActivity.PREF_TITLE + widgetId);
|
||||||
|
|
||||||
|
return SubtasksHelper.applySubtasksToWidgetFilter(filter, query, tagName, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
** Copyright (c) 2012 Todoroo Inc
|
||||||
|
**
|
||||||
|
** See the file "LICENSE" for the full license governing this code.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/widget"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@android:color/transparent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/widget_header"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="39dip"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:gravity="top"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="3dp"
|
||||||
|
android:background="@color/widget_header_dark">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="39dip"
|
||||||
|
android:layout_gravity="left|center_vertical"
|
||||||
|
android:paddingLeft="2dip"
|
||||||
|
android:textColor="@color/widget_text_color_dark"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="left|center_vertical"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/widget_button"
|
||||||
|
android:layout_width="33dip"
|
||||||
|
android:layout_height="39dip"
|
||||||
|
android:layout_gravity="right|center_vertical"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:src="@drawable/ic_action_new_light"
|
||||||
|
android:scaleType="fitCenter"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/widget_header_separator"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="1px"
|
||||||
|
android:background="@color/dark_blue_theme_color"/>
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/list_view"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:background="@color/widget_body_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@color/widget_body_light"
|
||||||
|
android:layout_marginTop="37dip"
|
||||||
|
android:padding="10dip"
|
||||||
|
android:text="@string/TWi_loading"
|
||||||
|
style="@style/TextAppearance.Widget" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
** Copyright (c) 2012 Todoroo Inc
|
||||||
|
**
|
||||||
|
** See the file "LICENSE" for the full license governing this code.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/widget"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@android:color/transparent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/widget_header"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="39dip"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:gravity="top"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="3dp"
|
||||||
|
android:background="@color/widget_header_light">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="39dip"
|
||||||
|
android:layout_gravity="left|center_vertical"
|
||||||
|
android:paddingLeft="2dip"
|
||||||
|
android:textColor="@color/widget_text_color_light"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="left|center_vertical"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/widget_button"
|
||||||
|
android:layout_width="33dip"
|
||||||
|
android:layout_height="39dip"
|
||||||
|
android:layout_gravity="right|center_vertical"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:src="@drawable/ic_action_new"
|
||||||
|
android:scaleType="fitCenter"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/widget_header_separator"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="1px"
|
||||||
|
android:background="@color/dark_blue_theme_color"/>
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/list_view"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:background="@color/widget_body_light"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@color/widget_body_light"
|
||||||
|
android:layout_marginTop="37dip"
|
||||||
|
android:padding="10dip"
|
||||||
|
android:text="@string/TWi_loading"
|
||||||
|
style="@style/TextAppearance.Widget" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<bool name="is_pre_api_14">false</bool>
|
||||||
|
<bool name="is_post_api_14">true</bool>
|
||||||
|
</resources>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
** Copyright (c) 2012 Todoroo Inc
|
||||||
|
**
|
||||||
|
** See the file "LICENSE" for the full license governing this code.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:configure="org.tasks.widget.ScrollableWidgetConfigActivity"
|
||||||
|
android:minWidth="142dip"
|
||||||
|
android:minHeight="142dip"
|
||||||
|
android:minResizeWidth="142dip"
|
||||||
|
android:minResizeHeight="142dip"
|
||||||
|
android:updatePeriodMillis="86400000"
|
||||||
|
android:initialLayout="@layout/scrollable_widget_light"
|
||||||
|
android:resizeMode="horizontal|vertical"/>
|
||||||
Loading…
Reference in New Issue