diff --git a/opentasks/src/main/java/org/dmfs/tasks/EditTaskFragment.java b/opentasks/src/main/java/org/dmfs/tasks/EditTaskFragment.java index 3c617586c..d1b94fe85 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/EditTaskFragment.java +++ b/opentasks/src/main/java/org/dmfs/tasks/EditTaskFragment.java @@ -789,7 +789,9 @@ public void saveAndExit() activity.finish(); if (isNewTask) { - activity.startActivity(new Intent("android.intent.action.VIEW", mTaskUri)); + activity.startActivity( + new Intent(Intent.ACTION_VIEW, mTaskUri) + .putExtra(ViewTaskActivity.EXTRA_COLOR, mListColor)); } } else diff --git a/opentasks/src/main/java/org/dmfs/tasks/EmptyTaskFragment.java b/opentasks/src/main/java/org/dmfs/tasks/EmptyTaskFragment.java index e5238bcc8..52ef0a74f 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/EmptyTaskFragment.java +++ b/opentasks/src/main/java/org/dmfs/tasks/EmptyTaskFragment.java @@ -18,11 +18,13 @@ import android.app.Activity; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import org.dmfs.android.bolts.color.colors.AttributeColor; +import org.dmfs.android.bolts.color.Color; +import org.dmfs.android.bolts.color.elementary.ValueColor; import org.dmfs.android.retentionmagic.SupportFragment; @@ -34,11 +36,22 @@ */ public class EmptyTaskFragment extends SupportFragment { + private static final String ARG_COLOR = "color"; - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + private Color mColor; + + + /** + * @param color + * The color that the toolbars should take. (If available provide the actual task list color, otherwise the primary color.) + */ + public static Fragment newInstance(Color color) { - return inflater.inflate(R.layout.opentasks_fragment_empty_task, container, false); + EmptyTaskFragment fragment = new EmptyTaskFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_COLOR, color.argb()); + fragment.setArguments(args); + return fragment; } @@ -46,10 +59,21 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa public void onAttach(Activity activity) { super.onAttach(activity); + + mColor = new ValueColor(getArguments().getInt(ARG_COLOR)); + if (activity instanceof ViewTaskFragment.Callback) { - ((ViewTaskFragment.Callback) activity) - .updateColor(new AttributeColor(getContext(), R.attr.colorPrimary)); + ((ViewTaskFragment.Callback) activity).onListColorLoaded(mColor); } } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + { + View view = inflater.inflate(R.layout.opentasks_fragment_empty_task, container, false); + view.findViewById(R.id.empty_task_fragment_appbar).setBackgroundColor(mColor.argb()); + return view; + } } diff --git a/opentasks/src/main/java/org/dmfs/tasks/TaskGroupPagerAdapter.java b/opentasks/src/main/java/org/dmfs/tasks/TaskGroupPagerAdapter.java index 5a2639298..4155d84a6 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/TaskGroupPagerAdapter.java +++ b/opentasks/src/main/java/org/dmfs/tasks/TaskGroupPagerAdapter.java @@ -45,7 +45,6 @@ public class TaskGroupPagerAdapter extends FragmentStatePagerAdapter @SuppressWarnings("unused") private static final String TAG = "TaskGroupPager"; private final Map mGroupingFactories = new HashMap(16); - private boolean mTwoPaneLayout; private final TabConfig mTabConfig; @@ -94,7 +93,7 @@ public Fragment getItem(int position) int pageId = mTabConfig.getVisibleItem(position).getId(); AbstractGroupingFactory factory = getGroupingFactoryForId(pageId); - TaskListFragment fragment = TaskListFragment.newInstance(position, mTwoPaneLayout); + TaskListFragment fragment = TaskListFragment.newInstance(position); fragment.setExpandableGroupDescriptor(factory.getExpandableGroupDescriptor()); fragment.setPageId(pageId); return fragment; @@ -159,12 +158,6 @@ public int getCount() } - public void setTwoPaneLayout(boolean twoPane) - { - mTwoPaneLayout = twoPane; - } - - public int getTabIcon(int position) { return mTabConfig.getVisibleItem(position).getIcon(); diff --git a/opentasks/src/main/java/org/dmfs/tasks/TaskListActivity.java b/opentasks/src/main/java/org/dmfs/tasks/TaskListActivity.java index 6d1da9024..277c85d80 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/TaskListActivity.java +++ b/opentasks/src/main/java/org/dmfs/tasks/TaskListActivity.java @@ -26,6 +26,7 @@ import android.os.Bundle; import android.os.Handler; import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; import android.support.design.widget.AppBarLayout; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; @@ -47,6 +48,8 @@ import android.view.WindowManager; import org.dmfs.android.bolts.color.Color; +import org.dmfs.android.bolts.color.colors.PrimaryColor; +import org.dmfs.android.bolts.color.elementary.ValueColor; import org.dmfs.android.retentionmagic.annotations.Retain; import org.dmfs.provider.tasks.AuthorityUtil; import org.dmfs.tasks.contract.TaskContract.Tasks; @@ -92,8 +95,6 @@ public class TaskListActivity extends BaseActivity implements TaskListFragment.C **/ public static final String EXTRA_FORCE_LIST_SELECTION = "org.dmfs.tasks.FORCE_LIST_SELECTION"; - private static final String TAG = "TaskListActivity"; - private final static int REQUEST_CODE_NEW_TASK = 2924; /** @@ -101,7 +102,7 @@ public class TaskListActivity extends BaseActivity implements TaskListFragment.C */ private final static int SEARCH_UPDATE_DELAY = 400; // ms - private final static String DETAIL_FRAGMENT_TAG = "taskListActivity.ViewTaskFragment"; + private final static String DETAILS_FRAGMENT_TAG = "details_fragment_tag"; /** * Array of {@link ExpandableGroupDescriptor}s. @@ -118,6 +119,14 @@ public class TaskListActivity extends BaseActivity implements TaskListFragment.C @Retain(permanent = true) private int mCurrentPageId; + /** + * The last used color for the toolbars. {@link android.graphics.Color#TRANSPARENT} represents the absent value. + * (Used upon start/rotation until the actually selected task with its color is loaded, to avoid flashing up primary color.) + */ + @Retain(permanent = true) + @ColorInt + private int mLastUsedColor = android.graphics.Color.TRANSPARENT; + /** * The current pager position **/ @@ -148,12 +157,6 @@ public class TaskListActivity extends BaseActivity implements TaskListFragment.C **/ private Uri mSelectedTaskUriOnLaunch; - /** - * Indicates to display the two pane layout with details - **/ - @Retain - private boolean mShouldShowDetails = false; - /** * Indicates to show ViewTaskActivity when rotating to single pane. **/ @@ -180,6 +183,12 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (mLastUsedColor == android.graphics.Color.TRANSPARENT) + { + // no saved color, use the primary color + mLastUsedColor = new PrimaryColor(this).argb(); + } + // check for single pane activity change mTwoPane = getResources().getBoolean(R.bool.has_two_panes); @@ -187,10 +196,11 @@ protected void onCreate(Bundle savedInstanceState) if (mSelectedTaskUri != null) { - if (mShouldShowDetails && mShouldSwitchToDetail) + if (!mTwoPane && mShouldSwitchToDetail) { Intent viewTaskIntent = new Intent(Intent.ACTION_VIEW); viewTaskIntent.setData(mSelectedTaskUri); + viewTaskIntent.putExtra(ViewTaskActivity.EXTRA_COLOR, mLastUsedColor); startActivity(viewTaskIntent); mShouldSwitchToDetail = false; mTransientState = true; @@ -198,7 +208,7 @@ protected void onCreate(Bundle savedInstanceState) } else { - mShouldShowDetails = false; + mShouldSwitchToDetail = false; } setContentView(R.layout.activity_task_list); @@ -211,21 +221,18 @@ protected void onCreate(Bundle savedInstanceState) if (findViewById(R.id.task_detail_container) != null) { - // In two-pane mode, list items should be given the - // 'activated' state when touched. - - // get list fragment - // mTaskListFrag = (TaskListFragment) getSupportFragmentManager().findFragmentById(R.id.task_list); - // mTaskListFrag.setListViewScrollbarPositionLeft(true); - - // mTaskListFrag.setActivateOnItemClick(true); - - loadTaskDetailFragment(mSelectedTaskUri); + /* Note: 'savedInstanceState == null' is not used here as would be usual with fragments, because of the case of when rotation means + switching from one-pane mode to two-pane mode on small tablets and the fragment has to added. To cover that case as well, the fragment is always replaced. */ + replaceTaskDetailsFragment( + mSelectedTaskUri == null ? + EmptyTaskFragment.newInstance(new ValueColor(mLastUsedColor)) + : ViewTaskFragment.newInstance(mSelectedTaskUri, new ValueColor(mLastUsedColor))); } else { + // When rotating the screen means switching from two-pane to single-pane mode (on small tablets), remove the obsolete fragment that gets recreated by FragmentManager: FragmentManager fragmentManager = getSupportFragmentManager(); - Fragment detailFragment = fragmentManager.findFragmentByTag(DETAIL_FRAGMENT_TAG); + Fragment detailFragment = fragmentManager.findFragmentByTag(DETAILS_FRAGMENT_TAG); if (detailFragment != null) { fragmentManager.beginTransaction().remove(detailFragment).commit(); @@ -238,8 +245,6 @@ protected void onCreate(Bundle savedInstanceState) mPagerAdapter = new Unchecked<>(() -> new TaskGroupPagerAdapter(getSupportFragmentManager(), mGroupingFactories, this, R.xml.listview_tabs)).value(); - // Setup ViewPager - mPagerAdapter.setTwoPaneLayout(mTwoPane); mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mPagerAdapter); @@ -379,20 +384,19 @@ protected void onDestroy() * Callback method from {@link TaskListFragment.Callbacks} indicating that the item with the given ID was selected. */ @Override - public void onItemSelected(Uri uri, boolean forceReload, int pagePosition) + public void onItemSelected(@NonNull Uri uri, @NonNull Color taskListColor, boolean forceReload, int pagePosition) { // only accept selections from the current visible task fragment or the activity itself if (pagePosition == -1 || pagePosition == mCurrentPagePosition) { if (mTwoPane) { - mShouldShowDetails = true; if (forceReload) { - mSelectedTaskUri = null; + mSelectedTaskUri = uri; mShouldSwitchToDetail = false; } - loadTaskDetailFragment(uri); + replaceTaskDetailsFragment(ViewTaskFragment.newInstance(uri, taskListColor)); } else if (forceReload) { @@ -402,6 +406,7 @@ else if (forceReload) // for the selected item ID. Intent detailIntent = new Intent(Intent.ACTION_VIEW); detailIntent.setData(uri); + detailIntent.putExtra(ViewTaskActivity.EXTRA_COLOR, mLastUsedColor); startActivity(detailIntent); mShouldSwitchToDetail = false; } @@ -409,34 +414,48 @@ else if (forceReload) } - private void loadTaskDetailFragment(Uri uri) + @Override + public void onItemRemoved(@NonNull Uri taskUri) { - Fragment detailFragment = getSupportFragmentManager().findFragmentByTag(DETAIL_FRAGMENT_TAG); - - if (uri == null) + if (taskUri.equals(mSelectedTaskUri)) { - if (!(detailFragment instanceof EmptyTaskFragment)) + mSelectedTaskUri = null; + if (mTwoPane) { - replaceDetailFragment(new EmptyTaskFragment()); + replaceTaskDetailsFragment(EmptyTaskFragment.newInstance(new ValueColor(mLastUsedColor))); } } - else + } + + + @Override + public void onAddNewTask() + { + Intent editTaskIntent = new Intent(Intent.ACTION_INSERT); + editTaskIntent.setData(Tasks.getContentUri(mAuthority)); + startActivityForResult(editTaskIntent, REQUEST_CODE_NEW_TASK); + } + + + @Override + public ExpandableGroupDescriptor getGroupDescriptor(int pageId) + { + for (AbstractGroupingFactory factory : mGroupingFactories) { - if (detailFragment instanceof ViewTaskFragment) - { - ((ViewTaskFragment) detailFragment).loadUri(uri); - } - else + if (factory.getId() == pageId) { - replaceDetailFragment(ViewTaskFragment.newInstance(uri)); + return factory.getExpandableGroupDescriptor(); } } + return null; } - private void replaceDetailFragment(Fragment fragment) + private void replaceTaskDetailsFragment(@NonNull Fragment fragment) { - getSupportFragmentManager().beginTransaction().replace(R.id.task_detail_container, fragment, DETAIL_FRAGMENT_TAG).commit(); + getSupportFragmentManager().beginTransaction() + .setCustomAnimations(0, R.anim.openttasks_fade_exit, 0, 0) + .replace(R.id.task_detail_container, fragment, DETAILS_FRAGMENT_TAG).commit(); } @@ -467,30 +486,6 @@ private void updateTitle(int pageId) } - @Override - public void onEditTask(Uri taskUri, ContentSet data) - { - Intent editTaskIntent = new Intent(Intent.ACTION_EDIT); - editTaskIntent.setData(taskUri); - if (data != null) - { - Bundle extraBundle = new Bundle(); - extraBundle.putParcelable(EditTaskActivity.EXTRA_DATA_CONTENT_SET, data); - editTaskIntent.putExtra(EditTaskActivity.EXTRA_DATA_BUNDLE, extraBundle); - } - startActivity(editTaskIntent); - } - - - @Override - public void onAddNewTask() - { - Intent editTaskIntent = new Intent(Intent.ACTION_INSERT); - editTaskIntent.setData(Tasks.getContentUri(mAuthority)); - startActivityForResult(editTaskIntent, REQUEST_CODE_NEW_TASK); - } - - private void resolveIntentAction(Intent intent) { // check which task should be selected @@ -542,14 +537,67 @@ public void onActivityResult(int requestCode, int resultCode, Intent intent) @Override - public void onDelete(Uri taskUri) + public void onTaskEditRequested(@NonNull Uri taskUri, ContentSet data) { - // nothing to do here, the loader will take care of reloading the list and the list view will take care of selecting the next element. + Intent editTaskIntent = new Intent(Intent.ACTION_EDIT); + editTaskIntent.setData(taskUri); + if (data != null) + { + Bundle extraBundle = new Bundle(); + extraBundle.putParcelable(EditTaskActivity.EXTRA_DATA_CONTENT_SET, data); + editTaskIntent.putExtra(EditTaskActivity.EXTRA_DATA_BUNDLE, extraBundle); + } + startActivity(editTaskIntent); + } - // empty the detail fragment + + @Override + public void onTaskDeleted(@NonNull Uri taskUri) + { + if (taskUri.equals(mSelectedTaskUri)) // Only the selected task can be deleted on the UI, but just to be safe + { + mSelectedTaskUri = null; + if (mTwoPane) + { + // empty the detail fragment + replaceTaskDetailsFragment(EmptyTaskFragment.newInstance(new ValueColor(mLastUsedColor))); + } + } + // The loader will take care of reloading the list and the list view will take care of selecting the next element. + } + + + @Override + public void onTaskCompleted(@NonNull Uri taskUri) + { + /* TODO We delegate to onTaskDeleted() which was used previously for this event, too. + This causes the removal of details view, but the task is selected again if completed tasks are shown. This causes a flash. */ + onTaskDeleted(taskUri); + } + + + @SuppressLint("NewApi") + @Override + public void onListColorLoaded(@NonNull Color color) + { + mLastUsedColor = color.argb(); if (mTwoPane) { - loadTaskDetailFragment(null); + int colorInt = color.argb(); + getSupportActionBar().setBackgroundDrawable(new ColorDrawable(colorInt)); + mTabs.setBackgroundColor(colorInt); + + if (mAppBarLayout != null) + { + mAppBarLayout.setBackgroundColor(colorInt); + } + + if (VERSION.SDK_INT >= 21) + { + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(darkenColor(colorInt)); + } } } @@ -687,20 +735,6 @@ public boolean onQueryTextChange(String query) } - @Override - public ExpandableGroupDescriptor getGroupDescriptor(int pageId) - { - for (AbstractGroupingFactory factory : mGroupingFactories) - { - if (factory.getId() == pageId) - { - return factory.getExpandableGroupDescriptor(); - } - } - return null; - } - - /** * Notifies the search fragment of an update. */ @@ -727,31 +761,6 @@ private int darkenColor(@ColorInt int color) } - @SuppressLint("NewApi") - @Override - public void updateColor(Color color) - { - if (mTwoPane) - { - int colorInt = color.argb(); - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(colorInt)); - mTabs.setBackgroundColor(colorInt); - - if (mAppBarLayout != null) - { - mAppBarLayout.setBackgroundColor(colorInt); - } - - if (VERSION.SDK_INT >= 21) - { - Window window = getWindow(); - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - window.setStatusBarColor(darkenColor(colorInt)); - } - } - } - - public Uri getSelectedTaskUri() { if (mShouldSelectTaskListItem) diff --git a/opentasks/src/main/java/org/dmfs/tasks/TaskListFragment.java b/opentasks/src/main/java/org/dmfs/tasks/TaskListFragment.java index 1ee755df5..87ef80dc7 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/TaskListFragment.java +++ b/opentasks/src/main/java/org/dmfs/tasks/TaskListFragment.java @@ -33,6 +33,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; @@ -49,6 +50,8 @@ import android.widget.ListView; import android.widget.TextView; +import org.dmfs.android.bolts.color.Color; +import org.dmfs.android.bolts.color.elementary.ValueColor; import org.dmfs.android.retentionmagic.SupportFragment; import org.dmfs.android.retentionmagic.annotations.Parameter; import org.dmfs.android.retentionmagic.annotations.Retain; @@ -62,6 +65,7 @@ import org.dmfs.tasks.groupings.filters.ConstantFilter; import org.dmfs.tasks.model.Model; import org.dmfs.tasks.model.Sources; +import org.dmfs.tasks.model.TaskFieldAdapters; import org.dmfs.tasks.utils.ExpandableGroupDescriptor; import org.dmfs.tasks.utils.ExpandableGroupDescriptorAdapter; import org.dmfs.tasks.utils.FlingDetector; @@ -89,7 +93,6 @@ public class TaskListFragment extends SupportFragment private static final String TAG = "org.dmfs.tasks.TaskListFragment"; private final static String ARG_INSTANCE_ID = "instance_id"; - private final static String ARG_TWO_PANE_LAYOUT = "two_pane_layout"; private static final long INTERVAL_LISTVIEW_REDRAW = 60000; @@ -125,14 +128,13 @@ public class TaskListFragment extends SupportFragment @Parameter(key = ARG_INSTANCE_ID) private int mInstancePosition; - @Parameter(key = ARG_TWO_PANE_LAYOUT) - private boolean mTwoPaneLayout; - private Loader mCursorLoader; private String mAuthority; private Uri mSelectedTaskUri; + private boolean mTwoPaneLayout; + /** * The child position to open when the fragment is displayed. **/ @@ -149,11 +151,8 @@ public boolean onChildClick(ExpandableListView parent, View v, int groupPosition { selectChildView(parent, groupPosition, childPosition, true); - if (mExpandableListView.getChoiceMode() == ExpandableListView.CHOICE_MODE_SINGLE) - { - mActivatedPositionGroup = groupPosition; - mActivatedPositionChild = childPosition; - } + mActivatedPositionGroup = groupPosition; + mActivatedPositionChild = childPosition; /* * In contrast to a ListView an ExpandableListView does not set the activated item on it's own. So we have to do that here. */ @@ -189,14 +188,27 @@ public interface Callbacks * * @param taskUri * The {@link Uri} of the selected task. + * @param taskListColor + * the color of the task list (used for toolbars) * @param forceReload * Whether to reload the task or not. */ - void onItemSelected(Uri taskUri, boolean forceReload, int pagePosition); + void onItemSelected(@NonNull Uri taskUri, @NonNull Color taskListColor, boolean forceReload, int pagePosition); - ExpandableGroupDescriptor getGroupDescriptor(int position); + /** + * Called when a task has been removed from the list. + *

+ * TODO It's only called when task is deleted by the swipe out, and not when it is completed. + * It should probably be called that time, too. See https://github.com/dmfs/opentasks/issues/641. + * + * @param taskUri + * the content uri of the task that has been removed + */ + void onItemRemoved(@NonNull Uri taskUri); void onAddNewTask(); + + ExpandableGroupDescriptor getGroupDescriptor(int position); } @@ -216,12 +228,11 @@ public void run() }); - public static TaskListFragment newInstance(int instancePosition, boolean twoPaneLayout) + public static TaskListFragment newInstance(int instancePosition) { TaskListFragment result = new TaskListFragment(); Bundle args = new Bundle(); args.putInt(ARG_INSTANCE_ID, instancePosition); - args.putBoolean(ARG_TWO_PANE_LAYOUT, twoPaneLayout); result.setArguments(args); return result; } @@ -239,6 +250,9 @@ public TaskListFragment() public void onAttach(Activity activity) { super.onAttach(activity); + + mTwoPaneLayout = activity.getResources().getBoolean(R.bool.has_two_panes); + mAuthority = AuthorityUtil.taskAuthority(activity); mAppContext = activity.getBaseContext(); @@ -487,18 +501,11 @@ private void selectChildView(ExpandableListView expandLV, int groupPosition, int { return; } - // TODO: for now we get the id of the task, not the instance, once we support recurrence we'll have to change that - Long selectTaskId = cursor.getLong(cursor.getColumnIndex(Instances.TASK_ID)); - if (selectTaskId != null) - { - // Notify the active callbacks interface (the activity, if the fragment is attached to one) that an item has been selected. - - // TODO: use the instance URI one we support recurrence - Uri taskUri = ContentUris.withAppendedId(Tasks.getContentUri(mAuthority), selectTaskId); - - mCallbacks.onItemSelected(taskUri, force, mInstancePosition); - } + // TODO For now we get the id of the task, not the instance, once we support recurrence we'll have to change that, use instance URI that time + Uri taskUri = ContentUris.withAppendedId(Tasks.getContentUri(mAuthority), (long) TaskFieldAdapters.TASK_ID.get(cursor)); + Color taskListColor = new ValueColor(TaskFieldAdapters.LIST_COLOR.get(cursor)); + mCallbacks.onItemSelected(taskUri, taskListColor, force, mInstancePosition); } } @@ -585,7 +592,7 @@ public void onClick(DialogInterface dialog, int which) // TODO: remove the task in a background task mAppContext.getContentResolver().delete(taskUri, null, null); Snackbar.make(mExpandableListView, getString(R.string.toast_task_deleted, taskTitle), Snackbar.LENGTH_SHORT).show(); - mCallbacks.onItemSelected(null, false, -1); + mCallbacks.onItemRemoved(taskUri); } }).setMessage(getString(R.string.confirm_delete_message_with_title, taskTitle)).create().show(); } diff --git a/opentasks/src/main/java/org/dmfs/tasks/ViewTaskActivity.java b/opentasks/src/main/java/org/dmfs/tasks/ViewTaskActivity.java index 37f1a2d2e..a689935e7 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/ViewTaskActivity.java +++ b/opentasks/src/main/java/org/dmfs/tasks/ViewTaskActivity.java @@ -23,12 +23,15 @@ import android.os.Bundle; import android.os.Handler; import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; import org.dmfs.android.bolts.color.Color; +import org.dmfs.android.bolts.color.colors.PrimaryColor; +import org.dmfs.android.bolts.color.elementary.ValueColor; import org.dmfs.tasks.model.ContentSet; import org.dmfs.tasks.utils.BaseActivity; @@ -43,6 +46,13 @@ public class ViewTaskActivity extends BaseActivity implements ViewTaskFragment.Callback { + /** + * The {@link ColorInt} the toolbars should take while loading the task. Optional parameter. + * {@link android.graphics.Color#TRANSPARENT} also means absent. + */ + public static final String EXTRA_COLOR = "color"; + + @Override protected void onCreate(Bundle savedInstanceState) { @@ -65,7 +75,9 @@ protected void onCreate(Bundle savedInstanceState) if (savedInstanceState == null) { - ViewTaskFragment fragment = ViewTaskFragment.newInstance(getIntent().getData()); + int color = getIntent().getIntExtra(EXTRA_COLOR, 0); + ViewTaskFragment fragment = ViewTaskFragment.newInstance( + getIntent().getData(), color != 0 ? new ValueColor(color) : new PrimaryColor(this)); getSupportFragmentManager().beginTransaction().add(R.id.task_detail_container, fragment).commit(); } } @@ -109,7 +121,7 @@ public boolean onOptionsItemSelected(MenuItem item) @Override - public void onEditTask(Uri taskUri, ContentSet data) + public void onTaskEditRequested(@NonNull Uri taskUri, ContentSet data) { Intent editTaskIntent = new Intent(Intent.ACTION_EDIT); editTaskIntent.setData(taskUri); @@ -124,11 +136,17 @@ public void onEditTask(Uri taskUri, ContentSet data) @Override - public void onDelete(Uri taskUri) + public void onTaskDeleted(@NonNull Uri taskUri) + { + // The task we're showing has been deleted, just finish. + finish(); + } + + + @Override + public void onTaskCompleted(@NonNull Uri taskUri) { - /* - * The task we're showing has been deleted, just finish. - */ + // The task we're showing has been completed, just finish. finish(); } @@ -145,7 +163,7 @@ private int darkenColor(@ColorInt int color) @SuppressLint("NewApi") @Override - public void updateColor(Color color) + public void onListColorLoaded(@NonNull Color color) { if (VERSION.SDK_INT >= 21) diff --git a/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java b/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java index f7e02033c..61b886b0c 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java +++ b/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java @@ -26,9 +26,10 @@ import android.content.Intent; import android.content.res.ColorStateList; import android.database.ContentObserver; -import android.graphics.Color; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout.OnOffsetChangedListener; import android.support.design.widget.CoordinatorLayout; @@ -50,7 +51,8 @@ import android.view.animation.AlphaAnimation; import android.widget.TextView; -import org.dmfs.android.bolts.color.colors.ValueColor; +import org.dmfs.android.bolts.color.Color; +import org.dmfs.android.bolts.color.elementary.ValueColor; import org.dmfs.android.retentionmagic.SupportFragment; import org.dmfs.android.retentionmagic.annotations.Parameter; import org.dmfs.android.retentionmagic.annotations.Retain; @@ -83,6 +85,7 @@ public class ViewTaskFragment extends SupportFragment implements OnModelLoadedListener, OnContentChangeListener, OnMenuItemClickListener, OnOffsetChangedListener { private final static String ARG_URI = "uri"; + private static final String ARG_STARTING_COLOR = "starting_color"; /** * A set of values that may affect the recurrence set of a task. If one of these values changes we have to submit all of them. @@ -156,22 +159,24 @@ public class ViewTaskFragment extends SupportFragment public interface Callback { /** - * This is called to instruct the Activity to call the editor for a specific task. + * Called when user pressed 'edit' for the task. * * @param taskUri * The {@link Uri} of the task to edit. * @param data * The task data that belongs to the {@link Uri}. This is purely an optimization and may be null. */ - void onEditTask(Uri taskUri, ContentSet data); + void onTaskEditRequested(@NonNull Uri taskUri, @Nullable ContentSet data); /** - * This is called to inform the Activity that a task has been deleted. - * - * @param taskUri - * The {@link Uri} of the deleted task. Note that the Uri is likely to be invalid at the time of calling this method. + * Called when the task has been deleted by the user. */ - void onDelete(Uri taskUri); + void onTaskDeleted(@NonNull Uri taskUri); + + /** + * Called when the task has been marked completed by the user. + */ + void onTaskCompleted(@NonNull Uri taskUri); /** * Notifies the listener about the list color of the current task. @@ -179,28 +184,25 @@ public interface Callback * @param color * The color. */ - void updateColor(org.dmfs.android.bolts.color.Color color); - } - - - public static ViewTaskFragment newInstance(Uri uri) - { - ViewTaskFragment result = new ViewTaskFragment(); - if (uri != null) - { - Bundle args = new Bundle(); - args.putParcelable(ARG_URI, uri); - result.setArguments(args); - } - return result; + void onListColorLoaded(@NonNull Color color); } /** - * Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon screen orientation changes). + * @param taskContentUri + * the content uri of the task to display + * @param startingColor + * The color that is used for the toolbars until the actual task color is loaded. (If available provide the actual task list color, otherwise the + * primary color.) */ - public ViewTaskFragment() + public static ViewTaskFragment newInstance(@NonNull Uri taskContentUri, @NonNull Color startingColor) { + ViewTaskFragment fragment = new ViewTaskFragment(); + Bundle args = new Bundle(); + args.putParcelable(ARG_URI, taskContentUri); + args.putInt(ARG_STARTING_COLOR, startingColor.argb()); + fragment.setArguments(args); + return fragment; } @@ -243,11 +245,6 @@ public void onDestroyView() mAppContext.getContentResolver().unregisterContentObserver(mObserver); } - if (mContent != null) - { - mContent.removeAllViews(); - } - if (mDetailView != null) { // remove values, to ensure all listeners get released @@ -274,15 +271,12 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mFloatingActionButton = (FloatingActionButton) mRootView.findViewById(R.id.floating_action_button); showFloatingActionButton(false); - mFloatingActionButton.setOnClickListener(new View.OnClickListener() - { + mFloatingActionButton.setOnClickListener(v -> completeTask()); - @Override - public void onClick(View v) - { - completeTask(); - } - }); + // Update the toolbar color until the actual is loaded for the task + + mListColor = new ValueColor(getArguments().getInt(ARG_STARTING_COLOR)).argb(); + updateColor(); mRestored = savedInstanceState != null; @@ -339,6 +333,17 @@ private void persistTask() } + /* + TODO Refactor, simplify ViewTaskFragment now that it is only for displaying a single task once. + Ticket for this: https://github.com/dmfs/opentasks/issues/628 + + Earlier this Fragment was responsible for displaying no task (empty content) + and also updating itself to show a newly selected one, using this loadUri() method which was public at the time. + After refactorings, the Fragment is now only responsible to load an existing task once, for the task uri that is received in the args. + As a result this class can now be simplified, for example potentially removing all uri == null checks. + */ + + /** * Load the task with the given {@link Uri} in the detail view. *

@@ -349,7 +354,7 @@ private void persistTask() * @param uri * The {@link Uri} of the task to show. */ - public void loadUri(Uri uri) + private void loadUri(Uri uri) { showFloatingActionButton(false); @@ -537,7 +542,7 @@ public boolean onOptionsItemSelected(MenuItem item) if (itemId == R.id.edit_task) { // open editor for this task - mCallback.onEditTask(mTaskUri, mContentSet); + mCallback.onTaskEditRequested(mTaskUri, mContentSet); return true; } else if (itemId == R.id.delete_task) @@ -559,7 +564,8 @@ public void onClick(DialogInterface dialog, int which) { // TODO: remove the task in a background task mContentSet.delete(mAppContext); - mCallback.onDelete(mTaskUri); + mCallback.onTaskDeleted(mTaskUri); + mTaskUri = null; } } }).setMessage(R.string.confirm_delete_message).create().show(); @@ -622,8 +628,7 @@ private void completeTask() persistTask(); Snackbar.make(getActivity().getWindow().getDecorView(), getString(R.string.toast_task_completed, TaskFieldAdapters.TITLE.get(mContentSet)), Snackbar.LENGTH_SHORT).show(); - // at present we just handle it like deletion, i.e. close the task in phone mode, do nothing in tablet mode - mCallback.onDelete(mTaskUri); + mCallback.onTaskCompleted(mTaskUri); if (mShowFloatingActionButton) { // hide fab in two pane mode @@ -641,7 +646,7 @@ private void updateColor() { // the FAB gets a slightly lighter color to stand out a bit more. If it's too light, we darken it instead. float[] hsv = new float[3]; - Color.colorToHSV(mListColor, hsv); + android.graphics.Color.colorToHSV(mListColor, hsv); if (hsv[2] * (1 - hsv[1]) < 0.4) { hsv[2] *= 1.2; @@ -650,7 +655,8 @@ private void updateColor() { hsv[2] /= 1.2; } - mFloatingActionButton.setBackgroundTintList(new ColorStateList(new int[][] { new int[] { 0 } }, new int[] { Color.HSVToColor(hsv) })); + mFloatingActionButton.setBackgroundTintList( + new ColorStateList(new int[][] { new int[] { 0 } }, new int[] { android.graphics.Color.HSVToColor(hsv) })); } } @@ -662,7 +668,7 @@ public void onContentLoaded(ContentSet contentSet) if (contentSet.containsKey(Tasks.ACCOUNT_TYPE)) { mListColor = TaskFieldAdapters.LIST_COLOR.get(contentSet); - ((Callback) getActivity()).updateColor(new ValueColor(mListColor)); + ((Callback) getActivity()).onListColorLoaded(new ValueColor(mListColor)); updateColor(); @@ -715,7 +721,7 @@ public void onContentLoaded(ContentSet contentSet) @Override public void onChange(boolean selfChange) { - if (mContentSet != null) + if (mContentSet != null && mTaskUri != null) { // reload the task mContentSet.update(mAppContext, CONTENT_VALUE_MAPPER); diff --git a/opentasks/src/main/res/anim/openttasks_fade_exit.xml b/opentasks/src/main/res/anim/openttasks_fade_exit.xml new file mode 100644 index 000000000..f2dbd30b4 --- /dev/null +++ b/opentasks/src/main/res/anim/openttasks_fade_exit.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/opentasks/src/main/res/layout/fragment_task_view_detail.xml b/opentasks/src/main/res/layout/fragment_task_view_detail.xml index 456449c48..000c31854 100644 --- a/opentasks/src/main/res/layout/fragment_task_view_detail.xml +++ b/opentasks/src/main/res/layout/fragment_task_view_detail.xml @@ -127,6 +127,7 @@ diff --git a/opentasks/src/main/res/layout/opentasks_fragment_empty_task.xml b/opentasks/src/main/res/layout/opentasks_fragment_empty_task.xml index 636cb2f45..5b9a72797 100644 --- a/opentasks/src/main/res/layout/opentasks_fragment_empty_task.xml +++ b/opentasks/src/main/res/layout/opentasks_fragment_empty_task.xml @@ -3,6 +3,7 @@ android:layout_height="match_parent">