mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1412 lines
52 KiB
Java
1412 lines
52 KiB
Java
/**
|
|
* Copyright (c) 2012 Todoroo Inc
|
|
*
|
|
* See the file "LICENSE" for the full license governing this code.
|
|
*/
|
|
package com.todoroo.astrid.activity;
|
|
|
|
import java.util.List;
|
|
import java.util.Timer;
|
|
import java.util.TimerTask;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.SharedPreferences;
|
|
import android.content.SharedPreferences.Editor;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.content.res.Resources;
|
|
import android.database.Cursor;
|
|
import android.database.sqlite.SQLiteException;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Bundle;
|
|
import android.support.v4.app.Fragment;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
import android.view.ContextMenu;
|
|
import android.view.ContextMenu.ContextMenuInfo;
|
|
import android.view.KeyEvent;
|
|
import android.view.LayoutInflater;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.View.OnClickListener;
|
|
import android.view.View.OnKeyListener;
|
|
import android.view.View.OnTouchListener;
|
|
import android.view.ViewGroup;
|
|
import android.widget.AbsListView;
|
|
import android.widget.AdapterView;
|
|
import android.widget.AdapterView.AdapterContextMenuInfo;
|
|
import android.widget.AdapterView.OnItemClickListener;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ListView;
|
|
import android.widget.TextView;
|
|
|
|
import com.actionbarsherlock.app.ActionBar;
|
|
import com.actionbarsherlock.app.SherlockListFragment;
|
|
import com.actionbarsherlock.view.Menu;
|
|
import com.actionbarsherlock.view.MenuItem;
|
|
import org.tasks.R;
|
|
import com.todoroo.andlib.data.Property;
|
|
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.service.ExceptionService;
|
|
import com.todoroo.andlib.sql.Criterion;
|
|
import com.todoroo.andlib.sql.Field;
|
|
import com.todoroo.andlib.sql.Join;
|
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
import com.todoroo.andlib.utility.DateUtilities;
|
|
import com.todoroo.andlib.utility.Preferences;
|
|
import com.todoroo.astrid.actfm.CommentsActivity;
|
|
import com.todoroo.astrid.actfm.TagViewFragment;
|
|
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
|
|
import com.todoroo.astrid.activity.SortSelectionActivity.OnSortSelectedListener;
|
|
import com.todoroo.astrid.adapter.TaskAdapter;
|
|
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
|
|
import com.todoroo.astrid.adapter.TaskAdapter.ViewHolder;
|
|
import com.todoroo.astrid.api.AstridApiConstants;
|
|
import com.todoroo.astrid.api.Filter;
|
|
import com.todoroo.astrid.api.FilterWithCustomIntent;
|
|
import com.todoroo.astrid.api.TaskContextActionExposer;
|
|
import com.todoroo.astrid.api.TaskDecoration;
|
|
import com.todoroo.astrid.core.CoreFilterExposer;
|
|
import com.todoroo.astrid.core.CustomFilterActivity;
|
|
import com.todoroo.astrid.core.SortHelper;
|
|
import com.todoroo.astrid.dao.Database;
|
|
import com.todoroo.astrid.dao.TaskListMetadataDao;
|
|
import com.todoroo.astrid.data.Metadata;
|
|
import com.todoroo.astrid.data.RemoteModel;
|
|
import com.todoroo.astrid.data.TagData;
|
|
import com.todoroo.astrid.data.Task;
|
|
import com.todoroo.astrid.data.TaskAttachment;
|
|
import com.todoroo.astrid.data.TaskListMetadata;
|
|
import com.todoroo.astrid.data.User;
|
|
import com.todoroo.astrid.helper.SyncActionHelper;
|
|
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader;
|
|
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader.ContextMenuItem;
|
|
import com.todoroo.astrid.reminders.ReminderDebugContextActions;
|
|
import com.todoroo.astrid.service.AddOnService;
|
|
import com.todoroo.astrid.service.AstridDependencyInjector;
|
|
import com.todoroo.astrid.service.MetadataService;
|
|
import com.todoroo.astrid.service.TagDataService;
|
|
import com.todoroo.astrid.service.TaskService;
|
|
import com.todoroo.astrid.service.ThemeService;
|
|
import com.todoroo.astrid.service.UpgradeService;
|
|
import com.todoroo.astrid.subtasks.SubtasksHelper;
|
|
import com.todoroo.astrid.subtasks.SubtasksListFragment;
|
|
import com.todoroo.astrid.subtasks.SubtasksUpdater;
|
|
import com.todoroo.astrid.sync.SyncProviderPreferences;
|
|
import com.todoroo.astrid.tags.TaskToTagMetadata;
|
|
import com.todoroo.astrid.timers.TimerPlugin;
|
|
import com.todoroo.astrid.ui.FeedbackPromptDialogs;
|
|
import com.todoroo.astrid.ui.QuickAddBar;
|
|
import com.todoroo.astrid.utility.AstridPreferences;
|
|
import com.todoroo.astrid.utility.Flags;
|
|
import com.todoroo.astrid.widget.TasksWidget;
|
|
|
|
/**
|
|
* Primary activity for the Bente application. Shows a list of upcoming tasks
|
|
* and a user's coaches.
|
|
*
|
|
* @author Tim Su <tim@todoroo.com>
|
|
*
|
|
*/
|
|
public class TaskListFragment extends SherlockListFragment implements OnSortSelectedListener {
|
|
|
|
public static final String TAG_TASKLIST_FRAGMENT = "tasklist_fragment"; //$NON-NLS-1$
|
|
|
|
public static final String PREF_LAST_FEEDBACK_TIME = "pref_last_feedback_time"; //$NON-NLS-1$
|
|
private static final long FEEDBACK_TIME_INTERVAL = DateUtilities.ONE_WEEK * 12;
|
|
|
|
// --- activities
|
|
|
|
public static final long AUTOSYNC_INTERVAL = 90000L;
|
|
private static final long BACKGROUND_REFRESH_INTERVAL = 120000L;
|
|
private static final long WAIT_BEFORE_AUTOSYNC = 2000L;
|
|
public static final int ACTIVITY_EDIT_TASK = 0;
|
|
public static final int ACTIVITY_SETTINGS = 1;
|
|
public static final int ACTIVITY_SORT = 2;
|
|
public static final int ACTIVITY_ADDONS = 3;
|
|
public static final int ACTIVITY_MENU_EXTERNAL = 4;
|
|
public static final int ACTIVITY_REQUEST_NEW_FILTER = 5;
|
|
|
|
// --- menu codes
|
|
|
|
protected static final int MENU_SETTINGS_ID = R.string.TLA_menu_settings;
|
|
public static final int MENU_SORT_ID = R.string.TLA_menu_sort;
|
|
protected static final int MENU_SYNC_ID = R.string.TLA_menu_sync;
|
|
protected static final int MENU_SUPPORT_ID = R.string.TLA_menu_support;
|
|
public static final int MENU_NEW_FILTER_ID = R.string.FLA_new_filter;
|
|
protected static final int MENU_ADDON_INTENT_ID = Menu.FIRST + 199;
|
|
|
|
protected static final int CONTEXT_MENU_EDIT_TASK_ID = R.string.TAd_contextEditTask;
|
|
protected static final int CONTEXT_MENU_COPY_TASK_ID = R.string.TAd_contextCopyTask;
|
|
protected static final int CONTEXT_MENU_DELETE_TASK_ID = R.string.TAd_contextDeleteTask;
|
|
protected static final int CONTEXT_MENU_UNDELETE_TASK_ID = R.string.TAd_contextUndeleteTask;
|
|
protected static final int CONTEXT_MENU_PURGE_TASK_ID = R.string.TAd_contextPurgeTask;
|
|
protected static final int CONTEXT_MENU_BROADCAST_INTENT_ID = Menu.FIRST + 25;
|
|
protected static final int CONTEXT_MENU_PLUGIN_ID_FIRST = Menu.FIRST + 26;
|
|
|
|
// --- constants
|
|
|
|
/** token for passing a {@link Filter} object through extras */
|
|
public static final String TOKEN_FILTER = "filter"; //$NON-NLS-1$
|
|
|
|
private static final String TOKEN_EXTRAS = "extras"; //$NON-NLS-1$
|
|
|
|
// --- instance variables
|
|
|
|
@Autowired
|
|
protected ExceptionService exceptionService;
|
|
|
|
@Autowired
|
|
protected TaskService taskService;
|
|
|
|
@Autowired MetadataService metadataService;
|
|
|
|
@Autowired Database database;
|
|
|
|
@Autowired AddOnService addOnService;
|
|
|
|
@Autowired UpgradeService upgradeService;
|
|
|
|
@Autowired TagDataService tagDataService;
|
|
|
|
@Autowired TaskListMetadataDao taskListMetadataDao;
|
|
|
|
@Autowired ActFmPreferenceService actFmPreferenceService;
|
|
|
|
private final TaskContextActionExposer[] contextItemExposers = new TaskContextActionExposer[] {
|
|
new ReminderDebugContextActions.MakeNotification(),
|
|
new ReminderDebugContextActions.WhenReminder(),
|
|
};
|
|
|
|
protected Resources resources;
|
|
protected TaskAdapter taskAdapter = null;
|
|
protected DetailReceiver detailReceiver = new DetailReceiver();
|
|
protected RefreshReceiver refreshReceiver = new RefreshReceiver();
|
|
protected final AtomicReference<String> sqlQueryTemplate = new AtomicReference<String>();
|
|
protected SyncActionHelper syncActionHelper;
|
|
protected Filter filter;
|
|
protected int sortFlags;
|
|
protected int sortSort;
|
|
protected QuickAddBar quickAddBar;
|
|
|
|
private Timer backgroundTimer;
|
|
protected Bundle extras;
|
|
protected boolean isInbox;
|
|
protected boolean isTodayFilter;
|
|
protected TaskListMetadata taskListMetadata;
|
|
|
|
private final TaskListContextMenuExtensionLoader contextMenuExtensionLoader = new TaskListContextMenuExtensionLoader();
|
|
|
|
// --- fragment handling variables
|
|
protected OnTaskListItemClickedListener mListener;
|
|
private boolean mDualFragments = false;
|
|
|
|
/*
|
|
* ======================================================================
|
|
* ======================================================= initialization
|
|
* ======================================================================
|
|
*/
|
|
|
|
static {
|
|
AstridDependencyInjector.initialize();
|
|
}
|
|
|
|
/**
|
|
* Instantiates and returns an instance of TaskListFragment (or some subclass). Custom types of
|
|
* TaskListFragment can be created, with the following precedence:
|
|
*
|
|
* --If the filter is of type {@link FilterWithCustomIntent}, the task list type it specifies will be used
|
|
* --Otherwise, the specified customComponent will be used
|
|
*
|
|
* See also: instantiateWithFilterAndExtras(Filter, Bundle) which uses TaskListFragment as the default
|
|
* custom component.
|
|
* @param filter
|
|
* @param extras
|
|
* @param customComponent
|
|
* @return
|
|
*/
|
|
public static TaskListFragment instantiateWithFilterAndExtras(Filter filter, Bundle extras, Class<?> customComponent) {
|
|
Class<?> component = customComponent;
|
|
if (filter instanceof FilterWithCustomIntent && component == null) {
|
|
try {
|
|
component = Class.forName(((FilterWithCustomIntent) filter).customTaskList.getClassName());
|
|
} catch (Exception e) {
|
|
// Invalid
|
|
}
|
|
}
|
|
if (component == null) {
|
|
component = TaskListFragment.class;
|
|
}
|
|
|
|
TaskListFragment newFragment;
|
|
try {
|
|
newFragment = (TaskListFragment) component.newInstance();
|
|
} catch (java.lang.InstantiationException e) {
|
|
Log.e("tla-instantiate", "tla-instantiate", e);
|
|
newFragment = new TaskListFragment();
|
|
} catch (IllegalAccessException e) {
|
|
Log.e("tla-instantiate", "tla-instantiate", e);
|
|
newFragment = new TaskListFragment();
|
|
}
|
|
Bundle args = new Bundle();
|
|
args.putBundle(TOKEN_EXTRAS, extras);
|
|
newFragment.setArguments(args);
|
|
return newFragment;
|
|
}
|
|
|
|
/**
|
|
* Convenience method for calling instantiateWithFilterAndExtras(Filter, Bundle, Class<?>) with
|
|
* TaskListFragment as the default component
|
|
* @param filter
|
|
* @param extras
|
|
* @return
|
|
*/
|
|
public static TaskListFragment instantiateWithFilterAndExtras(Filter filter, Bundle extras) {
|
|
return instantiateWithFilterAndExtras(filter, extras, null);
|
|
}
|
|
|
|
/**
|
|
* Container Activity must implement this interface and we ensure that it
|
|
* does during the onAttach() callback
|
|
*/
|
|
public interface OnTaskListItemClickedListener {
|
|
public void onTaskListItemClicked(long taskId);
|
|
public void onTaskListItemClicked(long taskId, boolean editable);
|
|
}
|
|
|
|
@Override
|
|
public void onAttach(Activity activity) {
|
|
super.onAttach(activity);
|
|
// Check that the container activity has implemented the callback
|
|
// interface
|
|
try {
|
|
mListener = (OnTaskListItemClickedListener) activity;
|
|
} catch (ClassCastException e) {
|
|
throw new ClassCastException(activity.toString()
|
|
+ " must implement OnTaskListItemClickedListener"); //$NON-NLS-1$
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return view to attach to the body of the task list. must contain two
|
|
* elements, a view with id android:id/empty and a list view with id
|
|
* android:id/list. It should NOT be attached to root
|
|
*/
|
|
protected View getListBody(ViewGroup root) {
|
|
return getActivity().getLayoutInflater().inflate(
|
|
R.layout.task_list_body_standard, root, false);
|
|
}
|
|
|
|
/** Called when loading up the activity */
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
DependencyInjectionService.getInstance().inject(this);
|
|
super.onCreate(savedInstanceState);
|
|
extras = getArguments() != null ? getArguments().getBundle(TOKEN_EXTRAS) : null;
|
|
if (extras == null) {
|
|
extras = new Bundle(); // Just need an empty one to prevent potential null pointers
|
|
}
|
|
}
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see
|
|
* android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater,
|
|
* android.view.ViewGroup, android.os.Bundle)
|
|
*/
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
Bundle savedInstanceState) {
|
|
ViewGroup parent = (ViewGroup) getActivity().getLayoutInflater().inflate(
|
|
R.layout.task_list_activity, container, false);
|
|
parent.addView(getListBody(parent), 0);
|
|
|
|
return parent;
|
|
}
|
|
|
|
@Override
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
super.onActivityCreated(savedInstanceState);
|
|
// We have a menu item to show in action bar.
|
|
resources = getResources();
|
|
setHasOptionsMenu(true);
|
|
syncActionHelper = new SyncActionHelper(getActivity(), this);
|
|
setUpUiComponents();
|
|
initializeData();
|
|
setupQuickAddBar();
|
|
|
|
Fragment filterlistFrame = getFragmentManager().findFragmentByTag(
|
|
FilterListFragment.TAG_FILTERLIST_FRAGMENT);
|
|
mDualFragments = (filterlistFrame != null)
|
|
&& filterlistFrame.isInLayout();
|
|
|
|
if (mDualFragments) {
|
|
// In dual-pane mode, the list view highlights the selected item.
|
|
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
|
getListView().setItemsCanFocus(false);
|
|
}
|
|
|
|
if (Preferences.getInt(AstridPreferences.P_UPGRADE_FROM, -1) > -1) {
|
|
upgradeService.showChangeLog(getActivity(),
|
|
Preferences.getInt(AstridPreferences.P_UPGRADE_FROM, -1));
|
|
}
|
|
|
|
getListView().setOnItemClickListener(new OnItemClickListener() {
|
|
@Override
|
|
public void onItemClick(AdapterView<?> parent, View view,
|
|
int position, long id) {
|
|
if (taskAdapter != null) {
|
|
TodorooCursor<Task> cursor = (TodorooCursor<Task>)taskAdapter.getItem(position);
|
|
Task task = new Task(cursor);
|
|
if(task.isDeleted()) {
|
|
return;
|
|
}
|
|
|
|
onTaskListItemClicked(id, task.isEditable());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @return the current tag you are viewing, or null if you're not viewing a tag
|
|
*/
|
|
public TagData getActiveTagData() {
|
|
return null;
|
|
}
|
|
|
|
public TaskListMetadata getTaskListMetadata() {
|
|
return taskListMetadata;
|
|
}
|
|
|
|
protected void initializeData() {
|
|
if (extras != null && extras.containsKey(TOKEN_FILTER)) {
|
|
filter = extras.getParcelable(TOKEN_FILTER);
|
|
extras.remove(TOKEN_FILTER); // Otherwise writing this filter to parcel gives infinite recursion
|
|
} else {
|
|
filter = CoreFilterExposer.buildInboxFilter(resources);
|
|
}
|
|
filter.setFilterQueryOverride(null);
|
|
isInbox = CoreFilterExposer.isInbox(filter);
|
|
isTodayFilter = false;
|
|
if (!isInbox) {
|
|
isTodayFilter = CoreFilterExposer.isTodayFilter(filter);
|
|
}
|
|
|
|
initializeTaskListMetadata();
|
|
|
|
setUpTaskList();
|
|
((AstridActivity) getActivity()).setupActivityFragment(getActiveTagData());
|
|
|
|
contextMenuExtensionLoader.loadInNewThread(getActivity());
|
|
}
|
|
|
|
protected void initializeTaskListMetadata() {
|
|
TagData td = getActiveTagData();
|
|
String tdId;
|
|
if (td == null) {
|
|
String filterId = null;
|
|
String prefId = null;
|
|
if (isInbox) {
|
|
filterId = TaskListMetadata.FILTER_ID_ALL;
|
|
prefId = SubtasksUpdater.ACTIVE_TASKS_ORDER;
|
|
} else if (isTodayFilter) {
|
|
filterId = TaskListMetadata.FILTER_ID_TODAY;
|
|
prefId = SubtasksUpdater.TODAY_TASKS_ORDER;
|
|
}
|
|
if (!TextUtils.isEmpty(filterId)) {
|
|
taskListMetadata = taskListMetadataDao.fetchByTagId(filterId, TaskListMetadata.PROPERTIES);
|
|
if (taskListMetadata == null) {
|
|
String defaultOrder = Preferences.getStringValue(prefId);
|
|
if (TextUtils.isEmpty(defaultOrder)) {
|
|
defaultOrder = "[]"; //$NON-NLS-1$
|
|
}
|
|
defaultOrder = SubtasksHelper.convertTreeToRemoteIds(defaultOrder);
|
|
taskListMetadata = new TaskListMetadata();
|
|
taskListMetadata.setValue(TaskListMetadata.FILTER, filterId);
|
|
taskListMetadata.setValue(TaskListMetadata.TASK_IDS, defaultOrder);
|
|
taskListMetadataDao.createNew(taskListMetadata);
|
|
}
|
|
}
|
|
} else {
|
|
tdId = td.getUuid();
|
|
taskListMetadata = taskListMetadataDao.fetchByTagId(td.getUuid(), TaskListMetadata.PROPERTIES);
|
|
if (taskListMetadata == null && !RemoteModel.isUuidEmpty(tdId)) {
|
|
taskListMetadata = new TaskListMetadata();
|
|
taskListMetadata.setValue(TaskListMetadata.TAG_UUID, tdId);
|
|
taskListMetadataDao.createNew(taskListMetadata);
|
|
}
|
|
}
|
|
postLoadTaskListMetadata();
|
|
}
|
|
|
|
protected void postLoadTaskListMetadata() {
|
|
// Hook
|
|
}
|
|
|
|
protected void addSyncRefreshMenuItem(Menu menu, int themeFlags) {
|
|
addMenuItem(menu, R.string.TLA_menu_sync,
|
|
ThemeService.getDrawable(R.drawable.icn_menu_refresh, themeFlags), MENU_SYNC_ID, true);
|
|
}
|
|
|
|
protected void addMenuItem(Menu menu, int title, int imageRes, int id, boolean showAsAction) {
|
|
AstridActivity activity = (AstridActivity) getActivity();
|
|
if ((activity.getFragmentLayout() != AstridActivity.LAYOUT_SINGLE && showAsAction) || !(activity instanceof TaskListActivity)) {
|
|
MenuItem item = menu.add(Menu.NONE, id, Menu.NONE, title);
|
|
item.setIcon(imageRes);
|
|
if (activity instanceof TaskListActivity) {
|
|
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
|
}
|
|
} else {
|
|
((TaskListActivity) activity).getMainMenuPopover().addMenuItem(title, imageRes, id);
|
|
}
|
|
}
|
|
|
|
protected void addMenuItem(Menu menu, CharSequence title, Drawable image, Intent customIntent, int id) {
|
|
Activity activity = getActivity();
|
|
if (activity instanceof TaskListActivity) {
|
|
((TaskListActivity) activity).getMainMenuPopover().addMenuItem(title, image, customIntent, id);
|
|
} else {
|
|
MenuItem item = menu.add(Menu.NONE, id, Menu.NONE, title);
|
|
item.setIcon(image);
|
|
item.setIntent(customIntent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create options menu (displayed when user presses menu key)
|
|
*
|
|
* @return true if menu should be displayed
|
|
*/
|
|
@Override
|
|
public void onCreateOptionsMenu(Menu menu, com.actionbarsherlock.view.MenuInflater inflater) {
|
|
Activity activity = getActivity();
|
|
if (activity == null) {
|
|
return;
|
|
}
|
|
if (!isCurrentTaskListFragment()) {
|
|
return;
|
|
}
|
|
|
|
addMenuItems(menu, activity);
|
|
}
|
|
|
|
protected void addMenuItems(Menu menu, Activity activity) {
|
|
boolean isTablet = AstridPreferences.useTabletLayout(activity);
|
|
TaskListActivity tla = null;
|
|
if (activity instanceof TaskListActivity) {
|
|
tla = (TaskListActivity) activity;
|
|
tla.getMainMenuPopover().clear();
|
|
}
|
|
// --- sync
|
|
if (tla == null || tla.getTaskEditFragment() == null && Preferences.getBoolean(R.string.p_show_menu_sync, true)) {
|
|
addSyncRefreshMenuItem(menu, isTablet ? ThemeService.FLAG_INVERT : 0);
|
|
}
|
|
|
|
// --- sort
|
|
if (allowResorting() && Preferences.getBoolean(R.string.p_show_menu_sort, true)) {
|
|
addMenuItem(menu, R.string.TLA_menu_sort,
|
|
ThemeService.getDrawable(R.drawable.icn_menu_sort_by_size, isTablet ? ThemeService.FLAG_FORCE_DARK: 0), MENU_SORT_ID, false);
|
|
}
|
|
|
|
// --- new filter
|
|
if (Preferences.getBoolean(R.string.p_use_filters, true)) {
|
|
addMenuItem(menu, R.string.FLA_new_filter,
|
|
ThemeService.getDrawable(R.drawable.icn_menu_filters, isTablet ? ThemeService.FLAG_FORCE_DARK : 0), MENU_NEW_FILTER_ID, false);
|
|
}
|
|
|
|
// ask about plug-ins
|
|
Intent queryIntent = new Intent(
|
|
AstridApiConstants.ACTION_TASK_LIST_MENU);
|
|
|
|
PackageManager pm = getActivity().getPackageManager();
|
|
List<ResolveInfo> resolveInfoList = pm.queryIntentActivities(
|
|
queryIntent, 0);
|
|
int length = resolveInfoList.size();
|
|
for (int i = 0; i < length; i++) {
|
|
ResolveInfo resolveInfo = resolveInfoList.get(i);
|
|
Intent intent = new Intent(AstridApiConstants.ACTION_TASK_LIST_MENU);
|
|
intent.setClassName(resolveInfo.activityInfo.packageName,
|
|
resolveInfo.activityInfo.name);
|
|
addMenuItem(menu, resolveInfo.loadLabel(pm), resolveInfo.loadIcon(pm), intent, MENU_ADDON_INTENT_ID);
|
|
}
|
|
}
|
|
|
|
protected boolean allowResorting() {
|
|
return true;
|
|
}
|
|
|
|
protected void setUpUiComponents() {
|
|
// set listener for quick-changing task priority
|
|
getListView().setOnKeyListener(new OnKeyListener() {
|
|
@Override
|
|
public boolean onKey(View view, int keyCode, KeyEvent event) {
|
|
if (event.getAction() != KeyEvent.ACTION_UP || view == null) {
|
|
return false;
|
|
}
|
|
|
|
boolean filterOn = getListView().isTextFilterEnabled();
|
|
View selected = getListView().getSelectedView();
|
|
|
|
// hot-key to set task priority - 1-4 or ALT + Q-R
|
|
if (!filterOn && event.getUnicodeChar() >= '1'
|
|
&& event.getUnicodeChar() <= '4' && selected != null) {
|
|
int importance = event.getNumber() - '1';
|
|
Task task = ((ViewHolder) selected.getTag()).task;
|
|
task.setValue(Task.IMPORTANCE, importance);
|
|
taskService.save(task);
|
|
taskAdapter.setFieldContentsAndVisibility(selected);
|
|
}
|
|
// filter
|
|
else if (!filterOn && event.getUnicodeChar() != 0) {
|
|
getListView().setTextFilterEnabled(true);
|
|
getListView().setFilterText(
|
|
Character.toString((char) event.getUnicodeChar()));
|
|
}
|
|
// turn off filter if nothing is selected
|
|
else if (filterOn
|
|
&& TextUtils.isEmpty(getListView().getTextFilter())) {
|
|
getListView().setTextFilterEnabled(false);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
SharedPreferences publicPrefs = AstridPreferences.getPublicPrefs(getActivity());
|
|
sortFlags = publicPrefs.getInt(SortHelper.PREF_SORT_FLAGS, 0);
|
|
sortSort = publicPrefs.getInt(SortHelper.PREF_SORT_SORT, 0);
|
|
sortFlags = SortHelper.setManualSort(sortFlags, isDraggable());
|
|
|
|
getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
|
|
}
|
|
|
|
protected void setupQuickAddBar() {
|
|
quickAddBar = (QuickAddBar) getView().findViewById(R.id.taskListFooter);
|
|
quickAddBar.initialize((AstridActivity) getActivity(), this, mListener);
|
|
|
|
getListView().setOnTouchListener(new OnTouchListener() {
|
|
@Override
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
quickAddBar.clearFocus();
|
|
return false;
|
|
}
|
|
});
|
|
|
|
// set listener for astrid icon
|
|
((TextView) getView().findViewById(android.R.id.empty)).setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
quickAddBar.performButtonClick();
|
|
}
|
|
});
|
|
}
|
|
|
|
public void transitionForTaskEdit() {
|
|
AndroidUtilities.callOverridePendingTransition(getActivity(),
|
|
R.anim.slide_left_in, R.anim.slide_left_out);
|
|
}
|
|
|
|
private void setUpBackgroundJobs() {
|
|
backgroundTimer = new Timer();
|
|
|
|
// start a thread to refresh periodically
|
|
backgroundTimer.scheduleAtFixedRate(new TimerTask() {
|
|
@Override
|
|
public void run() {
|
|
// refresh if conditions match
|
|
Flags.checkAndClear(Flags.REFRESH);
|
|
getActivity().runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
refresh();
|
|
} catch (IllegalStateException e) {
|
|
// view may have been destroyed
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}, BACKGROUND_REFRESH_INTERVAL, BACKGROUND_REFRESH_INTERVAL);
|
|
}
|
|
|
|
/*
|
|
* ======================================================================
|
|
* ============================================================ lifecycle
|
|
* ======================================================================
|
|
*/
|
|
|
|
@Override
|
|
public void onStart() {
|
|
super.onStart();
|
|
quickAddBar.setupRecognizerApi();
|
|
}
|
|
|
|
@Override
|
|
public void onStop() {
|
|
super.onStop();
|
|
quickAddBar.destroyRecognizerApi();
|
|
}
|
|
|
|
/**
|
|
* Crazy hack so that tag view fragment won't automatically initiate an autosync after a tag
|
|
* is deleted. TagViewFragment has to call onResume, but we don't want it to call
|
|
* the normal tasklist onResume.
|
|
*/
|
|
protected void parentOnResume() {
|
|
super.onResume();
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
|
|
getActivity().registerReceiver(detailReceiver,
|
|
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS));
|
|
getActivity().registerReceiver(detailReceiver,
|
|
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DECORATIONS));
|
|
getActivity().registerReceiver(refreshReceiver,
|
|
new IntentFilter(AstridApiConstants.BROADCAST_EVENT_REFRESH));
|
|
syncActionHelper.register();
|
|
|
|
if (Flags.checkAndClear(Flags.REFRESH)) {
|
|
refresh();
|
|
}
|
|
|
|
setUpBackgroundJobs();
|
|
|
|
refreshFilterCount();
|
|
|
|
initiateAutomaticSync();
|
|
|
|
// showFeedbackPrompt();
|
|
}
|
|
|
|
private void showFeedbackPrompt() {
|
|
if (!(this instanceof TagViewFragment) &&
|
|
(DateUtilities.now() - Preferences.getLong(PREF_LAST_FEEDBACK_TIME, 0)) > FEEDBACK_TIME_INTERVAL &&
|
|
taskService.getUserActivationStatus()) {
|
|
final LinearLayout root = (LinearLayout) getView().findViewById(R.id.taskListParent);
|
|
if (root.findViewById(R.id.feedback_banner) == null) {
|
|
final View feedbackPrompt = getActivity().getLayoutInflater().inflate(R.layout.feedback_prompt, root, false);
|
|
|
|
feedbackPrompt.findViewById(R.id.positiveFeedback).setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
try {
|
|
root.removeView(feedbackPrompt);
|
|
FeedbackPromptDialogs.showFeedbackDialog((AstridActivity) getActivity(), true);
|
|
Preferences.setLong(PREF_LAST_FEEDBACK_TIME, DateUtilities.now());
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
});
|
|
|
|
feedbackPrompt.findViewById(R.id.negativeFeedback).setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
try {
|
|
root.removeView(feedbackPrompt);
|
|
FeedbackPromptDialogs.showFeedbackDialog((AstridActivity) getActivity(), false);
|
|
Preferences.setLong(PREF_LAST_FEEDBACK_TIME, DateUtilities.now());
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
});
|
|
|
|
root.addView(feedbackPrompt, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected boolean isCurrentTaskListFragment() {
|
|
AstridActivity activity = (AstridActivity) getActivity();
|
|
if (activity != null) {
|
|
return activity.getTaskListFragment() == this;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public final void initiateAutomaticSync() {
|
|
final AstridActivity activity = (AstridActivity) getActivity();
|
|
if (activity == null) {
|
|
return;
|
|
}
|
|
if (activity.fragmentLayout != AstridActivity.LAYOUT_SINGLE) {
|
|
initiateAutomaticSyncImpl();
|
|
} else {
|
|
// In single fragment case, we're using swipe between lists,
|
|
// so wait a couple seconds before initiating the autosync.
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
AndroidUtilities.sleepDeep(WAIT_BEFORE_AUTOSYNC);
|
|
activity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
initiateAutomaticSyncImpl();
|
|
}
|
|
});
|
|
}
|
|
}.start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of initiation automatic sync. Subclasses should override this method;
|
|
* the above method takes care of calling it in the correct way
|
|
*/
|
|
protected void initiateAutomaticSyncImpl() {
|
|
if (isCurrentTaskListFragment() && isInbox) {
|
|
syncActionHelper.initiateAutomaticSync();
|
|
}
|
|
}
|
|
|
|
// Subclasses should override this
|
|
public void requestCommentCountUpdate() {
|
|
TaskListActivity activity = (TaskListActivity) getActivity();
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
super.onPause();
|
|
|
|
AndroidUtilities.tryUnregisterReceiver(getActivity(), detailReceiver);
|
|
AndroidUtilities.tryUnregisterReceiver(getActivity(), refreshReceiver);
|
|
syncActionHelper.unregister();
|
|
|
|
backgroundTimer.cancel();
|
|
}
|
|
|
|
/**
|
|
* Receiver which receives refresh intents
|
|
*
|
|
* @author Tim Su <tim@todoroo.com>
|
|
*
|
|
*/
|
|
protected class RefreshReceiver extends BroadcastReceiver {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
if (intent == null
|
|
|| !AstridApiConstants.BROADCAST_EVENT_REFRESH.equals(intent.getAction())) {
|
|
return;
|
|
}
|
|
|
|
final Activity activity = getActivity();
|
|
if (activity != null) {
|
|
activity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
refresh();
|
|
if (activity instanceof TaskListActivity) {
|
|
((TaskListActivity) activity).refreshMainMenu();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called by the RefreshReceiver when the task list receives a refresh
|
|
* broadcast. Subclasses should override this.
|
|
*/
|
|
protected void refresh() {
|
|
if (taskAdapter != null) {
|
|
taskAdapter.flushCaches();
|
|
}
|
|
taskService.cleanup();
|
|
loadTaskListContent(true);
|
|
}
|
|
|
|
/**
|
|
* Receiver which receives detail or decoration intents
|
|
*
|
|
* @author Tim Su <tim@todoroo.com>
|
|
*
|
|
*/
|
|
protected class DetailReceiver extends BroadcastReceiver {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
try {
|
|
Bundle receivedExtras = intent.getExtras();
|
|
long taskId = receivedExtras.getLong(AstridApiConstants.EXTRAS_TASK_ID);
|
|
String addOn = receivedExtras.getString(AstridApiConstants.EXTRAS_ADDON);
|
|
|
|
if (AstridApiConstants.BROADCAST_SEND_DECORATIONS.equals(intent.getAction())) {
|
|
TaskDecoration deco = receivedExtras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
|
|
taskAdapter.decorationManager.addNew(taskId, addOn, deco,
|
|
null);
|
|
} else if (AstridApiConstants.BROADCAST_SEND_DETAILS.equals(intent.getAction())) {
|
|
String detail = receivedExtras.getString(AstridApiConstants.EXTRAS_RESPONSE);
|
|
taskAdapter.addDetails(taskId, detail);
|
|
}
|
|
} catch (Exception e) {
|
|
exceptionService.reportError("receive-detail-" + //$NON-NLS-1$
|
|
intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON),
|
|
e);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
if(quickAddBar.onActivityResult(requestCode, resultCode, data)) {
|
|
return;
|
|
}
|
|
|
|
if (requestCode == ACTIVITY_SETTINGS) {
|
|
if (resultCode == EditPreferences.RESULT_CODE_THEME_CHANGED || resultCode == EditPreferences.RESULT_CODE_PERFORMANCE_PREF_CHANGED) {
|
|
getActivity().finish();
|
|
getActivity().startActivity(getActivity().getIntent());
|
|
TasksWidget.updateWidgets(getActivity());
|
|
return;
|
|
} else if (resultCode == SyncProviderPreferences.RESULT_CODE_SYNCHRONIZE) {
|
|
Preferences.setLong(SyncActionHelper.PREF_LAST_AUTO_SYNC, 0); // Forces autosync to occur after login
|
|
}
|
|
}
|
|
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
}
|
|
|
|
public void onScroll(AbsListView view, int firstVisibleItem,
|
|
int visibleItemCount, int totalItemCount) {
|
|
// do nothing
|
|
}
|
|
|
|
/*
|
|
* ======================================================================
|
|
* =================================================== managing list view
|
|
* ======================================================================
|
|
*/
|
|
|
|
/**
|
|
* Load or re-load action items and update views
|
|
*
|
|
* @param requery
|
|
*/
|
|
public void loadTaskListContent(boolean requery) {
|
|
if (taskAdapter == null) {
|
|
setUpTaskList();
|
|
return;
|
|
}
|
|
|
|
Cursor taskCursor = taskAdapter.getCursor();
|
|
|
|
if (requery) {
|
|
taskCursor.requery();
|
|
taskAdapter.flushCaches();
|
|
taskAdapter.notifyDataSetChanged();
|
|
}
|
|
|
|
if (getView() != null) { // This was happening sometimes
|
|
int oldListItemSelected = getListView().getSelectedItemPosition();
|
|
if (oldListItemSelected != ListView.INVALID_POSITION
|
|
&& oldListItemSelected < taskCursor.getCount()) {
|
|
getListView().setSelection(oldListItemSelected);
|
|
}
|
|
}
|
|
|
|
// also load sync actions
|
|
syncActionHelper.request();
|
|
}
|
|
|
|
public static int getTaskRowResource() {
|
|
int rowStyle = Preferences.getIntegerFromString(R.string.p_taskRowStyle_v2, 0);
|
|
switch(rowStyle) {
|
|
case 1:
|
|
return R.layout.task_adapter_row_simple;
|
|
case 2:
|
|
return R.layout.task_adapter_row_title_only;
|
|
case 0:
|
|
default:
|
|
return R.layout.task_adapter_row;
|
|
}
|
|
}
|
|
|
|
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
|
|
|
|
return new TaskAdapter(this, getTaskRowResource(),
|
|
cursor, sqlQueryTemplate, false,
|
|
new OnCompletedTaskListener() {
|
|
@Override
|
|
public void onCompletedTask(Task item, boolean newState) {
|
|
if (newState == true) {
|
|
onTaskCompleted(item);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public static final String TAGS_METADATA_JOIN = "for_tags"; //$NON-NLS-1$
|
|
|
|
public static final String USER_IMAGE_JOIN = "for_images"; // //$NON-NLS-1$
|
|
|
|
public static final String FILE_METADATA_JOIN = "for_actions"; //$NON-NLS-1$
|
|
|
|
|
|
/**
|
|
* Fill in the Task List with current items
|
|
*
|
|
* @param withCustomId
|
|
* force task with given custom id to be part of list
|
|
*/
|
|
public void setUpTaskList() {
|
|
if (filter == null) {
|
|
return;
|
|
}
|
|
|
|
TodorooCursor<Task> currentCursor = constructCursor();
|
|
if (currentCursor == null) {
|
|
return;
|
|
}
|
|
|
|
// set up list adapters
|
|
taskAdapter = createTaskAdapter(currentCursor);
|
|
|
|
setListAdapter(taskAdapter);
|
|
registerForContextMenu(getListView());
|
|
|
|
loadTaskListContent(true);
|
|
}
|
|
|
|
public Property<?>[] taskProperties() {
|
|
if (Preferences.getIntegerFromString(R.string.p_taskRowStyle_v2, 0) == 2) {
|
|
return TaskAdapter.BASIC_PROPERTIES;
|
|
}
|
|
return TaskAdapter.PROPERTIES;
|
|
}
|
|
|
|
public Filter getFilter() {
|
|
return filter;
|
|
}
|
|
|
|
private TodorooCursor<Task> constructCursor() {
|
|
String tagName = null;
|
|
if (getActiveTagData() != null) {
|
|
tagName = getActiveTagData().getValue(TagData.NAME);
|
|
}
|
|
|
|
Criterion tagsJoinCriterion = Criterion.and(
|
|
Field.field(TAGS_METADATA_JOIN + "." + Metadata.KEY.name).eq(TaskToTagMetadata.KEY), //$NON-NLS-1$
|
|
Field.field(TAGS_METADATA_JOIN + "." + Metadata.DELETION_DATE.name).eq(0),
|
|
Task.ID.eq(Field.field(TAGS_METADATA_JOIN + "." + Metadata.TASK.name)));
|
|
if (tagName != null) {
|
|
tagsJoinCriterion = Criterion.and(tagsJoinCriterion, Field.field(TAGS_METADATA_JOIN + "." + TaskToTagMetadata.TAG_NAME.name).neq(tagName));
|
|
}
|
|
|
|
// TODO: For now, we'll modify the query to join and include the things like tag data here.
|
|
// Eventually, we might consider restructuring things so that this query is constructed elsewhere.
|
|
String joinedQuery =
|
|
Join.left(Metadata.TABLE.as(TAGS_METADATA_JOIN),
|
|
tagsJoinCriterion).toString() //$NON-NLS-1$
|
|
+ Join.left(User.TABLE.as(USER_IMAGE_JOIN),
|
|
Task.USER_ID.eq(Field.field(USER_IMAGE_JOIN + "." + User.UUID.name))).toString()
|
|
+ Join.left(TaskAttachment.TABLE.as(FILE_METADATA_JOIN), Task.UUID.eq(Field.field(FILE_METADATA_JOIN + "." + TaskAttachment.TASK_UUID.name)))
|
|
+ filter.getSqlQuery();
|
|
|
|
sqlQueryTemplate.set(SortHelper.adjustQueryForFlagsAndSort(
|
|
joinedQuery, sortFlags, sortSort));
|
|
|
|
String groupedQuery;
|
|
if (sqlQueryTemplate.get().contains("GROUP BY")) {
|
|
groupedQuery = sqlQueryTemplate.get();
|
|
} else if (sqlQueryTemplate.get().contains("ORDER BY")) //$NON-NLS-1$
|
|
{
|
|
groupedQuery = sqlQueryTemplate.get().replace("ORDER BY", "GROUP BY " + Task.ID + " ORDER BY"); //$NON-NLS-1$
|
|
} else {
|
|
groupedQuery = sqlQueryTemplate.get() + " GROUP BY " + Task.ID;
|
|
}
|
|
sqlQueryTemplate.set(groupedQuery);
|
|
|
|
// Peform query
|
|
try {
|
|
return taskService.fetchFiltered(
|
|
sqlQueryTemplate.get(), null, taskProperties());
|
|
} catch (SQLiteException e) {
|
|
// We don't show this error anymore--seems like this can get triggered
|
|
// by a strange bug, but there seems to not be any negative side effect.
|
|
// For now, we'll suppress the error
|
|
// See http://astrid.com/home#tags-7tsoi/task-1119pk
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void reconstructCursor() {
|
|
TodorooCursor<Task> cursor = constructCursor();
|
|
if (cursor == null || taskAdapter == null) {
|
|
return;
|
|
}
|
|
taskAdapter.changeCursor(cursor);
|
|
}
|
|
|
|
/**
|
|
* Select a custom task id in the list. If it doesn't exist, create a new
|
|
* custom filter
|
|
*
|
|
* @param withCustomId
|
|
*/
|
|
public void selectCustomId(long withCustomId) {
|
|
// if already in the list, select it
|
|
TodorooCursor<Task> currentCursor = (TodorooCursor<Task>) taskAdapter.getCursor();
|
|
for (int i = 0; i < currentCursor.getCount(); i++) {
|
|
currentCursor.moveToPosition(i);
|
|
if (currentCursor.get(Task.ID) == withCustomId) {
|
|
getListView().setSelection(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======================================================================
|
|
* ============================================================== actions
|
|
* ======================================================================
|
|
*/
|
|
|
|
/**
|
|
* A task was completed from the task adapter
|
|
*
|
|
* @param item
|
|
* task that was completed
|
|
*/
|
|
protected void onTaskCompleted(Task item) {
|
|
if (isInbox) {
|
|
} else {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Comments button in action bar was clicked
|
|
*/
|
|
protected void handleCommentsButtonClicked() {
|
|
Activity activity = getActivity();
|
|
if (activity != null) {
|
|
Intent intent = new Intent(activity, CommentsActivity.class);
|
|
long id = 0;
|
|
TagData td = getActiveTagData();
|
|
if (td != null) {
|
|
id = td.getId();
|
|
}
|
|
intent.putExtra(TagViewFragment.EXTRA_TAG_DATA, id);
|
|
startActivity(intent);
|
|
AndroidUtilities.callOverridePendingTransition(activity, R.anim.slide_left_in, R.anim.slide_left_out);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCreateContextMenu(ContextMenu menu, View v,
|
|
ContextMenuInfo menuInfo) {
|
|
AdapterContextMenuInfo adapterInfo = (AdapterContextMenuInfo) menuInfo;
|
|
Task task = ((ViewHolder) adapterInfo.targetView.getTag()).task;
|
|
if (task.getValue(Task.IS_READONLY) > 0) {
|
|
return;
|
|
}
|
|
|
|
int id = (int) task.getId();
|
|
menu.setHeaderTitle(task.getValue(Task.TITLE));
|
|
|
|
if (task.isDeleted()) {
|
|
menu.add(id, CONTEXT_MENU_UNDELETE_TASK_ID, Menu.NONE,
|
|
R.string.TAd_contextUndeleteTask);
|
|
|
|
menu.add(id, CONTEXT_MENU_PURGE_TASK_ID, Menu.NONE,
|
|
R.string.TAd_contextPurgeTask);
|
|
} else {
|
|
menu.add(id, CONTEXT_MENU_EDIT_TASK_ID, Menu.NONE,
|
|
R.string.TAd_contextEditTask);
|
|
menu.add(id, CONTEXT_MENU_COPY_TASK_ID, Menu.NONE,
|
|
R.string.TAd_contextCopyTask);
|
|
|
|
for (int i = 0; i < contextItemExposers.length; i++) {
|
|
Object label = contextItemExposers[i].getLabel(task);
|
|
if (label != null) {
|
|
if (label instanceof Integer) {
|
|
menu.add(id, CONTEXT_MENU_PLUGIN_ID_FIRST + i,
|
|
Menu.NONE, (Integer) label);
|
|
} else {
|
|
menu.add(id, CONTEXT_MENU_PLUGIN_ID_FIRST + i,
|
|
Menu.NONE, (String) label);
|
|
}
|
|
}
|
|
}
|
|
|
|
long taskId = task.getId();
|
|
for (ContextMenuItem item : contextMenuExtensionLoader.getList()) {
|
|
android.view.MenuItem menuItem = menu.add(id,
|
|
CONTEXT_MENU_BROADCAST_INTENT_ID, Menu.NONE, item.title);
|
|
item.intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
|
|
menuItem.setIntent(item.intent);
|
|
}
|
|
|
|
menu.add(id, CONTEXT_MENU_DELETE_TASK_ID, Menu.NONE,
|
|
R.string.TAd_contextDeleteTask);
|
|
|
|
}
|
|
}
|
|
|
|
public boolean isInbox() {
|
|
return isInbox;
|
|
}
|
|
|
|
/** Show a dialog box and delete the task specified */
|
|
private void deleteTask(final Task task) {
|
|
new AlertDialog.Builder(getActivity()).setTitle(
|
|
R.string.DLG_confirm_title).setMessage(
|
|
R.string.DLG_delete_this_task_question).setIcon(
|
|
android.R.drawable.ic_dialog_alert).setPositiveButton(
|
|
android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
onTaskDelete(task);
|
|
taskService.delete(task);
|
|
loadTaskListContent(true);
|
|
}
|
|
}).setNegativeButton(android.R.string.cancel, null).show();
|
|
}
|
|
|
|
public void onTaskCreated(Task task) {
|
|
incrementFilterCount();
|
|
}
|
|
|
|
protected void onTaskDelete(Task task) {
|
|
decrementFilterCount();
|
|
|
|
Activity a = getActivity();
|
|
if (a instanceof AstridActivity) {
|
|
AstridActivity activity = (AstridActivity) a;
|
|
TaskEditFragment tef = activity.getTaskEditFragment();
|
|
if (tef != null) {
|
|
if (task.getId() == tef.model.getId()) {
|
|
tef.discardButtonClick();
|
|
}
|
|
}
|
|
}
|
|
TimerPlugin.updateTimer(ContextManager.getContext(), task, false);
|
|
}
|
|
|
|
public void incrementFilterCount() {
|
|
if (getActivity() instanceof TaskListActivity) {
|
|
((TaskListActivity) getActivity()).incrementFilterCount(filter);
|
|
}
|
|
}
|
|
|
|
public void decrementFilterCount() {
|
|
if (getActivity() instanceof TaskListActivity) {
|
|
((TaskListActivity) getActivity()).decrementFilterCount(filter);
|
|
}
|
|
}
|
|
|
|
public void refreshFilterCount() {
|
|
if (getActivity() instanceof TaskListActivity) {
|
|
((TaskListActivity) getActivity()).refreshFilterCount(filter);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onListItemClick(ListView l, View v, int position, long id) {
|
|
super.onListItemClick(l, v, position, id);
|
|
if (mDualFragments) {
|
|
setSelection(position);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onContextItemSelected(MenuItem item) {
|
|
return onOptionsItemSelected(item);
|
|
}
|
|
|
|
public boolean handleOptionsMenuItemSelected(int id, Intent intent) {
|
|
Activity activity = getActivity();
|
|
switch(id) {
|
|
case MENU_SORT_ID:
|
|
if (activity != null) {
|
|
AlertDialog dialog = SortSelectionActivity.createDialog(
|
|
getActivity(), hasDraggableOption(), this, sortFlags, sortSort);
|
|
dialog.show();
|
|
}
|
|
return true;
|
|
case MENU_SYNC_ID:
|
|
syncActionHelper.performSyncAction();
|
|
return true;
|
|
case MENU_ADDON_INTENT_ID:
|
|
if (activity != null) {
|
|
AndroidUtilities.startExternalIntent(activity, intent,
|
|
ACTIVITY_MENU_EXTERNAL);
|
|
}
|
|
return true;
|
|
case MENU_NEW_FILTER_ID:
|
|
if (activity != null) {
|
|
intent = new Intent(activity, CustomFilterActivity.class);
|
|
activity.startActivityForResult(intent, ACTIVITY_REQUEST_NEW_FILTER);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
|
Intent intent;
|
|
long itemId;
|
|
|
|
if (!isCurrentTaskListFragment()) {
|
|
return false;
|
|
}
|
|
|
|
// handle my own menus
|
|
if (handleOptionsMenuItemSelected(item.getItemId(), item.getIntent())) {
|
|
return true;
|
|
}
|
|
|
|
switch (item.getItemId()) {
|
|
// --- context menu items
|
|
|
|
case CONTEXT_MENU_BROADCAST_INTENT_ID: {
|
|
intent = item.getIntent();
|
|
getActivity().sendBroadcast(intent);
|
|
return true;
|
|
}
|
|
case CONTEXT_MENU_EDIT_TASK_ID: {
|
|
itemId = item.getGroupId();
|
|
mListener.onTaskListItemClicked(itemId);
|
|
return true;
|
|
}
|
|
case CONTEXT_MENU_COPY_TASK_ID: {
|
|
itemId = item.getGroupId();
|
|
duplicateTask(itemId);
|
|
return true;
|
|
}
|
|
case CONTEXT_MENU_DELETE_TASK_ID: {
|
|
itemId = item.getGroupId();
|
|
Task task = taskService.fetchById(itemId, Task.ID, Task.UUID);
|
|
if (task != null) {
|
|
deleteTask(task);
|
|
}
|
|
return true;
|
|
}
|
|
case CONTEXT_MENU_UNDELETE_TASK_ID: {
|
|
itemId = item.getGroupId();
|
|
Task task = new Task();
|
|
task.setId(itemId);
|
|
task.setValue(Task.DELETION_DATE, 0L);
|
|
taskService.save(task);
|
|
loadTaskListContent(true);
|
|
return true;
|
|
}
|
|
case CONTEXT_MENU_PURGE_TASK_ID: {
|
|
itemId = item.getGroupId();
|
|
Task task = new Task();
|
|
task.setId(itemId);
|
|
TimerPlugin.updateTimer(getActivity(), task, false);
|
|
taskService.purge(itemId);
|
|
loadTaskListContent(true);
|
|
return true;
|
|
}
|
|
default: {
|
|
if (item.getItemId() < CONTEXT_MENU_PLUGIN_ID_FIRST) {
|
|
return false;
|
|
}
|
|
if (item.getItemId() - CONTEXT_MENU_PLUGIN_ID_FIRST >= contextItemExposers.length) {
|
|
return false;
|
|
}
|
|
|
|
AdapterContextMenuInfo adapterInfo = (AdapterContextMenuInfo) item.getMenuInfo();
|
|
Task task = ((ViewHolder) adapterInfo.targetView.getTag()).task;
|
|
contextItemExposers[item.getItemId() - CONTEXT_MENU_PLUGIN_ID_FIRST].invoke(task);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void duplicateTask(long itemId) {
|
|
long cloneId = taskService.duplicateTask(itemId);
|
|
|
|
Intent intent = new Intent(getActivity(), TaskEditActivity.class);
|
|
intent.putExtra(TaskEditFragment.TOKEN_ID, cloneId);
|
|
intent.putExtra(TOKEN_FILTER, filter);
|
|
getActivity().startActivityForResult(intent, ACTIVITY_EDIT_TASK);
|
|
transitionForTaskEdit();
|
|
}
|
|
|
|
public void showSettings() {
|
|
Activity activity = getActivity();
|
|
if (activity == null) {
|
|
return;
|
|
}
|
|
|
|
Intent intent = new Intent(activity, EditPreferences.class);
|
|
startActivityForResult(intent, ACTIVITY_SETTINGS);
|
|
}
|
|
|
|
public void onTaskListItemClicked(long taskId, boolean editable) {
|
|
mListener.onTaskListItemClicked(taskId, editable);
|
|
}
|
|
|
|
protected void toggleDragDrop(boolean newState) {
|
|
extras.putParcelable(TOKEN_FILTER, filter);
|
|
if(newState) {
|
|
((AstridActivity) getActivity()).setupTasklistFragmentWithFilterAndCustomTaskList(filter,
|
|
extras, SubtasksListFragment.class);
|
|
} else {
|
|
filter.setFilterQueryOverride(null);
|
|
((AstridActivity)getActivity()).setupTasklistFragmentWithFilterAndCustomTaskList(filter,
|
|
extras, TaskListFragment.class);
|
|
}
|
|
}
|
|
|
|
protected boolean isDraggable() {
|
|
return false;
|
|
}
|
|
|
|
protected boolean hasDraggableOption() {
|
|
return isInbox || isTodayFilter;
|
|
}
|
|
|
|
@Override
|
|
public void onSortSelected(boolean always, int flags, int sort) {
|
|
boolean manualSettingChanged = SortHelper.isManualSort(sortFlags) !=
|
|
SortHelper.isManualSort(flags);
|
|
|
|
sortFlags = flags;
|
|
sortSort = sort;
|
|
|
|
if (always) {
|
|
SharedPreferences publicPrefs = AstridPreferences.getPublicPrefs(ContextManager.getContext());
|
|
if (publicPrefs != null) {
|
|
Editor editor = publicPrefs.edit();
|
|
if (editor != null) {
|
|
editor.putInt(SortHelper.PREF_SORT_FLAGS, flags);
|
|
editor.putInt(SortHelper.PREF_SORT_SORT, sort);
|
|
editor.commit();
|
|
TasksWidget.updateWidgets(ContextManager.getContext());
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
if(manualSettingChanged) {
|
|
toggleDragDrop(SortHelper.isManualSort(sortFlags));
|
|
} else {
|
|
setUpTaskList();
|
|
}
|
|
} catch (IllegalStateException e) {
|
|
// TODO: Fragment got detached somehow (rare)
|
|
}
|
|
}
|
|
}
|