diff options
| author | 2016-02-17 13:50:42 -0800 | |
|---|---|---|
| committer | 2016-02-22 09:36:17 -0800 | |
| commit | 893390bd580eca39ecd693cb0d76c7bc9b36a11d (patch) | |
| tree | 8d870efe19863ae95776b6e6ff838ce245fe9e5a | |
| parent | 855d408e17612beadfc637496f50b03a7149515a (diff) | |
Directory fragment refactoring.
First attempt to to refactor fragments handling, state and app lifecycle.
The goal was to simplify code by using android built lifecycle
mechanism, eliminate bugs caused by multiple creation of the fragment,
see the performance impact and give some fundament for refactoring of
fragments and activities in the app.
Search view manager:
* Remove curentSearch from state
* Restore search from saved state (ex. after rotation)
* Rename file to give the better overview of its purpose
Directory fragment:
* Store selection state in a bundle
* Remove double creation of fragment
* Use loaders to reload content when possible
* Keep info about state inside the object
* Refactor available types of fragment to be normal and recents
* Make search type a mode possibly available in all types
* Remove search being invoked from refresh method
* Do search by reloading fragments content instead of recreation as
an example
Other:
* Fix window title maybe
Bug: 26968405, 27101786
Change-Id: I58f36cd0a3e3a6ec98996cd8aac16e10e425e1fe
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java | 40 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java | 11 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java | 5 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java | 13 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java | 12 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java (renamed from packages/DocumentsUI/src/com/android/documentsui/SearchManager.java) | 75 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/Shared.java | 42 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/State.java | 5 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java | 263 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java | 11 | ||||
| -rw-r--r-- | packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java | 2 |
11 files changed, 280 insertions, 199 deletions
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index b67a6915add9..a34dd55032e6 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -23,9 +23,11 @@ import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_LEAVE; import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE; import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE; import static com.android.internal.util.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import android.app.Activity; import android.app.Fragment; +import android.app.FragmentManager; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -46,7 +48,7 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Spinner; -import com.android.documentsui.SearchManager.SearchManagerListener; +import com.android.documentsui.SearchViewManager.SearchManagerListener; import com.android.documentsui.State.ViewMode; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.dirlist.Model; @@ -64,14 +66,12 @@ import java.util.concurrent.Executor; public abstract class BaseActivity extends Activity implements SearchManagerListener, NavigationView.Environment { - static final String EXTRA_STATE = "state"; - // See comments where this const is referenced for details. private static final int DRAWER_NO_FIDDLE_DELAY = 1500; State mState; RootsCache mRoots; - SearchManager mSearchManager; + SearchViewManager mSearchManager; DrawerController mDrawer; NavigationView mNavigator; @@ -121,7 +121,7 @@ public abstract class BaseActivity extends Activity } }); - mSearchManager = new SearchManager(this); + mSearchManager = new SearchViewManager(this, icicle); DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar); setActionBar(toolbar); @@ -141,6 +141,7 @@ public abstract class BaseActivity extends Activity boolean showMenu = super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.activity, menu); + mNavigator.update(); mSearchManager.install((DocumentsToolbar) findViewById(R.id.toolbar)); return showMenu; @@ -188,7 +189,7 @@ public abstract class BaseActivity extends Activity private State getState(@Nullable Bundle icicle) { if (icicle != null) { - State state = icicle.<State>getParcelable(EXTRA_STATE); + State state = icicle.<State>getParcelable(Shared.EXTRA_STATE); if (DEBUG) Log.d(mTag, "Recovered existing state object: " + state); return state; } @@ -224,6 +225,9 @@ public abstract class BaseActivity extends Activity } void onRootPicked(RootInfo root) { + // Clicking on the current root removes search + mSearchManager.cancelSearch(); + // Skip refreshing if root nor directory didn't change if (root.equals(getCurrentRoot()) && mState.stack.size() == 1) { return; @@ -233,7 +237,6 @@ public abstract class BaseActivity extends Activity // Clear entire backstack and start in new root mState.onRootChanged(root); - mSearchManager.update(root); // Recents is always in memory, so we just load it directly. // Otherwise we delegate loading data from disk to a task @@ -370,18 +373,18 @@ public abstract class BaseActivity extends Activity * e.g. The current directory name displayed on the action bar won't get updated. */ @Override - public void onSearchChanged() { - refreshDirectory(ANIM_NONE); + public void onSearchChanged(@Nullable String query) { + // We should not get here if root is not searchable + checkState(canSearchRoot()); + reloadSearch(query); } - /** - * Called when search query changed. - * Updates the state object. - * @param query - New query - */ - @Override - public void onSearchQueryChanged(String query) { - mState.currentSearch = query; + private void reloadSearch(String query) { + FragmentManager fm = getFragmentManager(); + RootInfo root = getCurrentRoot(); + DocumentInfo cwd = getCurrentDirectory(); + + DirectoryFragment.reloadSearch(fm, root, cwd, query); } final List<String> getExcludedAuthorities() { @@ -486,7 +489,8 @@ public abstract class BaseActivity extends Activity @Override protected void onSaveInstanceState(Bundle state) { super.onSaveInstanceState(state); - state.putParcelable(EXTRA_STATE, mState); + state.putParcelable(Shared.EXTRA_STATE, mState); + mSearchManager.onSaveInstanceState(state); } @Override diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index b0542b9eb5be..13b7b14600eb 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -53,19 +53,22 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private final RootInfo mRoot; private final Uri mUri; private final int mUserSortOrder; + private final boolean mSearchMode; private DocumentInfo mDoc; private CancellationSignal mSignal; private DirectoryResult mResult; + public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri, - int userSortOrder) { + int userSortOrder, boolean inSearchMode) { super(context, ProviderExecutor.forAuthority(root.authority)); mType = type; mRoot = root; mUri = uri; mUserSortOrder = userSortOrder; mDoc = doc; + mSearchMode = inSearchMode; } @Override @@ -83,7 +86,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { final DirectoryResult result = new DirectoryResult(); // Use default document when searching - if (mType == DirectoryFragment.TYPE_SEARCH) { + if (mSearchMode) { final Uri docUri = DocumentsContract.buildDocumentUri( mRoot.authority, mRoot.documentId); try { @@ -106,7 +109,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { } // Search always uses ranking from provider - if (mType == DirectoryFragment.TYPE_SEARCH) { + if (mSearchMode) { result.sortOrder = State.SORT_ORDER_UNKNOWN; } @@ -127,7 +130,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1); - if (mType == DirectoryFragment.TYPE_SEARCH) { + if (mSearchMode) { // Filter directories out of search results, for now cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index ec7dde9230c4..9812495da0fd 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -290,13 +290,8 @@ public class DocumentsActivity extends BaseActivity { mState.derivedMode = visualMimes ? State.MODE_GRID : State.MODE_LIST; } } else { - if (mSearchManager.isSearching()) { - // Ongoing search - DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim); - } else { // Normal boring directory DirectoryFragment.showDirectory(fm, root, cwd, anim); - } } // Forget any replacement target diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java index f7a45e218a82..9609dee16489 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java @@ -16,8 +16,10 @@ package com.android.documentsui; +import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.State.ACTION_MANAGE; import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE; +import static com.android.internal.util.Preconditions.checkState; import android.app.Activity; import android.app.Fragment; @@ -119,17 +121,14 @@ public class DownloadsActivity extends BaseActivity { final RootInfo root = getCurrentRoot(); final DocumentInfo cwd = getCurrentDirectory(); + if (DEBUG) checkState(!mSearchManager.isSearching()); + // If started in manage roots mode, there has to be a cwd (i.e. the root dir of the managed // root). Preconditions.checkNotNull(cwd); - if (mState.currentSearch != null) { - // Ongoing search - DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim); - } else { - // Normal boring directory - DirectoryFragment.showDirectory(fm, root, cwd, anim); - } + // Normal boring directory + DirectoryFragment.showDirectory(fm, root, cwd, anim); } @Override diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java index c0faba306f6e..b8b50e47483f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java @@ -82,7 +82,6 @@ public class FilesActivity extends BaseActivity { if (mState.restored) { if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData()); - refreshCurrentRootAndDirectory(ANIM_NONE); } else if (!mState.stack.isEmpty()) { // If a non-empty stack is present in our state it was read (presumably) // from EXTRA_STACK intent extra. In this case, we'll skip other means of @@ -248,16 +247,13 @@ public class FilesActivity extends BaseActivity { final RootInfo root = getCurrentRoot(); final DocumentInfo cwd = getCurrentDirectory(); + if (DEBUG) checkState(!mSearchManager.isSearching()); + if (cwd == null) { DirectoryFragment.showRecentsOpen(fm, anim); } else { - if (mState.currentSearch != null) { - // Ongoing search - DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim); - } else { - // Normal boring directory - DirectoryFragment.showDirectory(fm, root, cwd, anim); - } + // Normal boring directory + DirectoryFragment.showDirectory(fm, root, cwd, anim); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java index 69f54c77fe8b..049686289eee 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java @@ -16,6 +16,8 @@ package com.android.documentsui; +import android.annotation.Nullable; +import android.os.Bundle; import android.provider.DocumentsContract.Root; import android.text.TextUtils; import android.util.Log; @@ -31,28 +33,27 @@ import com.android.documentsui.model.RootInfo; /** * Manages searching UI behavior. */ -final class SearchManager implements +final class SearchViewManager implements SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener { public interface SearchManagerListener { - void onSearchChanged(); - - void onSearchQueryChanged(String query); + void onSearchChanged(@Nullable String query); } public static final String TAG = "SearchManger"; private SearchManagerListener mListener; - private String currentSearch; private boolean mSearchExpanded; + private String mCurrentSearch; private boolean mIgnoreNextClose; private DocumentsToolbar mActionBar; private MenuItem mMenu; private SearchView mView; - public SearchManager(SearchManagerListener listener) { + public SearchViewManager(SearchManagerListener listener, @Nullable Bundle savedState) { mListener = listener; + mCurrentSearch = savedState != null ? savedState.getString(Shared.EXTRA_QUERY) : null; } public void setSearchMangerListener(SearchManagerListener listener) { @@ -69,6 +70,8 @@ final class SearchManager implements mView.setOnCloseListener(this); mView.setOnSearchClickListener(this); mView.setOnQueryTextFocusChangeListener(this); + + restoreSearch(); } /** @@ -80,12 +83,12 @@ final class SearchManager implements return; } - if (currentSearch != null) { + if (mCurrentSearch != null) { mMenu.expandActionView(); mView.setIconified(false); mView.clearFocus(); - mView.setQuery(currentSearch, false); + mView.setQuery(mCurrentSearch, false); } else { mView.clearFocus(); if (!mView.isIconified()) { @@ -108,13 +111,11 @@ final class SearchManager implements return; } - mMenu.setVisible(visible); if (!visible) { - currentSearch = null; - if (mListener != null) { - mListener.onSearchQueryChanged(currentSearch); - } + mCurrentSearch = null; } + + mMenu.setVisible(visible); } /** @@ -133,8 +134,23 @@ final class SearchManager implements return false; } + private void restoreSearch() { + if (isSearching()) { + onSearchExpanded(); + mView.setIconified(false); + mView.setQuery(mCurrentSearch, false); + mView.clearFocus(); + } + } + + private void onSearchExpanded() { + mSearchExpanded = true; + mView.setBackgroundColor( + mView.getResources().getColor(R.color.menu_search_background, null)); + } + boolean isSearching() { - return currentSearch != null; + return mCurrentSearch != null; } boolean isExpanded() { @@ -142,6 +158,14 @@ final class SearchManager implements } /** + * Called when owning activity is saving state to be used to restore state during creation. + * @param state Bundle to save state too + */ + public void onSaveInstanceState(Bundle state) { + state.putString(Shared.EXTRA_QUERY, mCurrentSearch); + } + + /** * Clears the search. Clears the SearchView background color. Triggers refreshing of the * directory content. * @return True if the default behavior of clearing/dismissing SearchView should be overridden. @@ -159,11 +183,10 @@ final class SearchManager implements mView.getResources().getColor(android.R.color.transparent, null)); // Refresh the directory if a search was done - if (currentSearch != null) { - currentSearch = null; + if (mCurrentSearch != null) { + mCurrentSearch = null; if (mListener != null) { - mListener.onSearchQueryChanged(currentSearch); - mListener.onSearchChanged(); + mListener.onSearchChanged(mCurrentSearch); } } return false; @@ -176,18 +199,15 @@ final class SearchManager implements */ @Override public void onClick(View v) { - mSearchExpanded = true; - mView.setBackgroundColor( - mView.getResources().getColor(R.color.menu_search_background, null)); + onSearchExpanded(); } @Override public boolean onQueryTextSubmit(String query) { - currentSearch = query; + mCurrentSearch = query; mView.clearFocus(); if (mListener != null) { - mListener.onSearchQueryChanged(currentSearch); - mListener.onSearchChanged(); + mListener.onSearchChanged(mCurrentSearch); } return true; } @@ -195,7 +215,7 @@ final class SearchManager implements @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { - if (currentSearch == null) { + if (mCurrentSearch == null) { mView.setIconified(true); } else if (TextUtils.isEmpty(mView.getQuery())) { cancelSearch(); @@ -207,4 +227,9 @@ final class SearchManager implements public boolean onQueryTextChange(String newText) { return false; } + + String getCurrentSearch() { + return mCurrentSearch; + } + } diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java index b90a1194596a..a288fe8f30ab 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java +++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java @@ -37,10 +37,50 @@ public final class Shared { * specifies if the destination directory needs to create new directory or not. */ public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY"; + public static final String EXTRA_STACK = "com.android.documentsui.STACK"; + + /** + * Extra flag used to store query of type String in the bundle. + */ + public static final String EXTRA_QUERY = "query"; + + /** + * Extra flag used to store state of type State in the bundle. + */ + public static final String EXTRA_STATE = "state"; + + /** + * Extra flag used to store type of DirectoryFragment's type ResultType type in the bundle. + */ + public static final String EXTRA_TYPE = "type"; + + /** + * Extra flag used to store root of type RootInfo in the bundle. + */ + public static final String EXTRA_ROOT = "root"; + + /** + * Extra flag used to store document of DocumentInfo type in the bundle. + */ + public static final String EXTRA_DOC = "document"; + + /** + * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle. + */ + public static final String EXTRA_SELECTION = "selection"; + + /** + * Extra flag used to store DirectoryFragment's search mode of boolean type in the bundle. + */ + public static final String EXTRA_SEARCH_MODE = "searchMode"; + + /** + * Extra flag used to store DirectoryFragment's ignore state of boolean type in the bundle. + */ + public static final String EXTRA_IGNORE_STATE = "ignoreState"; public static final boolean DEBUG = true; public static final String TAG = "Documents"; - public static final String EXTRA_STACK = "com.android.documentsui.STACK"; /** diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java index 0948ab1e7571..2ecbdf615b0e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/State.java +++ b/packages/DocumentsUI/src/com/android/documentsui/State.java @@ -105,9 +105,6 @@ public class State implements android.os.Parcelable { private boolean mInitialRootChanged; private boolean mInitialDocChanged; - /** Currently active search, overriding any stack. */ - public String currentSearch; - /** Instance state for every shown directory */ public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>(); @@ -186,7 +183,6 @@ public class State implements android.os.Parcelable { out.writeInt(showAdvanced ? 1 : 0); out.writeInt(restored ? 1 : 0); DurableUtils.writeToParcel(out, stack); - out.writeString(currentSearch); out.writeMap(dirState); out.writeParcelable(selectedDocuments, 0); out.writeList(selectedDocumentsForCopy); @@ -217,7 +213,6 @@ public class State implements android.os.Parcelable { state.showAdvanced = in.readInt() != 0; state.restored = in.readInt() != 0; DurableUtils.readFromParcel(in, state.stack); - state.currentSearch = in.readString(); in.readMap(state.dirState, loader); state.selectedDocuments = in.readParcelable(loader); in.readList(state.selectedDocumentsForCopy, loader); diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 4583decc14a8..669eb71e4d02 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -113,19 +113,26 @@ import java.util.List; /** * Display the documents inside a single directory. */ -public class DirectoryFragment extends Fragment implements DocumentsAdapter.Environment { +public class DirectoryFragment extends Fragment + implements DocumentsAdapter.Environment, LoaderCallbacks<DirectoryResult> { @IntDef(flag = true, value = { TYPE_NORMAL, - TYPE_SEARCH, TYPE_RECENT_OPEN }) @Retention(RetentionPolicy.SOURCE) public @interface ResultType {} public static final int TYPE_NORMAL = 1; - public static final int TYPE_SEARCH = 2; - public static final int TYPE_RECENT_OPEN = 3; + public static final int TYPE_RECENT_OPEN = 2; + @IntDef(flag = true, value = { + ANIM_NONE, + ANIM_SIDE, + ANIM_LEAVE, + ANIM_ENTER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AnimationType {} public static final int ANIM_NONE = 1; public static final int ANIM_SIDE = 2; public static final int ANIM_LEAVE = 3; @@ -146,12 +153,6 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi private static final int DELETE_JOB_DELAY = 5500; private static final int EMPTY_REVEAL_DURATION = 250; - private static final String EXTRA_TYPE = "type"; - private static final String EXTRA_ROOT = "root"; - private static final String EXTRA_DOC = "doc"; - private static final String EXTRA_QUERY = "query"; - private static final String EXTRA_IGNORE_STATE = "ignoreState"; - private Model mModel; private MultiSelectManager mSelectionManager; private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener(); @@ -164,12 +165,10 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi private RecyclerView mRecView; private ListeningGestureDetector mGestureDetector; - private @ResultType int mType = TYPE_NORMAL; private String mStateKey; private int mLastSortOrder = SORT_ORDER_UNKNOWN; private DocumentsAdapter mAdapter; - private LoaderCallbacks<DirectoryResult> mCallbacks; private FragmentTuner mTuner; private DocumentClipper mClipper; private GridLayoutManager mLayout; @@ -178,6 +177,14 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi 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; + private DocumentInfo mDocument; + private String mQuery = null; + private Selection mSelection = null; + private boolean mSearchMode = false; + @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -226,9 +233,16 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi final Context context = getActivity(); final State state = getDisplayState(); - final RootInfo root = getArguments().getParcelable(EXTRA_ROOT); - final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC); - mStateKey = buildStateKey(root, doc); + // 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); + mSelection = args.getParcelable(Shared.EXTRA_SELECTION); + mSearchMode = args.getBoolean(Shared.EXTRA_SEARCH_MODE); mIconHelper = new IconHelper(context, MODE_GRID); @@ -248,13 +262,6 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi mRecView.addOnItemTouchListener(mGestureDetector); - // final here because we'll manually bump the listener iwhen we had an initial selection, - // but only after the model is fully loaded. - final SelectionModeListener selectionListener = new SelectionModeListener(); - final Selection initialSelection = state.selectedDocuments.hasDirectoryKey(mStateKey) - ? state.selectedDocuments - : null; - // TODO: instead of inserting the view into the constructor, extract listener-creation code // and set the listener on the view after the fact. Then the view doesn't need to be passed // into the selection manager. @@ -264,9 +271,9 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi state.allowMultiple ? MultiSelectManager.MODE_MULTIPLE : MultiSelectManager.MODE_SINGLE, - initialSelection); + null); - mSelectionManager.addCallback(selectionListener); + mSelectionManager.addCallback(new SelectionModeListener()); mModel = new Model(); mModel.addUpdateListener(mAdapter); @@ -275,8 +282,6 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi // Make sure this is done after the RecyclerView is set up. mFocusManager = new FocusManager(context, mRecView, mModel); - mType = getArguments().getInt(EXTRA_TYPE); - mTuner = FragmentTuner.pick(getContext(), state); mClipper = new DocumentClipper(context); @@ -286,7 +291,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi hideGridTitles = MimePredicate.mimeMatches( MimePredicate.VISUAL_MIMES, state.acceptMimes); } else { - hideGridTitles = (doc != null) && doc.isGridTitlesHidden(); + hideGridTitles = (mDocument != null) && mDocument.isGridTitlesHidden(); } GridDocumentHolder.setHideTitles(hideGridTitles); @@ -295,86 +300,20 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi boolean svelte = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN); mIconHelper.setThumbnailsEnabled(!svelte); - mCallbacks = new LoaderCallbacks<DirectoryResult>() { - @Override - public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) { - final String query = getArguments().getString(EXTRA_QUERY); - - Uri contentsUri; - switch (mType) { - case TYPE_NORMAL: - contentsUri = DocumentsContract.buildChildDocumentsUri( - doc.authority, doc.documentId); - if (state.action == ACTION_MANAGE) { - contentsUri = DocumentsContract.setManageMode(contentsUri); - } - return new DirectoryLoader( - context, mType, root, doc, contentsUri, state.userSortOrder); - case TYPE_SEARCH: - contentsUri = DocumentsContract.buildSearchDocumentsUri( - root.authority, root.rootId, query); - if (state.action == ACTION_MANAGE) { - contentsUri = DocumentsContract.setManageMode(contentsUri); - } - return new DirectoryLoader( - context, mType, root, doc, contentsUri, state.userSortOrder); - case TYPE_RECENT_OPEN: - final RootsCache roots = DocumentsApplication.getRootsCache(context); - return new RecentsLoader(context, roots, state); - default: - throw new IllegalStateException("Unknown type " + mType); - } - } - - @Override - public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) { - if (!isAdded()) return; - - mModel.update(result); - state.derivedSortOrder = result.sortOrder; - - updateDisplayState(); - - if (initialSelection != null) { - selectionListener.onSelectionChanged(); - } - - // Restore any previous instance state - final SparseArray<Parcelable> container = state.dirState.remove(mStateKey); - if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) { - getView().restoreHierarchyState(container); - } else if (mLastSortOrder != state.derivedSortOrder) { - // The derived sort order takes the user sort order into account, but applies - // directory-specific defaults when the user doesn't explicitly set the sort - // order. Scroll to the top if the sort order actually changed. - mRecView.smoothScrollToPosition(0); - } - - mLastSortOrder = state.derivedSortOrder; - - mTuner.onModelLoaded(mModel, mType); - } - - @Override - public void onLoaderReset(Loader<DirectoryResult> loader) { - mModel.update(null); - } - }; - // Kick off loader at least once - getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks); + getLoaderManager().restartLoader(LOADER_ID, null, this); } @Override public void onSaveInstanceState(Bundle outState) { - State state = getDisplayState(); - if (mSelectionManager.hasSelection()) { - mSelectionManager.getSelection(state.selectedDocuments); - state.selectedDocuments.setDirectoryKey(mStateKey); - if (!state.selectedDocuments.isEmpty()) { - if (DEBUG) Log.d(TAG, "Persisted selection: " + state.selectedDocuments); - } - } + 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.putParcelable(Shared.EXTRA_SELECTION, mSelectionManager.getSelection()); + outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode); } @Override @@ -449,7 +388,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi public void onSortOrderChanged() { // Sort order is implemented as a sorting wrapper around directory // results. So when sort order changes, we force a reload of the directory. - getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks); + getLoaderManager().restartLoader(LOADER_ID, null, this); } public void onViewModeChanged() { @@ -1342,7 +1281,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE); if (model.isEmpty()) { - if (getDisplayState().currentSearch != null) { + if (mSearchMode) { showNoResults(getDisplayState().stack.root); } else { showEmptyDirectory(); @@ -1468,32 +1407,50 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi public static void showDirectory( FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) { - show(fm, TYPE_NORMAL, root, doc, null, anim); + create(fm, TYPE_NORMAL, root, doc, null, anim); } - public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) { - show(fm, TYPE_SEARCH, root, null, query, anim); + public static void showRecentsOpen(FragmentManager fm, int anim) { + create(fm, TYPE_RECENT_OPEN, null, null, null, anim); } - public static void showRecentsOpen(FragmentManager fm, int anim) { - show(fm, TYPE_RECENT_OPEN, null, null, null, anim); + public static void reloadSearch(FragmentManager fm, RootInfo root, DocumentInfo doc, + String query) { + DirectoryFragment df = get(fm); + + df.mQuery = query; + df.mRoot = root; + df.mDocument = doc; + df.mSearchMode = query != null; + df.getLoaderManager().restartLoader(LOADER_ID, null, df); + } + + public static void reload(FragmentManager fm, int type, RootInfo root, DocumentInfo doc, + String query) { + DirectoryFragment df = get(fm); + df.mType = type; + df.mQuery = query; + df.mRoot = root; + df.mDocument = doc; + df.mSearchMode = query != null; + df.getLoaderManager().restartLoader(LOADER_ID, null, df); } - private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc, + public static void create(FragmentManager fm, int type, RootInfo root, DocumentInfo doc, String query, int anim) { final Bundle args = new Bundle(); - args.putInt(EXTRA_TYPE, type); - args.putParcelable(EXTRA_ROOT, root); - args.putParcelable(EXTRA_DOC, doc); - args.putString(EXTRA_QUERY, query); + args.putInt(Shared.EXTRA_TYPE, type); + args.putParcelable(Shared.EXTRA_ROOT, root); + args.putParcelable(Shared.EXTRA_DOC, doc); + args.putString(Shared.EXTRA_QUERY, query); final FragmentTransaction ft = fm.beginTransaction(); switch (anim) { case ANIM_SIDE: - args.putBoolean(EXTRA_IGNORE_STATE, true); + args.putBoolean(Shared.EXTRA_IGNORE_STATE, true); break; case ANIM_ENTER: - args.putBoolean(EXTRA_IGNORE_STATE, true); + args.putBoolean(Shared.EXTRA_IGNORE_STATE, true); ft.setCustomAnimations(R.animator.dir_enter, R.animator.dir_frozen); break; case ANIM_LEAVE: @@ -1504,7 +1461,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi final DirectoryFragment fragment = new DirectoryFragment(); fragment.setArguments(args); - ft.replace(R.id.container_directory, fragment); + ft.replace(getFragmentId(), fragment); ft.commitAllowingStateLoss(); } @@ -1518,9 +1475,77 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi public static @Nullable DirectoryFragment get(FragmentManager fm) { // TODO: deal with multiple directories shown at once - Fragment fragment = fm.findFragmentById(R.id.container_directory); + Fragment fragment = fm.findFragmentById(getFragmentId()); return fragment instanceof DirectoryFragment ? (DirectoryFragment) fragment : null; } -} + + private static int getFragmentId() { + return R.id.container_directory; + } + + @Override + public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) { + Context context = getActivity(); + State state = getDisplayState(); + + Uri contentsUri; + switch (mType) { + case TYPE_NORMAL: + contentsUri = mSearchMode ? DocumentsContract.buildSearchDocumentsUri( + mRoot.authority, mRoot.rootId, mQuery) + : DocumentsContract.buildChildDocumentsUri( + mDocument.authority, mDocument.documentId); + if (state.action == ACTION_MANAGE) { + contentsUri = DocumentsContract.setManageMode(contentsUri); + } + return new DirectoryLoader( + context, mType, mRoot, mDocument, contentsUri, state.userSortOrder, mSearchMode); + case TYPE_RECENT_OPEN: + final RootsCache roots = DocumentsApplication.getRootsCache(context); + return new RecentsLoader(context, roots, state); + default: + throw new IllegalStateException("Unknown type " + mType); + } + } + + @Override + public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) { + if (!isAdded()) return; + + State state = getDisplayState(); + + mAdapter.notifyDataSetChanged(); + mModel.update(result); + + state.derivedSortOrder = result.sortOrder; + + updateLayout(state.derivedMode); + + if (mSelection != null) { + mSelectionManager.setItemsSelected(mSelection.toList(), true); + } + + // Restore any previous instance state + final SparseArray<Parcelable> container = state.dirState.remove(mStateKey); + if (container != null && !getArguments().getBoolean(Shared.EXTRA_IGNORE_STATE, false)) { + getView().restoreHierarchyState(container); + } else if (mLastSortOrder != state.derivedSortOrder) { + // The derived sort order takes the user sort order into account, but applies + // directory-specific defaults when the user doesn't explicitly set the sort + // order. Scroll to the top if the sort order actually changed. + mRecView.smoothScrollToPosition(0); + } + + mLastSortOrder = state.derivedSortOrder; + + mTuner.onModelLoaded(mModel, mType, mSearchMode); + + } + + @Override + public void onLoaderReset(Loader<DirectoryResult> loader) { + mModel.update(null); + } + } diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java index a9b0fd181d8b..914f71e15709 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java @@ -84,7 +84,7 @@ public abstract class FragmentTuner { return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType); } - abstract void onModelLoaded(Model model, @ResultType int resultType); + abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch); /** * Provides support for Platform specific specializations of DirectoryFragment. @@ -166,7 +166,7 @@ public abstract class FragmentTuner { } @Override - void onModelLoaded(Model model, @ResultType int resultType) { + void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) { // When launched into empty recents, show drawer if (resultType == DirectoryFragment.TYPE_RECENT_OPEN && model.isEmpty() @@ -211,7 +211,7 @@ public abstract class FragmentTuner { } @Override - void onModelLoaded(Model model, @ResultType int resultType) {} + void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {} } /** @@ -248,11 +248,10 @@ public abstract class FragmentTuner { } @Override - void onModelLoaded(Model model, @ResultType int resultType) { + void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) { if (DEBUG) Log.d(TAG, "Handling model loaded. Has Location shcnage: " + mState.initialLocationHasChanged()); // When launched into empty root, open drawer. - if (model.isEmpty() && !mState.initialLocationHasChanged() - && resultType != DirectoryFragment.TYPE_SEARCH) { + if (model.isEmpty() && !mState.initialLocationHasChanged() && !isSearch) { if (DEBUG) Log.d(TAG, "Showing roots drawer cuz stuffs empty."); // This noops on layouts without drawer, so no need to guard. diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java index c8b6f8528272..19268d759a47 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java @@ -687,7 +687,7 @@ public final class MultiSelectManager { * Returns an unordered array of selected positions (including any * provisional selections current in effect). */ - private List<String> toList() { + public List<String> toList() { ArrayList<String> selection = new ArrayList<String>(mSelection); selection.addAll(mProvisionalSelection); return selection; |