Move sort headers out of directory layout.
This allowed some simplification of the sort component lifecycle.
Also move progress bar.
Also, don't reset model when we get a loader-reset when last results
reported "loading". Fixes a bug where progress bar was being hidden too early.
Finally do a little pixel tweaking on sort headers and breadcrumb.
Change-Id: I49a2c70443edacfe911f692054e289bf1d5f6ecb
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index 6543dee..2c8c127 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -111,6 +111,7 @@
DrawerController mDrawer;
NavigationViewManager mNavigator;
List<EventListener> mEventListeners = new ArrayList<>();
+ SortController mSortController;
private final String mTag;
private final ContentObserver mRootsCacheObserver = new ContentObserver(new Handler()) {
@@ -126,7 +127,6 @@
private boolean mNavDrawerHasFocus;
private long mStartTime;
- private SortController mSortController;
public abstract void onDocumentPicked(DocumentInfo doc, Model model);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -183,7 +183,8 @@
mNavigator = new NavigationViewManager(mDrawer, toolbar, mState, this, breadcrumb);
- mSortController = new SortController(mState.sortModel, this);
+ mSortController = SortController.create(this, mState.derivedMode, mState.sortModel);
+
// Base classes must update result in their onCreate.
setResult(Activity.RESULT_CANCELED);
@@ -215,10 +216,6 @@
super.onDestroy();
}
- SortController getSortController() {
- return mSortController;
- }
-
private State getState(@Nullable Bundle icicle) {
if (icicle != null) {
State state = icicle.<State>getParcelable(Shared.EXTRA_STATE);
diff --git a/src/com/android/documentsui/DocumentsActivity.java b/src/com/android/documentsui/DocumentsActivity.java
index 017f7ae..8f9c787 100644
--- a/src/com/android/documentsui/DocumentsActivity.java
+++ b/src/com/android/documentsui/DocumentsActivity.java
@@ -406,7 +406,7 @@
public FragmentTuner createFragmentTuner() {
// Currently DocumentsTuner maintains a state specific to the fragment instance. Because of
// that, we create a new instance everytime it is needed
- return new DocumentsTuner(this, getDisplayState(), getSortController());
+ return new DocumentsTuner(this, getDisplayState(), mSortController);
}
@Override
diff --git a/src/com/android/documentsui/FilesActivity.java b/src/com/android/documentsui/FilesActivity.java
index 1fefcb5..46a663c 100644
--- a/src/com/android/documentsui/FilesActivity.java
+++ b/src/com/android/documentsui/FilesActivity.java
@@ -483,7 +483,7 @@
@Override
public FragmentTuner createFragmentTuner() {
- return new FilesTuner(this, getDisplayState(), getSortController());
+ return new FilesTuner(this, getDisplayState(), mSortController);
}
@Override
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index c4b4711..0022634 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -194,8 +194,6 @@
private DragScrollListener mOnDragListener;
private MenuManager mMenuManager;
- private TableHeaderController mTableHeaderController;
- private DropdownSortWidgetController mDropdownSortWidgetController;
private SortModel.UpdateListener mSortListener = (model, updateType) -> {
// Only when sort order has changed do we need to trigger another loading.
if ((updateType & SortModel.UPDATE_TYPE_SORTING) != 0) {
@@ -210,7 +208,8 @@
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
mMessageBar = MessageBar.create(getChildFragmentManager());
- mProgressBar = view.findViewById(R.id.progressbar);
+ mProgressBar = getActivity().findViewById(R.id.progressbar);
+ assert(mProgressBar != null);
mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh_layout);
mRefreshLayout.setOnRefreshListener(this);
@@ -235,10 +234,6 @@
mRecView.setOnDragListener(mOnDragListener);
mEmptyView.setOnDragListener(mOnDragListener);
- mTableHeaderController = TableHeaderController.create(view.findViewById(R.id.table_header));
- mDropdownSortWidgetController =
- DropdownSortWidgetController.create(view.findViewById(R.id.dropdown_sort_widget));
-
return view;
}
@@ -386,10 +381,6 @@
public void onStart() {
super.onStart();
- mTuner.mSortController.manage(
- mTableHeaderController,
- mDropdownSortWidgetController,
- getDisplayState().derivedMode);
// Add listener to update contents on sort model change
getDisplayState().sortModel.addListener(mSortListener);
}
@@ -398,10 +389,6 @@
public void onStop() {
super.onStop();
- // Remove listener to avoid leak.
- mTuner.mSortController.clean(
- mTableHeaderController,
- mDropdownSortWidgetController);
getDisplayState().sortModel.removeListener(mSortListener);
// Remember last scroll location
@@ -690,15 +677,14 @@
public void onSelectionChanged() {
mSelectionMgr.getSelection(mSelected);
if (mSelected.size() > 0) {
- if (DEBUG) Log.d(TAG, "Maybe starting action mode.");
- if (mActionMode == null) {
- if (DEBUG) Log.d(TAG, "Yeah. Starting action mode.");
+ if (mActionMode == null) {
+ if (DEBUG) Log.d(TAG, "Starting action mode.");
mActionMode = getActivity().startActionMode(this);
}
updateActionMenu();
} else {
- if (DEBUG) Log.d(TAG, "Finishing action mode.");
if (mActionMode != null) {
+ if (DEBUG) Log.d(TAG, "Finishing action mode.");
mActionMode.finish();
}
}
@@ -1512,6 +1498,8 @@
private final class ModelUpdateListener implements Model.UpdateListener {
@Override
public void onModelUpdate(Model model) {
+ if (DEBUG) Log.d(TAG, "Received model update. Loading=" + model.isLoading());
+
if (model.info != null || model.error != null) {
mMessageBar.setInfo(model.info);
mMessageBar.setError(model.error);
@@ -1637,6 +1625,7 @@
public static void showDirectory(
FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
+ if (DEBUG) Log.d(TAG, "Showing directory: " + doc.derivedUri);
create(fm, TYPE_NORMAL, root, doc, null, anim);
}
@@ -1657,6 +1646,7 @@
public static void reload(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
String query) {
+ if (DEBUG) Log.d(TAG, "Reloading directory: " + doc.derivedUri);
DirectoryFragment df = get(fm);
df.mType = type;
df.mQuery = query;
@@ -1668,6 +1658,7 @@
public static void create(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
String query, int anim) {
+ if (DEBUG) Log.d(TAG, "Creating new fragment for directory: " + doc.derivedUri);
final Bundle args = new Bundle();
args.putInt(Shared.EXTRA_TYPE, type);
args.putParcelable(Shared.EXTRA_ROOT, root);
@@ -1723,6 +1714,7 @@
@Override
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
+ if (DEBUG) Log.d(TAG, "Creating new loader for: " + mDocument.derivedUri);
Context context = getActivity();
State state = getDisplayState();
@@ -1736,10 +1728,12 @@
if (mTuner.managedModeEnabled()) {
contentsUri = DocumentsContract.setManageMode(contentsUri);
}
+ if (DEBUG) Log.d(TAG, "Creating new loader for: " + mDocument.derivedUri);
return new DirectoryLoader(
context, mType, mRoot, mDocument, contentsUri, state.sortModel,
mSearchMode);
case TYPE_RECENT_OPEN:
+ if (DEBUG) Log.d(TAG, "Creating new loader recents.");
final RootsCache roots = DocumentsApplication.getRootsCache(context);
return new RecentsLoader(context, roots, state);
@@ -1750,6 +1744,9 @@
@Override
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
+ if (DEBUG) Log.d(TAG, "Loader has finished for: " + mDocument.derivedUri);
+ assert(result != null);
+
if (!isAdded()) return;
if (mSearchMode) {
@@ -1798,7 +1795,8 @@
@Override
public void onLoaderReset(Loader<DirectoryResult> loader) {
- mModel.update(null);
+ if (DEBUG) Log.d(TAG, "Resetting loader for: " + mDocument.derivedUri);
+ mModel.onLoaderReset();
mRefreshLayout.setRefreshing(false);
}
diff --git a/src/com/android/documentsui/dirlist/DropdownSortWidgetController.java b/src/com/android/documentsui/dirlist/DropdownSortWidgetController.java
index 0a5b310..1ed42c6 100644
--- a/src/com/android/documentsui/dirlist/DropdownSortWidgetController.java
+++ b/src/com/android/documentsui/dirlist/DropdownSortWidgetController.java
@@ -16,7 +16,6 @@
package com.android.documentsui.dirlist;
-import android.annotation.Nullable;
import android.annotation.StringRes;
import android.view.Gravity;
import android.view.Menu;
@@ -44,8 +43,7 @@
private static final int LEVEL_UPWARD = 0;
private static final int LEVEL_DOWNWARD = 10000;
- private SortModel mModel;
-
+ private final SortModel mModel;
private final View mWidget;
private final TextView mDimensionButton;
private final PopupMenu mMenu;
@@ -55,7 +53,8 @@
private final OnClickListener mArrowClickListener = this::onChangeDirection;
private final UpdateListener mUpdateListener = this::onModelUpdate;
- private DropdownSortWidgetController(View widget) {
+ public DropdownSortWidgetController(SortModel model, View widget) {
+ mModel = model;
mWidget = widget;
mDimensionButton = (TextView) mWidget.findViewById(R.id.sort_dimen_dropdown);
@@ -63,26 +62,11 @@
mMenu.setOnMenuItemClickListener(this::onSelectDimension);
mArrow = (ImageView) mWidget.findViewById(R.id.sort_arrow);
- }
- static @Nullable DropdownSortWidgetController create(@Nullable View widget) {
- return widget == null ? null : new DropdownSortWidgetController(widget);
- }
+ populateMenuItems();
+ onModelUpdate(mModel, SortModel.UPDATE_TYPE_UNSPECIFIED);
- @Override
- public void setModel(SortModel model) {
- if (mModel != null) {
- mModel.removeListener(mUpdateListener);
- }
-
- mModel = model;
-
- if (mModel != null) {
- populateMenuItems();
- onModelUpdate(mModel, SortModel.UPDATE_TYPE_UNSPECIFIED);
-
- mModel.addListener(mUpdateListener);
- }
+ mModel.addListener(mUpdateListener);
}
@Override
diff --git a/src/com/android/documentsui/dirlist/FragmentTuner.java b/src/com/android/documentsui/dirlist/FragmentTuner.java
index 8dfd490..c4f37eb 100644
--- a/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -42,6 +42,10 @@
final SortController mSortController;
public FragmentTuner(Context context, State state, SortController sortController) {
+ assert(context != null);
+ assert(state != null);
+ assert(sortController != null);
+
mContext = context;
mState = state;
mSortController = sortController;
diff --git a/src/com/android/documentsui/dirlist/Model.java b/src/com/android/documentsui/dirlist/Model.java
index 1a171bd..5ddad31 100644
--- a/src/com/android/documentsui/dirlist/Model.java
+++ b/src/com/android/documentsui/dirlist/Model.java
@@ -79,21 +79,28 @@
}
}
- void update(DirectoryResult result) {
- if (DEBUG) Log.i(TAG, "Updating model with new result set.");
-
- if (result == null) {
- mCursor = null;
- mCursorCount = 0;
- mIds = new String[0];
- mPositions.clear();
- info = null;
- error = null;
- doc = null;
- mIsLoading = false;
- notifyUpdateListeners();
- return;
+ void onLoaderReset() {
+ if (mIsLoading) {
+ if (DEBUG) Log.w(TAG, "Received unexpected loader reset while in loading state.");
}
+ }
+
+ private void reset() {
+ mCursor = null;
+ mCursorCount = 0;
+ mIds = new String[0];
+ mPositions.clear();
+ info = null;
+ error = null;
+ doc = null;
+ mIsLoading = false;
+ notifyUpdateListeners();
+ }
+
+ void update(DirectoryResult result) {
+ assert(result != null);
+
+ if (DEBUG) Log.i(TAG, "Updating model with new result set.");
if (result.exception != null) {
Log.e(TAG, "Error while loading directory contents", result.exception);
diff --git a/src/com/android/documentsui/dirlist/header/TableHeaderController.java b/src/com/android/documentsui/dirlist/header/TableHeaderController.java
index a48f963..458c527 100644
--- a/src/com/android/documentsui/dirlist/header/TableHeaderController.java
+++ b/src/com/android/documentsui/dirlist/header/TableHeaderController.java
@@ -16,15 +16,15 @@
package com.android.documentsui.dirlist.header;
-import android.annotation.Nullable;
import android.view.View;
import com.android.documentsui.R;
+import com.android.documentsui.sorting.SortController;
import com.android.documentsui.sorting.SortDimension;
import com.android.documentsui.sorting.SortModel;
-import com.android.documentsui.sorting.SortModel.UpdateType;
import com.android.documentsui.sorting.SortModel.SortDimensionId;
-import com.android.documentsui.sorting.SortController;
+
+import javax.annotation.Nullable;
/**
* View controller for table header that associates header cells in table header and columns.
@@ -37,37 +37,39 @@
private final HeaderCell mSizeCell;
private final HeaderCell mDateCell;
- private final SortModel.UpdateListener mModelUpdaterListener = this::onModelUpdate;
+ // We assign this here porque each method reference creates a new object
+ // instance (which is wasteful).
private final View.OnClickListener mOnCellClickListener = this::onCellClicked;
- private SortModel mModel;
+ private final SortModel mModel;
- public static @Nullable TableHeaderController create(@Nullable View tableHeader) {
- return (tableHeader == null) ? null : new TableHeaderController(tableHeader);
- }
+ private TableHeaderController(SortModel sortModel, View tableHeader) {
+ assert(sortModel != null);
+ assert(tableHeader != null);
- private TableHeaderController(View tableHeader) {
+ mModel = sortModel;
mTableHeader = tableHeader;
mTitleCell = (HeaderCell) tableHeader.findViewById(android.R.id.title);
mSummaryCell = (HeaderCell) tableHeader.findViewById(android.R.id.summary);
mSizeCell = (HeaderCell) tableHeader.findViewById(R.id.size);
mDateCell = (HeaderCell) tableHeader.findViewById(R.id.date);
+
+ onModelUpdate(mModel, SortModel.UPDATE_TYPE_UNSPECIFIED);
+
+ mModel.addListener(this::onModelUpdate);
}
- @Override
- public void setModel(@Nullable SortModel model) {
- if (mModel != null) {
- mModel.removeListener(mModelUpdaterListener);
- }
+ private void onModelUpdate(SortModel model, int updateTypeUnspecified) {
+ bindCell(mTitleCell, SortModel.SORT_DIMENSION_ID_TITLE);
+ bindCell(mSummaryCell, SortModel.SORT_DIMENSION_ID_SUMMARY);
+ bindCell(mSizeCell, SortModel.SORT_DIMENSION_ID_SIZE);
+ bindCell(mDateCell, SortModel.SORT_DIMENSION_ID_DATE);
+ }
- mModel = model;
-
- if (mModel != null) {
- onModelUpdate(mModel, SortModel.UPDATE_TYPE_UNSPECIFIED);
-
- mModel.addListener(mModelUpdaterListener);
- }
+ public static @Nullable TableHeaderController create(
+ SortModel sortModel, @Nullable View tableHeader) {
+ return (tableHeader == null) ? null : new TableHeaderController(sortModel, tableHeader);
}
@Override
@@ -75,14 +77,8 @@
mTableHeader.setVisibility(visibility);
}
- private void onModelUpdate(SortModel model, @UpdateType int updateType) {
- bindCell(mTitleCell, SortModel.SORT_DIMENSION_ID_TITLE);
- bindCell(mSummaryCell, SortModel.SORT_DIMENSION_ID_SUMMARY);
- bindCell(mSizeCell, SortModel.SORT_DIMENSION_ID_SIZE);
- bindCell(mDateCell, SortModel.SORT_DIMENSION_ID_DATE);
- }
-
private void bindCell(HeaderCell cell, @SortDimensionId int id) {
+ assert(cell != null);
SortDimension dimension = mModel.getDimensionById(id);
cell.setTag(dimension);
diff --git a/src/com/android/documentsui/sorting/SortController.java b/src/com/android/documentsui/sorting/SortController.java
index b9b3140..4b1aa45 100644
--- a/src/com/android/documentsui/sorting/SortController.java
+++ b/src/com/android/documentsui/sorting/SortController.java
@@ -17,10 +17,11 @@
package com.android.documentsui.sorting;
import android.annotation.Nullable;
-import android.content.Context;
+import android.app.Activity;
import android.view.View;
import com.android.documentsui.Metrics;
+import com.android.documentsui.R;
import com.android.documentsui.State;
import com.android.documentsui.State.ViewMode;
import com.android.documentsui.dirlist.DropdownSortWidgetController;
@@ -31,103 +32,73 @@
* appear in different locations in the UI, like the menu, above the file list (pinned) and embedded
* at the top of file list... and maybe other places too.
*/
-public class SortController {
+public final class SortController {
- private static final WidgetController DUMMY_CONTROLLER = new WidgetController() {};
+ private final WidgetController mDropdownController;
+ private final @Nullable WidgetController mTableHeaderController;
- private final SortModel mModel;
- private final Context mContext;
+ public SortController(
+ WidgetController dropdownController,
+ @Nullable WidgetController tableHeaderController) {
- private WidgetController mTableHeaderController = DUMMY_CONTROLLER;
- private WidgetController mDropdownController = DUMMY_CONTROLLER;
-
- public SortController(SortModel model, Context context) {
- mModel = model;
- mContext = context.getApplicationContext();
-
- mModel.setMetricRecorder(this::recordSortMetric);
- }
-
- public void manage(
- @Nullable TableHeaderController headerController,
- @Nullable DropdownSortWidgetController gridController,
- @ViewMode int mode) {
- assert(mTableHeaderController == DUMMY_CONTROLLER);
- assert(mDropdownController == DUMMY_CONTROLLER);
-
- if (headerController != null) {
- mTableHeaderController = headerController;
- mTableHeaderController.setModel(mModel);
- }
-
- if (gridController != null) {
- mDropdownController = gridController;
- mDropdownController.setModel(mModel);
- }
-
- onViewModeChanged(mode);
- }
-
- public void clean(
- @Nullable TableHeaderController headerController,
- @Nullable DropdownSortWidgetController gridController) {
- assert(headerController == null || mTableHeaderController == headerController);
- assert(gridController == null || mDropdownController == gridController);
-
- if (headerController != null) {
- headerController.setModel(null);
- }
- mTableHeaderController = DUMMY_CONTROLLER;
-
- if (gridController != null) {
- gridController.setModel(null);
- }
- mDropdownController = DUMMY_CONTROLLER;
+ assert(dropdownController != null);
+ mDropdownController = dropdownController;
+ mTableHeaderController = tableHeaderController;
}
public void onViewModeChanged(@ViewMode int mode) {
- setVisibilityPerViewMode(mTableHeaderController, mode, View.GONE, View.VISIBLE);
-
- if (mTableHeaderController == DUMMY_CONTROLLER) {
+ // in phone layouts we only ever have the dropdown sort controller.
+ if (mTableHeaderController == null) {
mDropdownController.setVisibility(View.VISIBLE);
- } else {
- setVisibilityPerViewMode(mDropdownController, mode, View.VISIBLE, View.GONE);
+ return;
}
- }
- private static void setVisibilityPerViewMode(
- WidgetController controller,
- @ViewMode int mode,
- int visibilityInGrid,
- int visibilityInList) {
+ // in tablet mode, we have fancy pants tabular header.
switch (mode) {
case State.MODE_GRID:
- controller.setVisibility(visibilityInGrid);
+ case State.MODE_UNKNOWN:
+ mTableHeaderController.setVisibility(View.GONE);
+ mDropdownController.setVisibility(View.VISIBLE);
break;
case State.MODE_LIST:
- controller.setVisibility(visibilityInList);
+ mTableHeaderController.setVisibility(View.VISIBLE);
+ mDropdownController.setVisibility(View.GONE);
break;
- default:
- throw new IllegalArgumentException("Unknown view mode: " + mode + ".");
}
}
- private void recordSortMetric(SortDimension dimension) {
- switch (dimension.getId()) {
- case SortModel.SORT_DIMENSION_ID_TITLE:
- Metrics.logUserAction(mContext, Metrics.USER_ACTION_SORT_NAME);
- break;
- case SortModel.SORT_DIMENSION_ID_SIZE:
- Metrics.logUserAction(mContext, Metrics.USER_ACTION_SORT_SIZE);
- break;
- case SortModel.SORT_DIMENSION_ID_DATE:
- Metrics.logUserAction(mContext, Metrics.USER_ACTION_SORT_DATE);
- break;
- }
+ public static SortController create(
+ Activity activity,
+ @ViewMode int initialMode,
+ SortModel sortModel) {
+
+ sortModel.setMetricRecorder((SortDimension dimension) -> {
+ switch (dimension.getId()) {
+ case SortModel.SORT_DIMENSION_ID_TITLE:
+ Metrics.logUserAction(activity, Metrics.USER_ACTION_SORT_NAME);
+ break;
+ case SortModel.SORT_DIMENSION_ID_SIZE:
+ Metrics.logUserAction(activity, Metrics.USER_ACTION_SORT_SIZE);
+ break;
+ case SortModel.SORT_DIMENSION_ID_DATE:
+ Metrics.logUserAction(activity, Metrics.USER_ACTION_SORT_DATE);
+ break;
+ }
+ });
+
+ SortController controller = new SortController(
+ new DropdownSortWidgetController(
+ sortModel,
+ activity.findViewById(R.id.dropdown_sort_widget)),
+ TableHeaderController.create(
+ sortModel,
+ activity.findViewById(R.id.table_header)));
+
+ controller.onViewModeChanged(initialMode);
+ return controller;
}
public interface WidgetController {
- default void setModel(SortModel model) {}
default void setVisibility(int visibility) {}
}
}