diff --git a/build.gradle b/build.gradle index e41acec20..c58255a49 100644 --- a/build.gradle +++ b/build.gradle @@ -129,6 +129,7 @@ dependencies { compile 'com.jakewharton:process-phoenix:1.1.0' compile 'com.google.android.apps.dashclock:dashclock-api:2.0.0' compile 'com.twofortyfouram:android-plugin-api-for-locale:1.0.2' + compile 'com.bignerdranch.android:recyclerview-multiselect:0.2' compile ('com.rubiconproject.oss:jchronic:0.2.6') { transitive = false } diff --git a/src/googleplay/java/org/tasks/activities/GoogleTaskListSettingsActivity.java b/src/googleplay/java/org/tasks/activities/GoogleTaskListSettingsActivity.java index 9b6821a7e..65c6a342f 100644 --- a/src/googleplay/java/org/tasks/activities/GoogleTaskListSettingsActivity.java +++ b/src/googleplay/java/org/tasks/activities/GoogleTaskListSettingsActivity.java @@ -58,8 +58,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi public static final String EXTRA_STORE_DATA = "extra_store_data"; public static final String ACTION_DELETED = "action_deleted"; - public static final String ACTION_RENAMED = "action_renamed"; - public static final String ACTION_THEME_CHANGED = "action_theme_changed"; + public static final String ACTION_RELOAD = "action_reload"; private boolean isNewList; private GtasksList gtasksList; @@ -174,7 +173,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi if (colorChanged()) { gtasksList.setColor(selectedTheme); storeObjectDao.persist(gtasksList); - setResult(RESULT_OK, new Intent(ACTION_THEME_CHANGED).putExtra(TaskListActivity.OPEN_FILTER, new GtasksFilter(gtasksList))); + setResult(RESULT_OK, new Intent(ACTION_RELOAD).putExtra(TaskListActivity.OPEN_FILTER, new GtasksFilter(gtasksList))); } finish(); } @@ -269,7 +268,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi gtasksList.setName(taskList.getTitle()); gtasksList.setColor(selectedTheme); storeObjectDao.persist(gtasksList); - setResult(RESULT_OK, new Intent(ACTION_RENAMED).putExtra(TaskListActivity.OPEN_FILTER, new GtasksFilter(gtasksList))); + setResult(RESULT_OK, new Intent(ACTION_RELOAD).putExtra(TaskListActivity.OPEN_FILTER, new GtasksFilter(gtasksList))); finish(); } @@ -302,6 +301,6 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi color.setText(themeColor.getName()); } themeColor.apply(toolbar); - themeColor.applyStatusBarColor(this); + themeColor.applyToStatusBar(this); } } diff --git a/src/googleplay/java/org/tasks/tasklist/GtasksListFragment.java b/src/googleplay/java/org/tasks/tasklist/GtasksListFragment.java index e516363f4..a3755dddd 100644 --- a/src/googleplay/java/org/tasks/tasklist/GtasksListFragment.java +++ b/src/googleplay/java/org/tasks/tasklist/GtasksListFragment.java @@ -7,6 +7,7 @@ import android.view.MenuItem; import com.todoroo.astrid.activity.TaskListActivity; import com.todoroo.astrid.activity.TaskListFragment; +import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.GtasksFilter; import com.todoroo.astrid.data.StoreObject; import com.todoroo.astrid.gtasks.GtasksList; @@ -75,9 +76,10 @@ public class GtasksListFragment extends TaskListFragment { String action = data.getAction(); if (GoogleTaskListSettingsActivity.ACTION_DELETED.equals(action)) { activity.onFilterItemClicked(null); - } else if (GoogleTaskListSettingsActivity.ACTION_RENAMED.equals(action) || - GoogleTaskListSettingsActivity.ACTION_THEME_CHANGED.equals(action)) { - activity.onFilterItemClicked(data.getParcelableExtra(TaskListActivity.OPEN_FILTER)); + } else if (GoogleTaskListSettingsActivity.ACTION_RELOAD.equals(action)) { + activity.getIntent().putExtra(TaskListActivity.OPEN_FILTER, + (Filter) data.getParcelableExtra(TaskListActivity.OPEN_FILTER)); + activity.recreate(); } } } else { diff --git a/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java b/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java index 27868ed04..0f3a5e9b9 100644 --- a/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java +++ b/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java @@ -11,9 +11,11 @@ import android.content.IntentFilter; import android.content.res.Configuration; import android.os.Bundle; import android.provider.Settings; +import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.FragmentManager; import android.support.v4.widget.DrawerLayout; +import android.support.v7.view.ActionMode; import android.view.View; import android.view.inputmethod.InputMethodManager; @@ -68,7 +70,9 @@ import butterknife.BindView; import butterknife.ButterKnife; import timber.log.Timber; +import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop; import static com.todoroo.astrid.activity.TaskEditFragment.newTaskEditFragment; +import static org.tasks.tasklist.ActionUtils.applySupportActionModeColor; import static org.tasks.ui.NavigationDrawerFragment.OnFilterItemClickedListener; public class TaskListActivity extends InjectingAppCompatActivity implements @@ -110,6 +114,7 @@ public class TaskListActivity extends InjectingAppCompatActivity implements private int currentNightMode; private Filter filter; + private ActionMode actionMode = null; /** * @see android.app.Activity#onCreate(Bundle) @@ -180,13 +185,15 @@ public class TaskListActivity extends InjectingAppCompatActivity implements } private void loadTaskListFragment(TaskListFragment taskListFragment) { + finishActionMode(); + filter = taskListFragment.filter; - ThemeColor themeColor = filter.tint >= 0 - ? themeCache.getThemeColor(filter.tint) - : theme.getThemeColor(); - themeColor.applyStatusBarColor(drawerLayout); - themeColor.applyTaskDescription(this, filter.listingTitle); - theme.withColor(themeColor).applyToContext(this); + ThemeColor filterColor = getFilterColor(); + + filterColor.applyToStatusBar(drawerLayout); + + filterColor.applyTaskDescription(this, filter.listingTitle); + filterColor.applyStyle(getTheme()); FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); @@ -196,7 +203,15 @@ public class TaskListActivity extends InjectingAppCompatActivity implements .commit(); } + private ThemeColor getFilterColor() { + return filter != null && filter.tint >= 0 + ? themeCache.getThemeColor(filter.tint) + : theme.getThemeColor(); + } + private void loadTaskEditFragment(TaskEditFragment taskEditFragment) { + finishActionMode(); + getSupportFragmentManager() .beginTransaction() .replace(isDoublePaneLayout() ? R.id.detail_dual : R.id.single_pane, taskEditFragment, TaskEditFragment.TAG_TASKEDIT_FRAGMENT) @@ -447,6 +462,7 @@ public class TaskListActivity extends InjectingAppCompatActivity implements @Override public void taskEditFinished() { getSupportFragmentManager().popBackStackImmediate(TaskEditFragment.TAG_TASKEDIT_FRAGMENT, FragmentManager.POP_BACK_STACK_INCLUSIVE); + getTaskListFragment().clearSelections(); hideKeyboard(); } @@ -484,4 +500,33 @@ public class TaskListActivity extends InjectingAppCompatActivity implements public void selectedList(GtasksList list) { getTaskEditFragment().onGoogleTaskListChanged(list); } + + @Override + public void onSupportActionModeStarted(@NonNull ActionMode mode) { + super.onSupportActionModeStarted(mode); + + actionMode = mode; + + ThemeColor filterColor = getFilterColor(); + + applySupportActionModeColor(filterColor, mode); + + filterColor.setStatusBarColor(this); + } + + @Override + public void onSupportActionModeFinished(@NonNull ActionMode mode) { + super.onSupportActionModeFinished(mode); + + if (atLeastLollipop()) { + getWindow().setStatusBarColor(0); + } + } + + private void finishActionMode() { + if (actionMode != null) { + actionMode.finish(); + actionMode = null; + } + } } diff --git a/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java b/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java index 2a5ee033d..6c938f98b 100644 --- a/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java +++ b/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java @@ -12,6 +12,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.Snackbar; import android.support.v4.view.MenuItemCompat; @@ -20,14 +21,11 @@ import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView.AdapterContextMenuInfo; import com.todoroo.andlib.data.Callback; import com.todoroo.andlib.data.Property; @@ -40,7 +38,6 @@ import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.CustomFilter; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.core.BuiltInFilterExposer; -import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.gtasks.GtasksPreferenceService; import com.todoroo.astrid.service.TaskCreator; @@ -63,12 +60,13 @@ import org.tasks.injection.FragmentComponent; import org.tasks.injection.InjectingFragment; import org.tasks.preferences.Preferences; import org.tasks.tasklist.TaskListRecyclerAdapter; -import org.tasks.tasklist.ViewHolder; import org.tasks.tasklist.ViewHolderFactory; import org.tasks.ui.CheckBoxes; import org.tasks.ui.MenuColorizer; import org.tasks.ui.ProgressDialogAsyncTask; +import java.util.List; + import javax.inject.Inject; import butterknife.BindView; @@ -100,11 +98,6 @@ public class TaskListFragment extends InjectingFragment implements public static final int VOICE_RECOGNITION_REQUEST_CODE = 1234; private static final int REQUEST_EDIT_FILTER = 11544; - // --- menu codes - - private static final int CONTEXT_MENU_COPY_TASK_ID = R.string.TAd_contextCopyTask; - private static final int CONTEXT_MENU_DELETE_TASK_ID = R.string.TAd_contextDeleteTask; - // --- instance variables @Inject SyncAdapterHelper syncAdapterHelper; @@ -120,7 +113,6 @@ public class TaskListFragment extends InjectingFragment implements @Inject Broadcaster broadcaster; @Inject protected TaskListDataProvider taskListDataProvider; @Inject TimerPlugin timerPlugin; - @Inject TaskDao taskDao; @Inject ViewHolderFactory viewHolderFactory; @Inject protected Tracker tracker; @@ -160,6 +152,15 @@ public class TaskListFragment extends InjectingFragment implements } } + @Override + public void onViewStateRestored(@Nullable Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + + if (savedInstanceState != null) { + recyclerAdapter.restoreSaveState(savedInstanceState); + } + } + /** * Container Activity must implement this interface and we ensure that it * does during the onAttach() callback @@ -203,6 +204,7 @@ public class TaskListFragment extends InjectingFragment implements super.onSaveInstanceState(outState); outState.putParcelable(EXTRA_FILTER, filter); + outState.putAll(recyclerAdapter.getSaveState()); } @Override @@ -320,7 +322,7 @@ public class TaskListFragment extends InjectingFragment implements @Override protected int getResultResource() { - return R.string.EPr_manage_delete_completed_status; + return R.string.delete_multiple_tasks_confirmation; } }.execute(); } @@ -343,8 +345,6 @@ public class TaskListFragment extends InjectingFragment implements @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - // We have a menu item to show in action bar. -// registerForContextMenu(recyclerView); filter.setFilterQueryOverride(null); @@ -352,12 +352,6 @@ public class TaskListFragment extends InjectingFragment implements recyclerView.setLayoutManager(new LinearLayoutManager(context)); loadTaskListContent(); - - if (getResources().getBoolean(R.bool.two_pane_layout)) { - // In dual-pane mode, the list view highlights the selected item. -// listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); -// listView.setItemsCanFocus(false); - } } @Override @@ -465,7 +459,8 @@ public class TaskListFragment extends InjectingFragment implements // set up list adapters taskAdapter = createTaskAdapter(currentCursor); - recyclerAdapter = new TaskListRecyclerAdapter(context, taskAdapter, viewHolderFactory, this); + recyclerAdapter = new TaskListRecyclerAdapter(getActivity(), taskAdapter, viewHolderFactory, + this, taskDeleter, taskDuplicator, tracker); } public Property[] taskProperties() { @@ -490,32 +485,22 @@ public class TaskListFragment extends InjectingFragment implements * ====================================================================== */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - AdapterContextMenuInfo adapterInfo = (AdapterContextMenuInfo) menuInfo; - Task task = ((ViewHolder) adapterInfo.targetView.getTag()).task; - int id = (int) task.getId(); - menu.setHeaderTitle(task.getTitle()); - - menu.add(id, CONTEXT_MENU_COPY_TASK_ID, Menu.NONE, R.string.TAd_contextCopyTask); - menu.add(id, CONTEXT_MENU_DELETE_TASK_ID, Menu.NONE, R.string.TAd_contextDeleteTask); - } - - /** Show a dialog box and delete the task specified */ - private void deleteTask(final Task task) { - dialogBuilder.newMessageDialog(R.string.delete_tag_confirmation, task.getTitle()) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { - onTaskDelete(task); - taskDeleter.delete(task); - loadTaskListContent(); - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); + public void onTaskCreated(List tasks) { + for (Task task : tasks) { + onTaskCreated(task.getUuid()); + } + syncAdapterHelper.requestSynchronization(); } public void onTaskCreated(String uuid) { } + public void onTaskDelete(List tasks) { + for (Task task : tasks) { + onTaskDelete(task); + } + } + protected void onTaskDelete(Task task) { TaskListActivity activity = (TaskListActivity) getActivity(); TaskEditFragment tef = activity.getTaskEditFragment(); @@ -527,14 +512,6 @@ public class TaskListFragment extends InjectingFragment implements timerPlugin.stopTimer(task); } -// @Override -// public void onListItemClick(ListView l, View v, int position, long id) { -// super.onListItemClick(l, v, position, id); -// if (getResources().getBoolean(R.bool.two_pane_layout)) { -// setSelection(position); -// } -// } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == VOICE_RECOGNITION_REQUEST_CODE) { @@ -551,14 +528,14 @@ public class TaskListFragment extends InjectingFragment implements } else if (requestCode == REQUEST_EDIT_FILTER) { if (resultCode == Activity.RESULT_OK) { String action = data.getAction(); - if (FilterSettingsActivity.ACTION_FILTER_RENAMED.equals(action)) { - CustomFilter customFilter = data.getParcelableExtra(FilterSettingsActivity.TOKEN_FILTER); - ((TaskListActivity) getActivity()).onFilterItemClicked(customFilter); - } else if(FilterSettingsActivity.ACTION_FILTER_DELETED.equals(action)) { - ((TaskListActivity) getActivity()).onFilterItemClicked(null); + TaskListActivity activity = (TaskListActivity) getActivity(); + if (FilterSettingsActivity.ACTION_FILTER_DELETED.equals(action)) { + activity.onFilterItemClicked(null); + } else if(FilterSettingsActivity.ACTION_FILTER_RENAMED.equals(action)) { + activity.getIntent().putExtra(TaskListActivity.OPEN_FILTER, + (Filter) data.getParcelableExtra(FilterSettingsActivity.TOKEN_FILTER)); + activity.recreate(); } - - broadcaster.refresh(); } } else { super.onActivityResult(requestCode, resultCode, data); @@ -569,33 +546,6 @@ public class TaskListFragment extends InjectingFragment implements public boolean onContextItemSelected(android.view.MenuItem item) { return onOptionsItemSelected(item); } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - long itemId; - - switch (item.getItemId()) { - case CONTEXT_MENU_COPY_TASK_ID: - itemId = item.getGroupId(); - duplicateTask(itemId); - return true; - case CONTEXT_MENU_DELETE_TASK_ID: - itemId = item.getGroupId(); - Task task = taskDao.fetch(itemId, Task.ID, Task.TITLE, Task.UUID); - if (task != null) { - deleteTask(task); - } - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private void duplicateTask(long itemId) { - long cloneId = taskDuplicator.duplicateTask(itemId); - onTaskListItemClicked(cloneId); - } - public void onTaskListItemClicked(long taskId) { callbacks.onTaskListItemClicked(taskId); } @@ -603,4 +553,8 @@ public class TaskListFragment extends InjectingFragment implements protected boolean hasDraggableOption() { return BuiltInFilterExposer.isInbox(context, filter) || BuiltInFilterExposer.isTodayFilter(context, filter); } + + public void clearSelections() { + recyclerAdapter.clearSelections(); + } } diff --git a/src/main/java/com/todoroo/astrid/service/TaskDeleter.java b/src/main/java/com/todoroo/astrid/service/TaskDeleter.java index 06e346f78..e43f6cf87 100644 --- a/src/main/java/com/todoroo/astrid/service/TaskDeleter.java +++ b/src/main/java/com/todoroo/astrid/service/TaskDeleter.java @@ -6,15 +6,11 @@ import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.Task; -import org.tasks.Broadcaster; -import org.tasks.calendars.CalendarEventProvider; - import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import static com.google.common.collect.Iterables.transform; import static com.todoroo.andlib.sql.Criterion.all; import static com.todoroo.astrid.dao.TaskDao.TaskCriteria.isVisible; import static com.todoroo.astrid.dao.TaskDao.TaskCriteria.notCompleted; @@ -22,14 +18,10 @@ import static com.todoroo.astrid.dao.TaskDao.TaskCriteria.notCompleted; public class TaskDeleter { private final TaskDao taskDao; - private final CalendarEventProvider calendarEventProvider; - private final Broadcaster broadcaster; @Inject - public TaskDeleter(TaskDao taskDao, CalendarEventProvider calendarEventProvider, Broadcaster broadcaster) { + public TaskDeleter(TaskDao taskDao) { this.taskDao = taskDao; - this.calendarEventProvider = calendarEventProvider; - this.broadcaster = broadcaster; } /** @@ -54,22 +46,31 @@ public class TaskDeleter { taskDao.delete(item.getId()); item.setId(Task.NO_ID); } else { - long id = item.getId(); - item.clear(); - item.setId(id); - item.setDeletionDate(DateUtilities.now()); - taskDao.save(item); + Task template = new Task(); + template.setId(item.getId()); + template.setDeletionDate(DateUtilities.now()); + taskDao.save(template); + } + } + + public int delete(List tasks) { + return markDeleted(tasks); + } + + public void undelete(List tasks) { + for (Task task : tasks) { + Task template = new Task(); + template.setId(task.getId()); + template.setDeletionDate(0L); + taskDao.save(template); } } private int markDeleted(List tasks) { - Task template = new Task(); - template.setDeletionDate(DateUtilities.now()); - int count = taskDao.update(Task.ID.in(transform(tasks, Task::getId)), template); - if (count > 0) { - broadcaster.refresh(); + for (Task task : tasks) { + delete(task); } - return count; + return tasks.size(); } public int clearCompleted(Filter filter) { diff --git a/src/main/java/com/todoroo/astrid/service/TaskDuplicator.java b/src/main/java/com/todoroo/astrid/service/TaskDuplicator.java index 73b70827b..4ebda08eb 100644 --- a/src/main/java/com/todoroo/astrid/service/TaskDuplicator.java +++ b/src/main/java/com/todoroo/astrid/service/TaskDuplicator.java @@ -10,6 +10,9 @@ import com.todoroo.astrid.gcal.GCalHelper; import com.todoroo.astrid.gtasks.GtasksMetadata; import com.todoroo.astrid.tags.TaskToTagMetadata; +import org.tasks.Broadcaster; + +import java.util.ArrayList; import java.util.List; import javax.inject.Inject; @@ -21,21 +24,27 @@ public class TaskDuplicator { private final GCalHelper gcalHelper; private final MetadataDao metadataDao; private final TaskDao taskDao; + private final Broadcaster broadcaster; @Inject - public TaskDuplicator(GCalHelper gcalHelper, MetadataDao metadataDao, TaskDao taskDao) { + public TaskDuplicator(GCalHelper gcalHelper, MetadataDao metadataDao, TaskDao taskDao, + Broadcaster broadcaster) { this.gcalHelper = gcalHelper; this.metadataDao = metadataDao; this.taskDao = taskDao; + this.broadcaster = broadcaster; } - /** - * Create an uncompleted copy of this task and edit it - * @return cloned item id - */ - public long duplicateTask(long itemId) { - Task original = taskDao.fetch(itemId, Task.PROPERTIES); - Timber.d("Cloning %s", original); + public List duplicate(List tasks) { + List result = new ArrayList<>(); + for (Task task : tasks) { + result.add(clone(taskDao.fetch(task.getId(), Task.PROPERTIES), true)); + } + broadcaster.refresh(); + return result; + } + + private Task clone(Task original, boolean suppressRefresh) { Task clone = new Task(original); clone.setCreationDate(DateUtilities.now()); clone.setCompletionDate(0L); @@ -44,10 +53,13 @@ public class TaskDuplicator { clone.clearValue(Task.ID); clone.clearValue(Task.UUID); - List metadataList = metadataDao.byTask(itemId); + List metadataList = metadataDao.byTask(original.getId()); if (!metadataList.isEmpty()) { clone.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); } + if (suppressRefresh) { + clone.putTransitory(TaskDao.TRANS_SUPPRESS_REFRESH, true); + } taskDao.save(clone); @@ -56,19 +68,21 @@ public class TaskDuplicator { continue; } Timber.d("Cloning %s", oldMetadata); - Metadata metadata = new Metadata(oldMetadata); - if(GtasksMetadata.METADATA_KEY.equals(metadata.getKey())) { - metadata.setValue(GtasksMetadata.ID, ""); //$NON-NLS-1$ - } else if (TaskToTagMetadata.KEY.equals(metadata.getKey())) { + if(GtasksMetadata.METADATA_KEY.equals(oldMetadata.getKey())) { + Metadata gtaskMetadata = GtasksMetadata.createEmptyMetadataWithoutList(clone.getId()); + gtaskMetadata.setValue(GtasksMetadata.LIST_ID, oldMetadata.getValue(GtasksMetadata.LIST_ID)); + metadataDao.createNew(gtaskMetadata); + } else if (TaskToTagMetadata.KEY.equals(oldMetadata.getKey())) { + Metadata metadata = new Metadata(oldMetadata); metadata.setValue(TaskToTagMetadata.TASK_UUID, clone.getUuid()); + metadata.setTask(clone.getId()); + metadata.clearValue(Metadata.ID); + metadataDao.createNew(metadata); } - metadata.setTask(clone.getId()); - metadata.clearValue(Metadata.ID); - metadataDao.createNew(metadata); } gcalHelper.createTaskEventIfEnabled(clone); - return clone.getId(); + return clone; } } diff --git a/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java b/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java index 2c6822dc2..c520a4f89 100644 --- a/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java +++ b/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java @@ -44,7 +44,7 @@ public class TagFilterExposer { } /** Create filter from new tag object */ - public static TagFilter filterFromTag(TagData tag) { + private static TagFilter filterFromTag(TagData tag) { if (tag == null || Strings.isNullOrEmpty(tag.getName())) { return null; } diff --git a/src/main/java/org/tasks/activities/TagSettingsActivity.java b/src/main/java/org/tasks/activities/TagSettingsActivity.java index 1edbc7638..b4ecc9329 100644 --- a/src/main/java/org/tasks/activities/TagSettingsActivity.java +++ b/src/main/java/org/tasks/activities/TagSettingsActivity.java @@ -18,12 +18,12 @@ import android.view.inputmethod.InputMethodManager; import com.todoroo.andlib.sql.Criterion; import com.todoroo.astrid.activity.TaskListActivity; +import com.todoroo.astrid.api.TagFilter; import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.helper.UUIDHelper; -import com.todoroo.astrid.tags.TagFilterExposer; import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TaskToTagMetadata; @@ -58,8 +58,8 @@ public class TagSettingsActivity extends ThemedInjectingAppCompatActivity implem public static final String EXTRA_TAG_DATA = "tagData"; //$NON-NLS-1$ public static final String EXTRA_TAG_UUID = "uuid"; //$NON-NLS-1$ - public static final String ACTION_TAG_RENAMED = "tagRenamed"; - public static final String ACTION_TAG_DELETED = "tagDeleted"; + public static final String ACTION_RELOAD = "tagRenamed"; + public static final String ACTION_DELETED = "tagDeleted"; private boolean isNewTag; private TagData tagData; @@ -190,7 +190,7 @@ public class TagSettingsActivity extends ThemedInjectingAppCompatActivity implem tagData.setName(newName); tagData.setColor(selectedTheme); tagDataDao.persist(tagData); - setResult(RESULT_OK, new Intent().putExtra(TaskListActivity.OPEN_FILTER, TagFilterExposer.filterFromTag(tagData))); + setResult(RESULT_OK, new Intent().putExtra(TaskListActivity.OPEN_FILTER, new TagFilter(tagData))); } else if (hasChanges()) { tagData.setName(newName); tagData.setColor(selectedTheme); @@ -201,7 +201,7 @@ public class TagSettingsActivity extends ThemedInjectingAppCompatActivity implem metadataDao.update(Criterion.and( MetadataDao.MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_UUID.eq(tagData.getUUID())), m); - setResult(RESULT_OK, new Intent(ACTION_TAG_RENAMED).putExtra(EXTRA_TAG_UUID, tagData.getUuid())); + setResult(RESULT_OK, new Intent(ACTION_RELOAD).putExtra(TaskListActivity.OPEN_FILTER, new TagFilter(tagData))); } finish(); @@ -251,7 +251,7 @@ public class TagSettingsActivity extends ThemedInjectingAppCompatActivity implem String uuid = tagData.getUuid(); metadataDao.deleteWhere(Criterion.and(MetadataDao.MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_UUID.eq(uuid))); tagDataDao.delete(tagData.getId()); - setResult(RESULT_OK, new Intent(ACTION_TAG_DELETED).putExtra(EXTRA_TAG_UUID, uuid)); + setResult(RESULT_OK, new Intent(ACTION_DELETED).putExtra(EXTRA_TAG_UUID, uuid)); } finish(); }) @@ -280,7 +280,7 @@ public class TagSettingsActivity extends ThemedInjectingAppCompatActivity implem color.setText(themeColor.getName()); } themeColor.apply(toolbar); - themeColor.applyStatusBarColor(this); + themeColor.applyToStatusBar(this); } @Override diff --git a/src/main/java/org/tasks/analytics/Tracking.java b/src/main/java/org/tasks/analytics/Tracking.java index 4cf03aefb..81334f70c 100644 --- a/src/main/java/org/tasks/analytics/Tracking.java +++ b/src/main/java/org/tasks/analytics/Tracking.java @@ -22,6 +22,8 @@ public class Tracking { GTASK_DELETE_LIST(R.string.tracking_category_google_tasks, R.string.tracking_action_delete_list), GTASK_SET_COLOR(R.string.tracking_category_google_tasks, R.string.p_theme_color), GTASK_CLEAR_COMPLETED(R.string.tracking_category_google_tasks, R.string.tracking_action_clear_completed), + MULTISELECT_DELETE(R.string.tracking_category_event, R.string.tracking_event_multiselect_delete), + MULTISELECT_CLONE(R.string.tracking_category_event, R.string.tracking_event_multiselect_clone), CLEAR_COMPLETED(R.string.tracking_category_event, R.string.tracking_action_clear_completed), UPGRADE(R.string.tracking_category_event, R.string.tracking_event_upgrade), LEGACY_TASKER_TRIGGER(R.string.tracking_category_event, R.string.tracking_event_legacy_tasker_trigger), diff --git a/src/main/java/org/tasks/preferences/ResourceResolver.java b/src/main/java/org/tasks/preferences/ResourceResolver.java index 6b5a74b2f..f3088746d 100644 --- a/src/main/java/org/tasks/preferences/ResourceResolver.java +++ b/src/main/java/org/tasks/preferences/ResourceResolver.java @@ -10,4 +10,10 @@ public class ResourceResolver { context.getTheme().resolveAttribute(attr, typedValue, true); return typedValue.data; } + + @Deprecated public static int getResourceId(Context context, int attr) { + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(attr, typedValue, true); + return typedValue.resourceId; + } } diff --git a/src/main/java/org/tasks/tasklist/ActionUtils.java b/src/main/java/org/tasks/tasklist/ActionUtils.java new file mode 100644 index 000000000..91fa43a22 --- /dev/null +++ b/src/main/java/org/tasks/tasklist/ActionUtils.java @@ -0,0 +1,71 @@ +package org.tasks.tasklist; + +import android.support.v7.app.WindowDecorActionBar; +import android.support.v7.view.StandaloneActionMode; +import android.support.v7.widget.ActionBarContextView; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import org.tasks.R; +import org.tasks.themes.ThemeColor; + +import java.lang.reflect.Field; + +public class ActionUtils { + // cribbed from Twittnuker + public static void applySupportActionModeColor(ThemeColor themeColor, final android.support.v7.view.ActionMode modeCompat) { + // Very dirty implementation + // This call ensures TitleView created + modeCompat.setTitle(modeCompat.getTitle()); + View contextView = null; + if (modeCompat instanceof WindowDecorActionBar.ActionModeImpl) { + WindowDecorActionBar actionBar = (WindowDecorActionBar) findFieldOfTypes(modeCompat, + WindowDecorActionBar.ActionModeImpl.class, WindowDecorActionBar.class); + if (actionBar == null) { + return; + } + contextView = (View) findFieldOfTypes(actionBar, WindowDecorActionBar.class, ActionBarContextView.class); + } else if (modeCompat instanceof StandaloneActionMode) { + contextView = (View) findFieldOfTypes(modeCompat, StandaloneActionMode.class, ActionBarContextView.class); + } + if (!(contextView instanceof ActionBarContextView)) { + return; + } + + contextView.setBackgroundColor(themeColor.getPrimaryColor()); + + TextView title = (TextView) contextView.findViewById(R.id.action_bar_title); + if (title != null) { + title.setTextColor(themeColor.getActionBarTint()); + } + + ImageView closeButton = (ImageView) contextView.findViewById(R.id.action_mode_close_button); + if (closeButton != null) { + closeButton.setColorFilter(themeColor.getActionBarTint()); + } + } + + private static Object findFieldOfTypes(T obj, Class cls, Class... checkTypes) { + labelField: + for (Field field : cls.getDeclaredFields()) { + field.setAccessible(true); + final Object fieldObj; + try { + fieldObj = field.get(obj); + } catch (Exception ignore) { + continue; + } + if (fieldObj != null) { + final Class type = fieldObj.getClass(); + for (Class checkType : checkTypes) { + if (!checkType.isAssignableFrom(type)) { + continue labelField; + } + } + return fieldObj; + } + } + return null; + } +} diff --git a/src/main/java/org/tasks/tasklist/TagListFragment.java b/src/main/java/org/tasks/tasklist/TagListFragment.java index fbf05cd09..d6f2d80bf 100644 --- a/src/main/java/org/tasks/tasklist/TagListFragment.java +++ b/src/main/java/org/tasks/tasklist/TagListFragment.java @@ -10,17 +10,12 @@ import com.todoroo.astrid.activity.TaskListActivity; import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.TagFilter; -import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.data.TagData; -import com.todoroo.astrid.tags.TagFilterExposer; -import org.tasks.Broadcaster; import org.tasks.R; import org.tasks.activities.TagSettingsActivity; import org.tasks.injection.FragmentComponent; -import javax.inject.Inject; - public class TagListFragment extends TaskListFragment { private static final int REQUEST_EDIT_TAG = 11543; @@ -34,9 +29,6 @@ public class TagListFragment extends TaskListFragment { private static final String EXTRA_TAG_DATA = "extra_tag_data"; - @Inject TagDataDao tagDataDao; - @Inject Broadcaster broadcaster; - protected TagData tagData; @Override @@ -72,24 +64,14 @@ public class TagListFragment extends TaskListFragment { if (requestCode == REQUEST_EDIT_TAG) { if (resultCode == Activity.RESULT_OK) { String action = data.getAction(); - String uuid = data.getStringExtra(TagSettingsActivity.EXTRA_TAG_UUID); TaskListActivity activity = (TaskListActivity) getActivity(); - if (TagSettingsActivity.ACTION_TAG_RENAMED.equals(action)) { - if (tagData.getUuid().equals(uuid)) { - TagData newTagData = tagDataDao.fetch(uuid, TagData.PROPERTIES); - if (newTagData != null) { - Filter filter = TagFilterExposer.filterFromTag(newTagData); - activity.onFilterItemClicked(filter); - } - } - } else if (TagSettingsActivity.ACTION_TAG_DELETED.equals(action)) { - String activeUuid = tagData.getUuid(); - if (activeUuid.equals(uuid)) { - activity.onFilterItemClicked(null); - } + if (TagSettingsActivity.ACTION_DELETED.equals(action)) { + activity.onFilterItemClicked(null); + } else if (TagSettingsActivity.ACTION_RELOAD.equals(action)) { + activity.getIntent().putExtra(TaskListActivity.OPEN_FILTER, + (Filter) data.getParcelableExtra(TaskListActivity.OPEN_FILTER)); + activity.recreate(); } - - broadcaster.refresh(); } } else { super.onActivityResult(requestCode, resultCode, data); diff --git a/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java b/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java index c53e070bb..c93cd663e 100644 --- a/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java +++ b/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java @@ -1,35 +1,66 @@ package org.tasks.tasklist; -import android.content.Context; +import android.app.Activity; import android.database.Cursor; import android.graphics.Canvas; +import android.os.Bundle; +import android.support.v7.view.ActionMode; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.ViewGroup; +import com.bignerdranch.android.multiselector.ModalMultiSelectorCallback; +import com.bignerdranch.android.multiselector.MultiSelector; +import com.google.common.collect.Ordering; import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.astrid.activity.TaskListActivity; import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.adapter.TaskAdapter; import com.todoroo.astrid.data.Task; +import com.todoroo.astrid.service.TaskDeleter; +import com.todoroo.astrid.service.TaskDuplicator; import com.todoroo.astrid.utility.Flags; import org.tasks.R; +import org.tasks.analytics.Tracker; +import org.tasks.analytics.Tracking; +import org.tasks.ui.MenuColorizer; + +import java.util.List; + +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Lists.newArrayList; public class TaskListRecyclerAdapter extends RecyclerView.Adapter implements ViewHolder.ViewHolderCallbacks { - private final Context context; + private final MultiSelector multiSelector = new MultiSelector(); + + private final Activity activity; private final TaskAdapter adapter; private final ViewHolderFactory viewHolderFactory; private final TaskListFragment taskList; + private final TaskDeleter taskDeleter; + private final TaskDuplicator taskDuplicator; + private final Tracker tracker; private final ItemTouchHelper itemTouchHelper; - public TaskListRecyclerAdapter(Context context, TaskAdapter adapter, - ViewHolderFactory viewHolderFactory, TaskListFragment taskList) { - this.context = context; + private ActionMode mode = null; + + public TaskListRecyclerAdapter(Activity activity, TaskAdapter adapter, + ViewHolderFactory viewHolderFactory, + TaskListFragment taskList, TaskDeleter taskDeleter, + TaskDuplicator taskDuplicator, Tracker tracker) { + this.activity = activity; this.adapter = adapter; this.viewHolderFactory = viewHolderFactory; this.taskList = taskList; + this.taskDeleter = taskDeleter; + this.taskDuplicator = taskDuplicator; + this.tracker = tracker; itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback()); } @@ -38,11 +69,23 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter im itemTouchHelper.attachToRecyclerView(recyclerView); } + public Bundle getSaveState() { + return multiSelector.saveSelectionStates(); + } + + public void restoreSaveState(Bundle savedState) { + multiSelector.restoreSelectionStates(savedState); + if (multiSelector.getSelectedPositions().size() > 0) { + mode = ((TaskListActivity) activity).startSupportActionMode(actionModeCallback); + updateModeTitle(); + } + } + @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - ViewGroup view = (ViewGroup) LayoutInflater.from(context) + ViewGroup view = (ViewGroup) LayoutInflater.from(activity) .inflate(R.layout.task_adapter_row_simple, parent, false); - return viewHolderFactory.newViewHolder(view, this); + return viewHolderFactory.newViewHolder(view, this, multiSelector); } @Override @@ -65,12 +108,121 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter im @Override public void onClick(ViewHolder viewHolder) { - Task task = viewHolder.task; - if (!task.isDeleted()) { + if (viewHolder.isMoving()) { + return; + } + if (multiSelector.tapSelection(viewHolder)) { + afterSelect(); + } else { + Task task = viewHolder.task; taskList.onTaskListItemClicked(task.getId()); } } + @Override + public boolean onLongPress(ViewHolder viewHolder) { + if (adapter.isManuallySorted()) { + return false; + } + select(viewHolder); + return true; + } + + private void select(ViewHolder viewHolder) { + if (!multiSelector.isSelectable()) { + multiSelector.setSelectable(true); + mode = ((TaskListActivity) activity).startSupportActionMode(actionModeCallback); + } + if (multiSelector.tapSelection(viewHolder)) { + afterSelect(); + } + } + + private void afterSelect() { + if (multiSelector.getSelectedPositions().isEmpty()) { + if (mode != null) { + mode.finish(); + } + } else { + updateModeTitle(); + } + } + + private List getTasks() { + return newArrayList(transform(multiSelector.getSelectedPositions(), adapter::getTask)); + } + + private void deleteSelectedItems() { + tracker.reportEvent(Tracking.Events.MULTISELECT_DELETE); + List tasks = getTasks(); + int result = taskDeleter.delete(tasks); + taskList.onTaskDelete(tasks); + for (int position : Ordering.natural().reverse().sortedCopy(multiSelector.getSelectedPositions())) { + notifyItemRemoved(position); + } + taskList.makeSnackbar(activity.getString(R.string.delete_multiple_tasks_confirmation, Integer.toString(result))) + .setAction(R.string.DLG_undo, v -> { + taskDeleter.undelete(tasks); + taskList.loadTaskListContent(); + }) + .show(); + } + + private void copySelectedItems() { + tracker.reportEvent(Tracking.Events.MULTISELECT_CLONE); + List duplicates = taskDuplicator.duplicate(getTasks()); + taskList.onTaskCreated(duplicates); + taskList.makeSnackbar(activity.getString(R.string.copy_multiple_tasks_confirmation, Integer.toString(duplicates.size()))) + .setAction(R.string.DLG_undo, v -> { + taskDeleter.delete(duplicates); + taskList.onTaskDelete(duplicates); + }) + .show(); + } + + public void clearSelections() { + multiSelector.clearSelections(); + } + + private void updateModeTitle() { + if (mode != null) { + mode.setTitle(Integer.toString(multiSelector.getSelectedPositions().size())); + } + } + + private ModalMultiSelectorCallback actionModeCallback = new ModalMultiSelectorCallback(multiSelector) { + @Override + public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { + MenuInflater inflater = actionMode.getMenuInflater(); + inflater.inflate(R.menu.menu_multi_select, menu); + MenuColorizer.colorMenu(activity, menu); + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch (item.getItemId()) { + case R.id.delete: + deleteSelectedItems(); + mode.finish(); + return true; + case R.id.copy_tasks: + copySelectedItems(); + mode.finish(); + return true; + default: + return false; + } + } + + @Override + public void onDestroyActionMode(ActionMode actionMode) { + super.onDestroyActionMode(actionMode); + multiSelector.clearSelections(); + TaskListRecyclerAdapter.this.mode = null; + } + }; + private class ItemTouchHelperCallback extends ItemTouchHelper.Callback { private int from = -1; @@ -78,7 +230,7 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter im @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { - if (!adapter.isManuallySorted()) { + if (!adapter.isManuallySorted() || mode != null) { return makeMovementFlags(0, 0); } @@ -97,7 +249,14 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter im @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { super.onSelectedChanged(viewHolder, actionState); - Flags.set(Flags.TLFP_NO_INTERCEPT_TOUCH); + if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { + if (viewHolder != null) { + ViewHolder vh = (ViewHolder) viewHolder; + vh.setMoving(true); + vh.updateBackground(); + Flags.set(Flags.TLFP_NO_INTERCEPT_TOUCH); + } + } } @Override @@ -129,16 +288,24 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter im @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); - - if (from >= 0 && from != to) { - if (from < to) { - to++; + ViewHolder vh = (ViewHolder) viewHolder; + if (vh.isMoving()) { + if (from == -1) { + select(vh); + } else { + if (from >= 0 && from != to) { + if (from < to) { + to++; + } + adapter.moved(from, to); + } } - adapter.moved(from, to); } from = -1; to = -1; Flags.checkAndClear(Flags.TLFP_NO_INTERCEPT_TOUCH); + vh.setMoving(false); + vh.updateBackground(); } @Override diff --git a/src/main/java/org/tasks/tasklist/ViewHolder.java b/src/main/java/org/tasks/tasklist/ViewHolder.java index 3d2659442..0f82893b7 100644 --- a/src/main/java/org/tasks/tasklist/ViewHolder.java +++ b/src/main/java/org/tasks/tasklist/ViewHolder.java @@ -5,7 +5,6 @@ import android.app.PendingIntent; import android.content.Context; import android.graphics.Paint; import android.support.v7.app.AlertDialog; -import android.support.v7.widget.RecyclerView; import android.text.SpannableString; import android.text.TextUtils; import android.text.method.LinkMovementMethod; @@ -16,6 +15,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.bignerdranch.android.multiselector.MultiSelector; +import com.bignerdranch.android.multiselector.MultiSelectorBindingHolder; import com.google.common.collect.Lists; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.utility.DateUtilities; @@ -47,12 +48,55 @@ import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop; * * @author Tim Su */ -public class ViewHolder extends RecyclerView.ViewHolder { +class ViewHolder extends MultiSelectorBindingHolder { - public interface ViewHolderCallbacks { + @Override + public void setSelectable(boolean selectable) { + this.selectable = selectable; + updateBackground(); + } + + @Override + public boolean isSelectable() { + return selectable; + } + + @Override + public void setActivated(boolean selected) { + this.selected = selected; + updateBackground(); + } + + void setMoving(boolean moving) { + this.moving = moving; + } + + boolean isMoving() { + return moving; + } + + void updateBackground() { + if (selected || moving) { + rowBody.setBackgroundColor(selectedColor); + } else if (selectable) { + rowBody.setBackgroundColor(0); + } else { + rowBody.setBackgroundResource(background); + rowBody.getBackground().jumpToCurrentState(); + } + } + + @Override + public boolean isActivated() { + return selected; + } + + interface ViewHolderCallbacks { void onCompletedTask(Task task, boolean newState); void onClick(ViewHolder viewHolder); + + boolean onLongPress(ViewHolder viewHolder); } @BindView(R.id.row) public ViewGroup row; @@ -79,16 +123,21 @@ public class ViewHolder extends RecyclerView.ViewHolder { private final DialogBuilder dialogBuilder; private final ViewHolderCallbacks callback; private final DisplayMetrics metrics; + private final int background; + private final int selectedColor; private final int textColorOverdue; private Pair lastTouchYRawY = new Pair<>(0f, 0f); private int indent; - - public ViewHolder(Context context, ViewGroup view, boolean showFullTaskTitle, int fontSize, - CheckBoxes checkBoxes, TagFormatter tagFormatter, - int textColorOverdue, int textColorSecondary, int textColorHint, TaskDao taskDao, - DialogBuilder dialogBuilder, ViewHolderCallbacks callback, int minRowHeight, - DisplayMetrics metrics) { - super(view); + private boolean selectable = false; + private boolean selected; + private boolean moving; + + ViewHolder(Context context, ViewGroup view, boolean showFullTaskTitle, int fontSize, + CheckBoxes checkBoxes, TagFormatter tagFormatter, + int textColorOverdue, int textColorSecondary, int textColorHint, TaskDao taskDao, + DialogBuilder dialogBuilder, ViewHolderCallbacks callback, int minRowHeight, + DisplayMetrics metrics, int background, int selectedColor, MultiSelector multiSelector) { + super(view, multiSelector); this.context = context; this.fontSize = fontSize; this.checkBoxes = checkBoxes; @@ -100,6 +149,8 @@ public class ViewHolder extends RecyclerView.ViewHolder { this.dialogBuilder = dialogBuilder; this.callback = callback; this.metrics = metrics; + this.background = background; + this.selectedColor = selectedColor; ButterKnife.bind(this, view); task = new Task(); @@ -293,6 +344,8 @@ public class ViewHolder extends RecyclerView.ViewHolder { rowBody.setOnClickListener(view -> callback.onClick(this)); + rowBody.setOnLongClickListener(view -> callback.onLongPress(this)); + if (taskActionContainer != null) { taskActionContainer.setOnClickListener(v -> { TaskAction action = (TaskAction) taskActionIcon.getTag(); diff --git a/src/main/java/org/tasks/tasklist/ViewHolderFactory.java b/src/main/java/org/tasks/tasklist/ViewHolderFactory.java index fcfffffbd..8fbb6eea4 100644 --- a/src/main/java/org/tasks/tasklist/ViewHolderFactory.java +++ b/src/main/java/org/tasks/tasklist/ViewHolderFactory.java @@ -4,6 +4,7 @@ import android.content.Context; import android.util.DisplayMetrics; import android.view.ViewGroup; +import com.bignerdranch.android.multiselector.MultiSelector; import com.todoroo.astrid.dao.TaskDao; import org.tasks.R; @@ -16,6 +17,7 @@ import javax.inject.Inject; import static android.support.v4.content.ContextCompat.getColor; import static org.tasks.preferences.ResourceResolver.getData; +import static org.tasks.preferences.ResourceResolver.getResourceId; public class ViewHolderFactory { @@ -31,6 +33,8 @@ public class ViewHolderFactory { private final DialogBuilder dialogBuilder; private final int minRowHeight; private final DisplayMetrics metrics; + private final int background; + private final int selectedColor; @Inject public ViewHolderFactory(@ForActivity Context context, Preferences preferences, @@ -44,15 +48,17 @@ public class ViewHolderFactory { textColorSecondary = getData(context, android.R.attr.textColorSecondary); textColorHint = getData(context, android.R.attr.textColorTertiary); textColorOverdue = getColor(context, R.color.overdue); + background = getResourceId(context, R.attr.selectableItemBackground); + selectedColor = getData(context, R.attr.colorControlHighlight); showFullTaskTitle = preferences.getBoolean(R.string.p_fullTaskTitle, false); fontSize = preferences.getIntegerFromString(R.string.p_fontSize, 18); metrics = context.getResources().getDisplayMetrics(); minRowHeight = (int) (metrics.density * 40); } - ViewHolder newViewHolder(ViewGroup viewGroup, ViewHolder.ViewHolderCallbacks callbacks) { + ViewHolder newViewHolder(ViewGroup viewGroup, ViewHolder.ViewHolderCallbacks callbacks, MultiSelector multiSelector) { return new ViewHolder(context, viewGroup, showFullTaskTitle, fontSize, checkBoxes, tagFormatter, textColorOverdue, textColorSecondary, textColorHint, taskDao, - dialogBuilder, callbacks, minRowHeight, metrics); + dialogBuilder, callbacks, minRowHeight, metrics, background, selectedColor, multiSelector); } } diff --git a/src/main/java/org/tasks/themes/Theme.java b/src/main/java/org/tasks/themes/Theme.java index 43f5e45be..df03cfcd3 100644 --- a/src/main/java/org/tasks/themes/Theme.java +++ b/src/main/java/org/tasks/themes/Theme.java @@ -51,7 +51,7 @@ public class Theme { } public void applyStatusBarColor(Activity activity) { - themeColor.applyStatusBarColor(activity); + themeColor.applyToStatusBar(activity); themeColor.applyTaskDescription(activity, activity.getString(R.string.app_name)); } @@ -72,8 +72,4 @@ public class Theme { applyToContext(wrapper); return wrapper; } - - public Theme withColor(ThemeColor themeColor) { - return new Theme(themeBase, themeColor, themeAccent); - } } diff --git a/src/main/java/org/tasks/themes/ThemeBase.java b/src/main/java/org/tasks/themes/ThemeBase.java index 811648e4e..894e5074a 100644 --- a/src/main/java/org/tasks/themes/ThemeBase.java +++ b/src/main/java/org/tasks/themes/ThemeBase.java @@ -11,11 +11,11 @@ import org.tasks.R; public class ThemeBase { private static final int[] THEMES = new int[]{ - R.style.TasksOverride, + R.style.Tasks, R.style.ThemeBlack, - R.style.TasksOverride, + R.style.Tasks, R.style.Wallpaper, - R.style.TasksOverride + R.style.Tasks }; private final String name; diff --git a/src/main/java/org/tasks/themes/ThemeColor.java b/src/main/java/org/tasks/themes/ThemeColor.java index 50b38d5d6..283a6f5b6 100644 --- a/src/main/java/org/tasks/themes/ThemeColor.java +++ b/src/main/java/org/tasks/themes/ThemeColor.java @@ -18,7 +18,7 @@ import static com.todoroo.andlib.utility.AndroidUtilities.atLeastMarshmallow; public class ThemeColor { - public static final int[] COLORS = new int[] { + static final int[] COLORS = new int[] { R.style.BlueGrey, R.style.DarkGrey, R.style.Red, @@ -58,10 +58,9 @@ public class ThemeColor { } @SuppressLint("NewApi") - public void applyStatusBarColor(Activity activity) { - if (atLeastLollipop()) { - activity.getWindow().setStatusBarColor(getColorPrimaryDark()); - } + public void applyToStatusBar(Activity activity) { + setStatusBarColor(activity); + if (atLeastMarshmallow()) { View decorView = activity.getWindow().getDecorView(); int systemUiVisibility = applyLightStatusBarFlag(decorView.getSystemUiVisibility()); @@ -69,8 +68,14 @@ public class ThemeColor { } } + public void setStatusBarColor(Activity activity) { + if (atLeastLollipop()) { + activity.getWindow().setStatusBarColor(getColorPrimaryDark()); + } + } + @SuppressLint("NewApi") - public void applyStatusBarColor(DrawerLayout drawerLayout) { + public void applyToStatusBar(DrawerLayout drawerLayout) { if (atLeastLollipop()) { drawerLayout.setStatusBarBackgroundColor(getColorPrimaryDark()); } @@ -110,7 +115,7 @@ public class ThemeColor { return actionBarTint; } - private int getColorPrimaryDark() { + public int getColorPrimaryDark() { return colorPrimaryDark; } diff --git a/src/main/java/org/tasks/ui/MenuColorizer.java b/src/main/java/org/tasks/ui/MenuColorizer.java index bc2525974..8a3ecf5d4 100644 --- a/src/main/java/org/tasks/ui/MenuColorizer.java +++ b/src/main/java/org/tasks/ui/MenuColorizer.java @@ -63,6 +63,12 @@ public class MenuColorizer { colorMenu(toolbar.getMenu(), color); } + public static void colorMenu(Context context, Menu menu) { + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.actionBarPrimaryText, typedValue, true); + colorMenu(menu, typedValue.data); + } + /** Sets a color filter on all menu icons, including the overflow button (if it exists) */ private static void colorMenu(final Menu menu, final int color) { for (int i = 0, size = menu.size(); i < size; i++) { diff --git a/src/main/res/drawable/ic_content_copy_black_24dp.xml b/src/main/res/drawable/ic_content_copy_black_24dp.xml new file mode 100644 index 000000000..416caa2a1 --- /dev/null +++ b/src/main/res/drawable/ic_content_copy_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/src/main/res/layout/task_adapter_row_simple.xml b/src/main/res/layout/task_adapter_row_simple.xml index 7d261e80e..4b3dcc899 100644 --- a/src/main/res/layout/task_adapter_row_simple.xml +++ b/src/main/res/layout/task_adapter_row_simple.xml @@ -3,6 +3,7 @@ android:id="@+id/row" android:layout_width="fill_parent" android:layout_height="wrap_content" + android:background="?attr/asContentBackground" android:orientation="vertical"> diff --git a/src/main/res/menu/menu_multi_select.xml b/src/main/res/menu/menu_multi_select.xml new file mode 100644 index 000000000..bb22ca068 --- /dev/null +++ b/src/main/res/menu/menu_multi_select.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/res/values-bg-rBG/strings.xml b/src/main/res/values-bg-rBG/strings.xml index 8f7600eec..667c79a47 100644 --- a/src/main/res/values-bg-rBG/strings.xml +++ b/src/main/res/values-bg-rBG/strings.xml @@ -107,7 +107,6 @@ Приключване на събития в календара на крайния срок Започване на събития в календара на крайния срок Управление на стари задачи - Изтрити %d задачи! Изчистване на изтритите задачи Наистина ли искате да изчистите всичките си приключени задачи?\n\nТези задачи ще изчезнат завинаги! Изчистени %d задачи! diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 689aba16a..1afeb4dfa 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -64,7 +64,6 @@ Aparença Restableix els valors predeterminats Mostra el títol sencer de la tasca - S\'han suprimit %d tasques Segur que voleu suprimir els esdeveniments de calendari de les tasques completades? Demà passat La setmana que ve diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 95dd10ce6..9b3a43fac 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -98,7 +98,6 @@ Událost v kalendáři končí splněním úkolu Událost v kalendáři začíná splněním úkolu Spravovat staré úkoly - Odstraněno %d úkolů! Vymazat smazané úkoly Opravdu chcete vymazat vaše smazané úkoly?\n\nTyto úkoly budou vymazány navždy! Vymyzáno %d úkolů! diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index ff08e37ee..a5e024dac 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -102,7 +102,6 @@ Kalendereintrag endet zur Fälligkeits-Uhrzeit Kalendereintrag Startet zur Fälligkeits-Uhrzeit Alte Aufgaben verwalten - %d Aufgaben gelöscht Gelöschte Aufgaben endgültig löschen Willst du wirklich alle gelöschten Aufgaben bereinigen?\n\nDiese Aufgaben werden für immer gelöscht bleiben! %d Aufgaben bereinigt! diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 512b2e83f..3858d2360 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -89,7 +89,6 @@ Τερματισμός γεγονότων ημερολογίου στην ώρα λήξης τους Εκκίνηση γεγονότων ημερολογίου στην ώρα λήξης τους Διαχείριση παλιών εργασιών - Διαγράφηκαν %d εργασίες! Εκκαθάριση διεγραμμένων εργασιών Θέλετε πραγματικά να καθαρίσετε όλες τις διαγραμμένες εργασίες;\n\nΑυτές οι εργασίες θα χαθούν για πάντα! Εκκαθαρίστηκαν %d εργασίες! diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index a0b964b74..54f7f1300 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -103,7 +103,6 @@ Finalizar eventos del calendario al vencimiento Iniciar eventos del calendario al vencimiento Administrar tareas antiguas - ¡%d tareas borradas! Limpiar tareas eliminadas ¿Seguro que desea limpiar todas las tareas eliminadas?\n\n¡Estas tareas se perderán por siempre! ¡%d tareas purgadas! diff --git a/src/main/res/values-fi/strings.xml b/src/main/res/values-fi/strings.xml index 4c42cb3ca..b56ab2ed2 100644 --- a/src/main/res/values-fi/strings.xml +++ b/src/main/res/values-fi/strings.xml @@ -107,7 +107,6 @@ Lopeta kalenteritapahtuma määräaikana Aloita kalenteritapahtumat määräaikana Hallinnoi vanhoja tehtäviä - Poistettu %d tehtävää Tyjennä poistetut tehtävät Haluatko todella tyhjentää kaikki poistetut tehtävät?\n\n Nämä tehtävät häviävät lopullisesti! Tyhjennettu %d tehtävää! diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index beb57bb67..9d7043cd9 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -102,7 +102,6 @@ Terminer les événements dans le calendrier à l\'échéance Commencer les événements dans le calendrier à l\'échéance Gérer les anciennes tâches - %d tâches supprimées ! Supprimer définitivement les tâches supprimées Voulez vous vraiment supprimer définitivement toutes les tâches supprimées ?\n\nCes tâches seront perdues à jamais ! %d tâches supprimées définitivement ! diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index 456c4b5fc..7e2991ee8 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -107,7 +107,6 @@ Naptáresemények befejezése határidőkor Naptáresemények kezdése határidőkor Régi feladatok kezelése - %d feladat törölve! Törölt feladatok elvetése Biztosan el akarja vetni az összes törölt feladatot?\n\nEzek a feladatok többé nem lesznek visszaállíthatóak! %d feladat elvetve diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 46070ee59..eb9ad1e0b 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -104,7 +104,6 @@ Calendario eventi fino alla scadenza Calendario eventi dalla scadenza Gestisci compiti vecchi - Eliminate %d attività! Elimina definitivamente i compiti eliminati Vuoi davvero eliminare definitivamente i compiti eliminati?\n\nQuesti compiti saranno persi per sempre! Rimosse %d attività! @@ -413,4 +412,4 @@ Crea nuova lista Cancella lista RInomina lista - \ No newline at end of file + diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index 6a7eb24af..ee0f5d9da 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -104,7 +104,6 @@ סיים את אירועי היומן במועד היעד? התחל את מאורעות היומן במועד היעד נהל משימות ישנות - %d משימות נמחקו! מחק לצמיתות משימות שנמחקו האם אתה בטוח שברצונך למחוק לצמיתות את כל המשימות שנמחקו? %d משימות אֻיְּנוּ! diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 37bd8b6d8..6805896fc 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -104,7 +104,6 @@ カレンダーイベント終了時間 カレンダーイベント開始時間 古いタスクを管理 - %d タスクが削除されました! 削除済のタスクを消去 削除済のタスクをすべて消去してもよろしいですか?\n\nこれらのタスクは永久になくなります! %d タスクを消去しました! diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index 201e88dce..7b57af5a5 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -106,7 +106,6 @@ 설정한 시간에 달력 이벤트 종료 설정한 시간에 달력 이벤트 시작 오래된 일정 관리하기 - %d 일정을 삭제했습니다! 삭제한 일정을 비우기 삭제한 일정을 모두 비울까요?\n\n비운 일정은 다시 살릴 수 없습니다! %d 일정을 비웠습니다! diff --git a/src/main/res/values-night/colors.xml b/src/main/res/values-night/colors.xml index c4126c5f3..503283499 100644 --- a/src/main/res/values-night/colors.xml +++ b/src/main/res/values-night/colors.xml @@ -9,4 +9,5 @@ @color/white_70 @color/white_50 @color/white_12 + @color/grey_900 \ No newline at end of file diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 9cb0b94e7..ec2fa4bea 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -103,7 +103,6 @@ Agenda item op tijd afronden Agenda item op tijd starten Oude taken beheren - %d taken verwijderd! Verwijderde taken opruimen Alle verwijderde taken werkelijk opschonen?\n\nDeze taken zijn dan definitief verwijderd! %d verwijderde taken opgeruimd! diff --git a/src/main/res/values-notnight/colors.xml b/src/main/res/values-notnight/colors.xml index a2de3ca2f..29d93175c 100644 --- a/src/main/res/values-notnight/colors.xml +++ b/src/main/res/values-notnight/colors.xml @@ -9,4 +9,5 @@ @color/black_54 @color/black_38 @color/black_12 + @color/grey_100 \ No newline at end of file diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index f530ac566..e75176eef 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -104,7 +104,6 @@ Zakończ zadania kalendarza w ustalonym czasie Rozpocznij zadania kalendarza w ustalonym czasie Zarządzaj starymi zadaniami - Usunięto %d zadań! Skasuj usunięte zadania Czy na pewno chcesz, aby oczyścić wszystkie usunięte zadania?\n\n Te zadania znikną na zawsze! Oczyszczone %d zadań! diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 482ca954e..23ab900a0 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -94,7 +94,6 @@ Eventos da agenda terminou no devido tempo Eventos da agenda iniciaram no devido tempo Gerenciar tarefas antigas - %d tarefas excluídas! Limpar tarefas excluídas Você realmente deseja limpar todas as suas tarefas excluídas?\n\nEstas tarefas irão desaparecer para sempre! %d tarefas removidas! diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index fd090d506..5219c5949 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -98,7 +98,6 @@ Terminar evento do calendário na hora limite Iniciar eventos de calendário na hora limite Gerir tarefas antigas - %d tarefas eliminadas! Remover tarefas eliminadas Quer mesmo remover todas as tarefas eliminadas?\n\nNunca mais as conseguirá recuperar! %d tarefas removidas! diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 11d2b6193..a5298a064 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -104,7 +104,6 @@ Завершать события в календаре при наступлении срока Создавать события в календаре при наступлении срока Управление старыми задачами - Удалено %d задач! Очистить удаленные задачи? Действительно хотите убрать все удалённые задачи?\n\nЭта операция необратима! Убрано %d задач! diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index bd025c8c3..93dd802da 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -96,7 +96,6 @@ Ukončiť udalosť kalendára v danom čase Začať kalendárne udalosti v danom čase Spravovať staré úlohy - Vymazané %d úlohy Zbaviť sa vymazaných úloh Naozaj chceš vyhodiť všetky tvoje vymazané úlohy?\n\nTieto budú vymazané navždy! Vyčistené %d úlohy diff --git a/src/main/res/values-sl-rSI/strings.xml b/src/main/res/values-sl-rSI/strings.xml index 895bdbb13..e26d870dd 100644 --- a/src/main/res/values-sl-rSI/strings.xml +++ b/src/main/res/values-sl-rSI/strings.xml @@ -91,7 +91,6 @@ Končaj dogodke na koledarju ob dospelosti Začni dogodke na koledarju ob dospelosti Upravljanje s preteklimi opravki - Število zbrisanih opravkov: %d Popolnoma odstrani zbrisane opravke Ste prepričani, da želite popolnoma odstraniti vse zbrisane opravke?\n\nTega ukaza ne bo mogoče razveljaviti! Število popolnoma odstranjenih opravkov: %d diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 246ac17f4..6528cfaeb 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -104,7 +104,6 @@ Avsluta kalenderhändelser vid förfallotid Påbörja kalenderhändelser vid förfallotid Hantera gamla uppgifter - %d uppgifter raderade! Rensa bort raderade uppgifter Vill du verkligen rensa bort alla dina raderade uppgifter?\n\nDe här uppgifterna kommer att tas bort för alltid! %d uppgifter bortrensade! diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 7c92b1415..3064aa721 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -66,7 +66,6 @@ Varsayılanlara sıfırla Tüm görev başlığını göster Eski Görevleri Yönet - %d görev silindi! Silinen Görevleri Temizle Tüm silinen görevleri temizlemek istediğine emin misin?\n\nBu görevler tamamen silinecek! %d görev temizlendi! diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index 460e15f99..54fb75569 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -107,7 +107,6 @@ Завершення календарної події при настанні терміну Початок календарної події при настанні терміну Управління старими завданнями - Видалено %d завдань! Очистити видалені завдання Ви дійсно хочете очистити всі видалені завдання?\n\nЦя операція незворотна! Очищено %d завдань! diff --git a/src/main/res/values-v21/theme.xml b/src/main/res/values-v21/theme.xml index 1c9197903..7fc645f8b 100644 --- a/src/main/res/values-v21/theme.xml +++ b/src/main/res/values-v21/theme.xml @@ -1,6 +1,6 @@ - diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index bdb1cf988..6adf77684 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -78,7 +78,6 @@ 显示完整的任务标题 任务列表选项 管理旧任务 - 删除了 %d 个任务! 清除已删除任务 您真的要清楚所有已删除任务吗? \n\n 这些任务将会被彻底永久删除! 清除了 %d 个任务! diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 815892cf0..17a67a481 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -77,7 +77,6 @@ 恢復默認值 顯示完整的任務工作標題 管理舊任務工作 - 刪除了 %d 個任務工作! 清除已刪除任務工作 您真的要清楚所有已刪除任務工作嗎? \n\n 這些任務工作將會被徹底永久刪除! 清除了 %d 個任務工作! diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 3c486bfe5..6a10520db 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -74,6 +74,7 @@ #5d4037 #fafafa + #f5f5f5 #9e9e9e #616161 #424242 diff --git a/src/main/res/values/keys.xml b/src/main/res/values/keys.xml index 295284583..8c22c15a1 100644 --- a/src/main/res/values/keys.xml +++ b/src/main/res/values/keys.xml @@ -297,6 +297,8 @@ Play Services Error Upgrade Legacy Tasker + Multiselect delete + Multiselect clone tesla_unread_enabled purchased_tesla_unread purchased_tasker diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index c4804b5c9..9955a0891 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -291,7 +291,6 @@ File %1$s contained %2$s.\n\n Manage old tasks - Deleted %d tasks! Purge deleted tasks Do you really want to purge all your deleted tasks?\n\nThese tasks will be gone forever! Purged %d tasks! @@ -821,5 +820,7 @@ File %1$s contained %2$s.\n\n Deleting list Renaming list Clear completed tasks? + %s copied + %s deleted diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 203dd754a..6ab381a5a 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -55,12 +55,10 @@ diff --git a/src/main/res/values/theme.xml b/src/main/res/values/theme.xml index 7d16c9a1e..86bec3943 100644 --- a/src/main/res/values/theme.xml +++ b/src/main/res/values/theme.xml @@ -1,7 +1,7 @@ - - + - -