Move runtime directory state into DirState object.

Preserve last seen sort info in dirState.

Change-Id: I47691288791f24d297953e777108853d27214e0c
diff --git a/src/com/android/documentsui/base/Shared.java b/src/com/android/documentsui/base/Shared.java
index 6b8f2d0..ebff4fb 100644
--- a/src/com/android/documentsui/base/Shared.java
+++ b/src/com/android/documentsui/base/Shared.java
@@ -30,7 +30,6 @@
 import android.view.WindowManager;
 
 import com.android.documentsui.R;
-import com.android.documentsui.R.bool;
 
 import java.text.Collator;
 import java.util.ArrayList;
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 997fc8a..1d29ba8 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -136,24 +136,27 @@
 
     private final Model mModel = new Model();
     private final EventListener<Model.Update> mModelUpdateListener = new ModelUpdateListener();
+
+    // This dependency is informally "injected" from the owning Activity in our onCreate method.
+    private FragmentTuner mTuner;
+
+    // This dependency is informally "injected" from the owning Activity in our onCreate method.
+    private MenuManager mMenuManager;
+
     private MultiSelectManager mSelectionMgr;
     private ActionModeController mActionModeController;
     private SelectionMetadata mSelectionMetadata;
     private UserInputHandler<InputEvent> mInputHandler;
     private FocusManager mFocusManager;
-
+    private @Nullable BandController mBandController;
+    private DragHoverListener mDragHoverListener;
     private IconHelper mIconHelper;
     private SwipeRefreshLayout mRefreshLayout;
     private View mEmptyView;
     private RecyclerView mRecView;
     private View mFileList;
 
-    private String mStateKey;
-
-    private SortDimension mLastSortDimension;
-    private @SortDirection int mLastSortDirection;
     private DocumentsAdapter mAdapter;
-    private FragmentTuner mTuner;
     private DocumentClipper mClipper;
     private GridLayoutManager mLayout;
     private int mColumnCount = 1;  // This will get updated when layout changes.
@@ -162,24 +165,11 @@
     private MessageBar mMessageBar;
     private View mProgressBar;
 
-    // Directory fragment state is defined by: root, document, query, type, selection
-    private @ResultType int mType = TYPE_NORMAL;
-    private RootInfo mRoot;
-    // Null when viewing Recents directory.
-    private @Nullable DocumentInfo mDocument;
-    private String mQuery = null;
+    private DirState mDirState;
+
     // Note, we use !null to indicate that selection was restored (from rotation).
     // So don't fiddle with this field unless you've got the bigger picture in mind.
     private @Nullable Selection mRestoredSelection = null;
-    // Here we save the clip details of moveTo/copyTo actions when picker shows up.
-    // This will be written to saved instance.
-    private @Nullable FileOperation mPendingOperation;
-    private boolean mSearchMode = false;
-
-    private @Nullable BandController mBandController;
-
-    private DragHoverListener mDragHoverListener;
-    private MenuManager mMenuManager;
 
     private SortModel.UpdateListener mSortListener = (model, updateType) -> {
         // Only when sort order has changed do we need to trigger another loading.
@@ -248,13 +238,9 @@
         // Read arguments when object created for the first time.
         // Restore state if fragment recreated.
         Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState;
-        mRoot = args.getParcelable(Shared.EXTRA_ROOT);
-        mDocument = args.getParcelable(Shared.EXTRA_DOC);
-        mStateKey = buildStateKey(mRoot, mDocument);
-        mQuery = args.getString(Shared.EXTRA_QUERY);
-        mType = args.getInt(Shared.EXTRA_TYPE);
-        mSearchMode = args.getBoolean(Shared.EXTRA_SEARCH_MODE);
-        mPendingOperation = args.getParcelable(FileOperationService.EXTRA_OPERATION);
+
+        mDirState = new DirState();
+        mDirState.restore(args);
 
         // Restore any selection we may have squirreled away in retained state.
         @Nullable RetainedState retained = getBaseActivity().getRetainedState();
@@ -312,7 +298,7 @@
                 mRecView);
 
         final BaseActivity activity = getBaseActivity();
