diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index b67a6915add90..a34dd55032e65 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.getParcelable(EXTRA_STATE); + State state = icicle.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 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 b0542b9eb5be5..13b7b14600eb8 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 { 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 { 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 { } // 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 { 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 ec7dde9230c45..9812495da0fd9 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 f7a45e218a82f..9609dee16489a 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 c0faba306f6ed..b8b50e47483f9 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 similarity index 78% rename from packages/DocumentsUI/src/com/android/documentsui/SearchManager.java rename to packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java index 69f54c77fe8b1..049686289eee9 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,14 +134,37 @@ 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() { return mSearchExpanded; } + /** + * 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. @@ -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 b90a1194596a6..a288fe8f30ab6 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 0948ab1e75718..2ecbdf615b0ea 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> 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 4583decc14a80..669eb71e4d022 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 { @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 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() { - @Override - public Loader 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 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 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 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); - } - - public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) { - show(fm, TYPE_SEARCH, root, null, query, anim); + create(fm, TYPE_NORMAL, root, doc, null, anim); } public static void showRecentsOpen(FragmentManager fm, int anim) { - show(fm, TYPE_RECENT_OPEN, null, null, null, anim); + create(fm, TYPE_RECENT_OPEN, null, null, null, anim); } - private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc, + 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); + } + + 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 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 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 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 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 a9b0fd181d8bc..914f71e157093 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 c8b6f85282720..19268d759a47b 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 toList() { + public List toList() { ArrayList selection = new ArrayList(mSelection); selection.addAll(mProvisionalSelection); return selection;