-        mTuner = activity.getFragmentTuner(mModel, mSelectionMgr, mSearchMode);
+        mTuner = activity.getFragmentTuner(mModel, mSelectionMgr, mDirState.mSearchMode);
         mMenuManager = activity.getMenuManager();
 
         if (state.allowMultiple) {
@@ -370,13 +356,13 @@
 
         final ActivityManager am = (ActivityManager) context.getSystemService(
                 Context.ACTIVITY_SERVICE);
-        boolean svelte = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
+        boolean svelte = am.isLowRamDevice() && (mDirState.mType == TYPE_RECENT_OPEN);
         mIconHelper.setThumbnailsEnabled(!svelte);
 
         // If mDocument is null, we sort it by last modified by default because it's in Recents.
         final boolean prefersLastModified =
-                (mDocument != null)
-                        ? (mDocument.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0
+                (mDirState.mDocument != null)
+                        ? (mDirState.mDocument.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0
                         : true;
         // Call this before adding the listener to avoid restarting the loader one more time
         state.sortModel.setDefaultDimension(
@@ -406,7 +392,7 @@
         final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
         getView().saveHierarchyState(container);
         final State state = getDisplayState();
-        state.dirState.put(mStateKey, container);
+        state.dirState.put(mDirState.getStateKey(), container);
     }
 
     public void retainState(RetainedState state) {
@@ -417,12 +403,7 @@
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
-        outState.putInt(Shared.EXTRA_TYPE, mType);
-        outState.putParcelable(Shared.EXTRA_ROOT, mRoot);
-        outState.putParcelable(Shared.EXTRA_DOC, mDocument);
-        outState.putString(Shared.EXTRA_QUERY, mQuery);
-        outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
-        outState.putParcelable(FileOperationService.EXTRA_OPERATION, mPendingOperation);
+        mDirState.save(outState);
     }
 
     @Override
@@ -461,8 +442,7 @@
 
     private void handleCopyResult(int resultCode, Intent data) {
 
-        FileOperation operation = mPendingOperation;
-        mPendingOperation = null;
+        FileOperation operation = mDirState.claimPendingOperation();
 
         if (resultCode == Activity.RESULT_CANCELED || data == null) {
             // User pressed the back button or otherwise cancelled the destination pick. Don't
@@ -867,7 +847,7 @@
         }
 
         Uri srcParent = getDisplayState().stack.peek().derivedUri;
-        mPendingOperation = new FileOperation.Builder()
+        mDirState.mPendingOperation = new FileOperation.Builder()
                 .withOpType(mode)
                 .withSrcParent(srcParent)
                 .withSrcs(srcs)
@@ -1261,7 +1241,7 @@
             mProgressBar.setVisibility(mModel.isLoading() ? View.VISIBLE : View.GONE);
 
             if (mModel.isEmpty()) {
-                if (mSearchMode) {
+                if (mDirState.mSearchMode) {
                     showNoResults(getDisplayState().stack.root);
                 } else {
                     showEmptyDirectory();
@@ -1314,10 +1294,7 @@
             String query) {
         DirectoryFragment df = get(fm);
 
-        df.mQuery = query;
-        df.mRoot = root;
-        df.mDocument = doc;
-        df.mSearchMode =  query != null;
+        df.mDirState.update(root, doc, query);
         df.getLoaderManager().restartLoader(LOADER_ID, null, df);
     }
 
@@ -1325,11 +1302,8 @@
             String query) {
         if (DEBUG) Log.d(TAG, "Reloading directory: " + DocumentInfo.debugString(doc));
         DirectoryFragment df = get(fm);
-        df.mType = type;
-        df.mQuery = query;
-        df.mRoot = root;
-        df.mDocument = doc;
-        df.mSearchMode =  query != null;
+
+        df.mDirState.update(type, root, doc, query);
         df.getLoaderManager().restartLoader(LOADER_ID, null, df);
     }
 
@@ -1366,14 +1340,6 @@
         ft.commitAllowingStateLoss();
     }
 
-    private static String buildStateKey(RootInfo root, DocumentInfo doc) {
-        final StringBuilder builder = new StringBuilder();
-        builder.append(root != null ? root.authority : "null").append(';');
-        builder.append(root != null ? root.rootId : "null").append(';');
-        builder.append(doc != null ? doc.documentId : "null");
-        return builder.toString();
-    }
-
     public static @Nullable DirectoryFragment get(FragmentManager fm) {
         // TODO: deal with multiple directories shown at once
         Fragment fragment = fm.findFragmentById(getFragmentId());
@@ -1408,38 +1374,39 @@
         State state = getDisplayState();
 
         Uri contentsUri;
-        switch (mType) {
+        switch (mDirState.mType) {
             case TYPE_NORMAL:
-                contentsUri = mSearchMode ? DocumentsContract.buildSearchDocumentsUri(
-                        mRoot.authority, mRoot.rootId, mQuery)
+                contentsUri = mDirState.mSearchMode ? DocumentsContract.buildSearchDocumentsUri(
+                        mDirState.mRoot.authority, mDirState.mRoot.rootId, mDirState.mQuery)
                         : DocumentsContract.buildChildDocumentsUri(
-                                mDocument.authority, mDocument.documentId);
+                                mDirState.mDocument.authority, mDirState.mDocument.documentId);
                 if (mTuner.managedModeEnabled()) {
                     contentsUri = DocumentsContract.setManageMode(contentsUri);
                 }
                 if (DEBUG) Log.d(TAG, "Creating new directory loader for: "
-                        + DocumentInfo.debugString(mDocument));
+                        + DocumentInfo.debugString(mDirState.mDocument));
                 return new DirectoryLoader(
-                        context, mRoot, mDocument, contentsUri, state.sortModel,
-                        mSearchMode);
+                        context, mDirState.mRoot, mDirState.mDocument, contentsUri, state.sortModel,
+                        mDirState.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);
 
             default:
-                throw new IllegalStateException("Unknown type " + mType);
+                throw new IllegalStateException("Unknown type " + mDirState.mType);
         }
     }
 
     @Override
     public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
-        if (DEBUG) Log.d(TAG, "Loader has finished for: " + DocumentInfo.debugString(mDocument));
+        if (DEBUG) Log.d(TAG, "Loader has finished for: "
+                + DocumentInfo.debugString(mDirState.mDocument));
         assert(result != null);
 
         if (!isAdded()) return;
 
-        if (mSearchMode) {
+        if (mDirState.mSearchMode) {
             Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SEARCH);
         }
 
@@ -1458,21 +1425,22 @@
         }
 
         // Restore any previous instance state
-        final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
+        final SparseArray<Parcelable> container = state.dirState.remove(mDirState.getStateKey());
         final int curSortedDimensionId = state.sortModel.getSortedDimensionId();
+
         final SortDimension curSortedDimension =
                 state.sortModel.getDimensionById(curSortedDimensionId);
         if (container != null && !getArguments().getBoolean(Shared.EXTRA_IGNORE_STATE, false)) {
             getView().restoreHierarchyState(container);
-        } else if (mLastSortDimension != curSortedDimension
-                || mLastSortDimension == null
-                || mLastSortDirection != curSortedDimension.getSortDirection()) {
+        } else if (mDirState.mLastSortDimensionId != curSortedDimension.getId()
+                || mDirState.mLastSortDimensionId == SortModel.SORT_DIMENSION_ID_UNKNOWN
+                || mDirState.mLastSortDirection != curSortedDimension.getSortDirection()) {
             // Scroll to the top if the sort order actually changed.
             mRecView.smoothScrollToPosition(0);
         }
 
-        mLastSortDimension = curSortedDimension;
-        mLastSortDirection = curSortedDimension.getSortDirection();
+        mDirState.mLastSortDimensionId = curSortedDimension.getId();
+        mDirState.mLastSortDirection = curSortedDimension.getSortDirection();
 
         if (mRefreshLayout.isRefreshing()) {
             new Handler().postDelayed(
@@ -1483,9 +1451,82 @@
 
     @Override
     public void onLoaderReset(Loader<DirectoryResult> loader) {
-        if (DEBUG) Log.d(TAG, "Resetting loader for: " + DocumentInfo.debugString(mDocument));
+        if (DEBUG) Log.d(TAG, "Resetting loader for: "
+                + DocumentInfo.debugString(mDirState.mDocument));
         mModel.onLoaderReset();
 
         mRefreshLayout.setRefreshing(false);
     }
-  }
+
+    private static final class DirState {
+
+        private static final String EXTRA_SORT_DIMENSION_ID = "sortDimensionId";
+        private static final String EXTRA_SORT_DIRECTION = "sortDirection";
+
+        // Directory fragment state is defined by: root, document, query, type, selection
+        private @ResultType int mType = TYPE_NORMAL;
+        private RootInfo mRoot;
+        // Null when viewing Recents directory.
+        private @Nullable DocumentInfo mDocument;
+        private String mQuery = null;
+        // Here we save the clip details of moveTo/copyTo actions when picker shows up.
+        // This will be written to saved instance.
+        private @Nullable FileOperation mPendingOperation;
+        private boolean mSearchMode;
+        private int mLastSortDimensionId = SortModel.SORT_DIMENSION_ID_UNKNOWN;
+        private @SortDirection int mLastSortDirection;
+
+        private String mStateKey;
+
+        public void restore(Bundle bundle) {
+            mRoot = bundle.getParcelable(Shared.EXTRA_ROOT);
+            mDocument = bundle.getParcelable(Shared.EXTRA_DOC);
+            mQuery = bundle.getString(Shared.EXTRA_QUERY);
+            mType = bundle.getInt(Shared.EXTRA_TYPE);
+            mSearchMode = bundle.getBoolean(Shared.EXTRA_SEARCH_MODE);
+            mPendingOperation = bundle.getParcelable(FileOperationService.EXTRA_OPERATION);
+            mLastSortDimensionId = bundle.getInt(EXTRA_SORT_DIMENSION_ID);
+            mLastSortDirection = bundle.getInt(EXTRA_SORT_DIRECTION);
+        }
+
+        public void save(Bundle bundle) {
+            bundle.putInt(Shared.EXTRA_TYPE, mType);
+            bundle.putParcelable(Shared.EXTRA_ROOT, mRoot);
+            bundle.putParcelable(Shared.EXTRA_DOC, mDocument);
+            bundle.putString(Shared.EXTRA_QUERY, mQuery);
+            bundle.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
+            bundle.putParcelable(FileOperationService.EXTRA_OPERATION, mPendingOperation);
+            bundle.putInt(EXTRA_SORT_DIMENSION_ID, mLastSortDimensionId);
+            bundle.putInt(EXTRA_SORT_DIRECTION, mLastSortDirection);
+        }
+
+        public FileOperation claimPendingOperation() {
+            FileOperation op = mPendingOperation;
+            mPendingOperation = null;
+            return op;
+        }
+
+        public void update(int type, RootInfo root, DocumentInfo doc, String query) {
+            mType = type;
+            update(root, doc, query);
+        }
+
+        public void update(RootInfo root, DocumentInfo doc, String query) {
+            mQuery = query;
+            mRoot = root;
+            mDocument = doc;
+            mSearchMode =  query != null;
+        }
+
+        private String getStateKey() {
+            if (mStateKey == null) {
+                final StringBuilder builder = new StringBuilder();
+                builder.append(mRoot != null ? mRoot.authority : "null").append(';');
+                builder.append(mRoot != null ? mRoot.rootId : "null").append(';');
+                builder.append(mDocument != null ? mDocument.documentId : "null");
+                mStateKey = builder.toString();
+            }
+            return mStateKey;
+        }
+    }
+}