Move full delete logic into ActionHandler.
Was spread across DirectoryFragment and UserInputHandlers.
Drop "android" test package in favor of the generic "testing".
Add an interface for ActionModeController (ActionModeAddons), so can be tested.
Moved ActionModeHandler out of dirlist ../up d dir.
Change-Id: Id915519656f163d3c413fe5d440d866c3ce4e2ca
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index d626b70..5088149 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -25,7 +25,6 @@
import com.android.documentsui.AbstractActionHandler.CommonAddons;
import com.android.documentsui.base.BooleanConsumer;
-import com.android.documentsui.base.ConfirmationCallback;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.Lookup;
@@ -34,7 +33,6 @@
import com.android.documentsui.base.State;
import com.android.documentsui.dirlist.AnimationView.AnimationType;
import com.android.documentsui.dirlist.DocumentDetails;
-import com.android.documentsui.dirlist.Model;
import com.android.documentsui.files.LauncherActivity;
import com.android.documentsui.files.OpenUriForViewTask;
import com.android.documentsui.roots.LoadRootTask;
@@ -153,7 +151,7 @@
}
@Override
- public void deleteSelectedDocuments(Model model, ConfirmationCallback callback) {
+ public void deleteSelectedDocuments() {
throw new UnsupportedOperationException("Delete not supported!");
}
diff --git a/src/com/android/documentsui/ActionHandler.java b/src/com/android/documentsui/ActionHandler.java
index 15ee5bc..fe6be0e 100644
--- a/src/com/android/documentsui/ActionHandler.java
+++ b/src/com/android/documentsui/ActionHandler.java
@@ -22,12 +22,10 @@
import android.net.Uri;
import com.android.documentsui.base.BooleanConsumer;
-import com.android.documentsui.base.ConfirmationCallback;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.dirlist.DocumentDetails;
-import com.android.documentsui.dirlist.Model;
public interface ActionHandler {
@@ -67,7 +65,7 @@
void showChooserForDoc(DocumentInfo doc);
- void deleteSelectedDocuments(Model model, ConfirmationCallback callback);
+ void deleteSelectedDocuments();
/**
* Called when initial activity setup is complete. Implementations
diff --git a/src/com/android/documentsui/ActionModeAddons.java b/src/com/android/documentsui/ActionModeAddons.java
new file mode 100644
index 0000000..83eba78
--- /dev/null
+++ b/src/com/android/documentsui/ActionModeAddons.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.documentsui;
+
+/**
+ * Extra ActionMode functionality required by ActionHandlers.
+ */
+public interface ActionModeAddons {
+
+ void finishActionMode();
+
+ void finishOnConfirmed(int code);
+}
diff --git a/src/com/android/documentsui/dirlist/ActionModeController.java b/src/com/android/documentsui/ActionModeController.java
similarity index 86%
rename from src/com/android/documentsui/dirlist/ActionModeController.java
rename to src/com/android/documentsui/ActionModeController.java
index 03582f4..a6fe7f1 100644
--- a/src/com/android/documentsui/dirlist/ActionModeController.java
+++ b/src/com/android/documentsui/ActionModeController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.documentsui.dirlist;
+package com.android.documentsui;
import static com.android.documentsui.base.Shared.DEBUG;
@@ -29,9 +29,7 @@
import android.view.MenuItem;
import android.view.View;
-import com.android.documentsui.MenuManager;
import com.android.documentsui.MenuManager.SelectionDetails;
-import com.android.documentsui.R;
import com.android.documentsui.base.ConfirmationCallback;
import com.android.documentsui.base.ConfirmationCallback.Result;
import com.android.documentsui.base.EventHandler;
@@ -46,7 +44,8 @@
/**
* A controller that listens to selection changes and manages life cycles of action modes.
*/
-public class ActionModeController implements SelectionManager.Callback, ActionMode.Callback {
+public class ActionModeController
+ implements SelectionManager.Callback, ActionMode.Callback, ActionModeAddons {
private static final String TAG = "ActionModeController";
@@ -55,7 +54,7 @@
private final MenuManager mMenuManager;
private final MessageBuilder mMessages;
- private final Config mConfig = new Config();
+ private final ContentScope mScope = new ContentScope();
private final Selection mSelected = new Selection();
private @Nullable ActionMode mActionMode;
@@ -80,7 +79,7 @@
if (mActionMode == null) {
if (DEBUG) Log.d(TAG, "Starting action mode.");
mActionMode = mActivity.startActionMode(this);
- mConfig.hapticPerformer.accept(HapticFeedbackConstants.LONG_PRESS);
+ mScope.hapticPerformer.accept(HapticFeedbackConstants.LONG_PRESS);
}
updateActionMenu();
} else {
@@ -95,7 +94,7 @@
final String title = mMessages.getQuantityString(
R.plurals.elements_selected, mSelected.size());
mActionMode.setTitle(title);
- mConfig.accessibilityAnnouncer.accept(title);
+ mScope.accessibilityAnnouncer.accept(title);
}
}
@@ -120,16 +119,7 @@
final String title = mMessages.getQuantityString(
R.plurals.elements_selected, mSelected.size());
mActionMode.setTitle(title);
- mConfig.accessibilityAnnouncer.accept(title);
- }
- }
-
- void finishActionMode() {
- if (mActionMode != null) {
- mActionMode.finish();
- mActionMode = null;
- } else {
- Log.w(TAG, "Tried to finish a null action mode.");
+ mScope.accessibilityAnnouncer.accept(title);
}
}
@@ -151,7 +141,7 @@
mSelected.clear();
// Re-enable TalkBack for the toolbars, as they are no longer covered by action mode.
- mConfig.accessibilityImportanceSetter.setAccessibilityImportance(
+ mScope.accessibilityImportanceSetter.setAccessibilityImportance(
View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, R.id.toolbar, R.id.roots_toolbar);
}
@@ -165,7 +155,7 @@
// Hide the toolbars if action mode is enabled, so TalkBack doesn't navigate to
// these controls when using linear navigation.
- mConfig.accessibilityImportanceSetter.setAccessibilityImportance(
+ mScope.accessibilityImportanceSetter.setAccessibilityImportance(
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
R.id.toolbar,
R.id.roots_toolbar);
@@ -184,13 +174,13 @@
private void updateActionMenu() {
assert(mMenu != null);
- mMenuManager.updateActionMenu(mMenu, mConfig.selectionDetails);
+ mMenuManager.updateActionMenu(mMenu, mScope.selectionDetails);
Menus.disableHiddenItems(mMenu);
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return mConfig.menuItemClicker.accept(item);
+ return mScope.menuItemClicker.accept(item);
}
private static void setImportantForAccessibility(
@@ -208,6 +198,17 @@
void setAccessibilityImportance(int accessibilityImportance, @IdRes int... viewIds);
}
+ @Override
+ public void finishActionMode() {
+ if (mActionMode != null) {
+ mActionMode.finish();
+ mActionMode = null;
+ } else {
+ Log.w(TAG, "Tried to finish a null action mode.");
+ }
+ }
+
+ @Override
public void finishOnConfirmed(@Result int code) {
if (code == ConfirmationCallback.CONFIRM) {
finishActionMode();
@@ -219,11 +220,11 @@
assert(mActionMode == null);
assert(mMenu == null);
- mConfig.menuItemClicker = menuItemClicker;
- mConfig.selectionDetails = selectionDetails;
- mConfig.hapticPerformer = view::performHapticFeedback;
- mConfig.accessibilityAnnouncer = view::announceForAccessibility;
- mConfig.accessibilityImportanceSetter =
+ mScope.menuItemClicker = menuItemClicker;
+ mScope.selectionDetails = selectionDetails;
+ mScope.hapticPerformer = view::performHapticFeedback;
+ mScope.accessibilityAnnouncer = view::announceForAccessibility;
+ mScope.accessibilityImportanceSetter =
(int accessibilityImportance, @IdRes int[] viewIds) -> {
setImportantForAccessibility(
mActivity, accessibilityImportance, viewIds);
@@ -232,7 +233,7 @@
return this;
}
- private static final class Config {
+ private static final class ContentScope {
private EventHandler<MenuItem> menuItemClicker;
private SelectionDetails selectionDetails;
private IntConsumer hapticPerformer;
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index 0f285f5..005f887 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -64,7 +64,6 @@
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
import com.android.documentsui.base.State.ViewMode;
-import com.android.documentsui.dirlist.ActionModeController;
import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.DocumentsAdapter;
diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java
index 5bf360f..c6e9514 100644
--- a/src/com/android/documentsui/MenuManager.java
+++ b/src/com/android/documentsui/MenuManager.java
@@ -27,7 +27,6 @@
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
-import com.android.documentsui.dirlist.ActionModeController;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.sidebar.RootsFragment;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index cb6ffbc..306644d 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -61,6 +61,7 @@
import android.widget.TextView;
import com.android.documentsui.ActionHandler;
+import com.android.documentsui.ActionModeController;
import com.android.documentsui.ActivityConfig;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.BaseActivity.RetainedState;
@@ -100,7 +101,6 @@
import com.android.documentsui.services.FileOperationService.OpType;
import com.android.documentsui.services.FileOperations;
import com.android.documentsui.sorting.SortDimension;
-import com.android.documentsui.sorting.SortDimension.SortDirection;
import com.android.documentsui.sorting.SortModel;
import com.android.documentsui.ui.DialogController;
import com.android.documentsui.ui.Snackbars;
@@ -182,7 +182,7 @@
private MessageBar mMessageBar;
private View mProgressBar;
- private Config mConfig;
+ private DirectoryState mLocalState;
// 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.
@@ -257,8 +257,8 @@
// Restore state if fragment recreated.
Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState;
- mConfig = new Config();
- mConfig.restore(args);
+ mLocalState = new DirectoryState();
+ mLocalState.restore(args);
// Restore any selection we may have squirreled away in retained state.
@Nullable RetainedState retained = getBaseActivity().getRetainedState();
@@ -298,7 +298,7 @@
final BaseActivity activity = getBaseActivity();
mSelectionMgr = activity.getSelectionManager(mAdapter, this::canSetSelectionState);
mFocusManager = activity.getFocusManager(mRecView, mModel);
- mActions = activity.getActionHandler(mModel, mConfig.mSearchMode);
+ mActions = activity.getActionHandler(mModel, mLocalState.mSearchMode);
mMenuManager = activity.getMenuManager();
mDialogs = activity.getDialogController();
@@ -343,7 +343,6 @@
(MotionEvent t) -> MotionInputEvent.obtain(t, mRecView),
this::canSelect,
this::onRightClick,
- (DocumentDetails ignored) -> onDeleteSelectedDocuments(), // delete handler
mDragStartListener::onTouchDragEvent,
gestureHandler);
@@ -367,13 +366,13 @@
final ActivityManager am = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
- boolean svelte = am.isLowRamDevice() && (mConfig.mType == TYPE_RECENT_OPEN);
+ boolean svelte = am.isLowRamDevice() && (mLocalState.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 =
- (mConfig.mDocument != null)
- ? (mConfig.mDocument.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0
+ (mLocalState.mDocument != null)
+ ? (mLocalState.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(
@@ -403,7 +402,7 @@
final SparseArray<Parcelable> container = new SparseArray<>();
getView().saveHierarchyState(container);
final State state = getDisplayState();
- state.dirConfigs.put(mConfig.getConfigKey(), container);
+ state.dirConfigs.put(mLocalState.getConfigKey(), container);
}
public void retainState(RetainedState state) {
@@ -414,7 +413,7 @@
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- mConfig.save(outState);
+ mLocalState.save(outState);
}
@Override
@@ -453,7 +452,7 @@
private void handleCopyResult(int resultCode, Intent data) {
- FileOperation operation = mConfig.claimPendingOperation();
+ FileOperation operation = mLocalState.claimPendingOperation();
if (resultCode == Activity.RESULT_CANCELED || data == null) {
// User pressed the back button or otherwise cancelled the destination pick. Don't
@@ -585,8 +584,7 @@
case R.id.menu_delete:
// deleteDocuments will end action mode if the documents are deleted.
// It won't end action mode if user cancels the delete.
- mActions.deleteSelectedDocuments(
- mModel, mActionModeController::finishOnConfirmed);
+ mActions.deleteSelectedDocuments();
return true;
case R.id.menu_copy_to:
@@ -726,15 +724,8 @@
startActivity(intent);
}
- private boolean onDeleteSelectedDocuments() {
- if (mSelectionMgr.hasSelection()) {
- mActions.deleteSelectedDocuments(mModel, mActionModeController::finishOnConfirmed);
- }
- return false;
- }
-
private void transferDocuments(final Selection selected, final @OpType int mode) {
- if(mode == FileOperationService.OPERATION_COPY) {
+ if (mode == FileOperationService.OPERATION_COPY) {
Metrics.logUserAction(getContext(), Metrics.USER_ACTION_COPY_TO);
} else if (mode == FileOperationService.OPERATION_MOVE) {
Metrics.logUserAction(getContext(), Metrics.USER_ACTION_MOVE_TO);
@@ -757,7 +748,7 @@
}
Uri srcParent = getDisplayState().stack.peek().derivedUri;
- mConfig.mPendingOperation = new FileOperation.Builder()
+ mLocalState.mPendingOperation = new FileOperation.Builder()
.withOpType(mode)
.withSrcParent(srcParent)
.withSrcs(srcs)
@@ -1146,7 +1137,7 @@
mProgressBar.setVisibility(mModel.isLoading() ? View.VISIBLE : View.GONE);
if (mModel.isEmpty()) {
- if (mConfig.mSearchMode) {
+ if (mLocalState.mSearchMode) {
showNoResults(getDisplayState().stack.root);
} else {
showEmptyDirectory();
@@ -1201,7 +1192,7 @@
String query) {
DirectoryFragment df = get(fm);
- df.mConfig.update(root, doc, query);
+ df.mLocalState.update(root, doc, query);
df.getLoaderManager().restartLoader(LOADER_ID, null, df);
}
@@ -1210,7 +1201,7 @@
if (DEBUG) Log.d(TAG, "Reloading directory: " + DocumentInfo.debugString(doc));
DirectoryFragment df = get(fm);
- df.mConfig.update(type, root, doc, query);
+ df.mLocalState.update(type, root, doc, query);
df.getLoaderManager().restartLoader(LOADER_ID, null, df);
}
@@ -1281,39 +1272,39 @@
State state = getDisplayState();
Uri contentsUri;
- switch (mConfig.mType) {
+ switch (mLocalState.mType) {
case TYPE_NORMAL:
- contentsUri = mConfig.mSearchMode ? DocumentsContract.buildSearchDocumentsUri(
- mConfig.mRoot.authority, mConfig.mRoot.rootId, mConfig.mQuery)
+ contentsUri = mLocalState.mSearchMode ? DocumentsContract.buildSearchDocumentsUri(
+ mLocalState.mRoot.authority, mLocalState.mRoot.rootId, mLocalState.mQuery)
: DocumentsContract.buildChildDocumentsUri(
- mConfig.mDocument.authority, mConfig.mDocument.documentId);
+ mLocalState.mDocument.authority, mLocalState.mDocument.documentId);
if (mActivityConfig.managedModeEnabled(state.stack)) {
contentsUri = DocumentsContract.setManageMode(contentsUri);
}
if (DEBUG) Log.d(TAG, "Creating new directory loader for: "
- + DocumentInfo.debugString(mConfig.mDocument));
+ + DocumentInfo.debugString(mLocalState.mDocument));
return new DirectoryLoader(
- context, mConfig.mRoot, mConfig.mDocument, contentsUri, state.sortModel,
- mConfig.mSearchMode);
+ context, mLocalState.mRoot, mLocalState.mDocument, contentsUri, state.sortModel,
+ mLocalState.mSearchMode);
case TYPE_RECENT_OPEN:
if (DEBUG) Log.d(TAG, "Creating new loader recents.");
final RootsAccess roots = DocumentsApplication.getRootsCache(context);
return new RecentsLoader(context, roots, state);
default:
- throw new IllegalStateException("Unknown type " + mConfig.mType);
+ throw new IllegalStateException("Unknown type " + mLocalState.mType);
}
}
@Override
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
if (DEBUG) Log.d(TAG, "Loader has finished for: "
- + DocumentInfo.debugString(mConfig.mDocument));
+ + DocumentInfo.debugString(mLocalState.mDocument));
assert(result != null);
if (!isAdded()) return;
- if (mConfig.mSearchMode) {
+ if (mLocalState.mSearchMode) {
Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SEARCH);
}
@@ -1332,22 +1323,22 @@
}
// Restore any previous instance state
- final SparseArray<Parcelable> container = state.dirConfigs.remove(mConfig.getConfigKey());
+ final SparseArray<Parcelable> container = state.dirConfigs.remove(mLocalState.getConfigKey());
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 (mConfig.mLastSortDimensionId != curSortedDimension.getId()
- || mConfig.mLastSortDimensionId == SortModel.SORT_DIMENSION_ID_UNKNOWN
- || mConfig.mLastSortDirection != curSortedDimension.getSortDirection()) {
+ } else if (mLocalState.mLastSortDimensionId != curSortedDimension.getId()
+ || mLocalState.mLastSortDimensionId == SortModel.SORT_DIMENSION_ID_UNKNOWN
+ || mLocalState.mLastSortDirection != curSortedDimension.getSortDirection()) {
// Scroll to the top if the sort order actually changed.
mRecView.smoothScrollToPosition(0);
}
- mConfig.mLastSortDimensionId = curSortedDimension.getId();
- mConfig.mLastSortDirection = curSortedDimension.getSortDirection();
+ mLocalState.mLastSortDimensionId = curSortedDimension.getId();
+ mLocalState.mLastSortDirection = curSortedDimension.getSortDirection();
if (mRefreshLayout.isRefreshing()) {
new Handler().postDelayed(
@@ -1359,81 +1350,9 @@
@Override
public void onLoaderReset(Loader<DirectoryResult> loader) {
if (DEBUG) Log.d(TAG, "Resetting loader for: "
- + DocumentInfo.debugString(mConfig.mDocument));
+ + DocumentInfo.debugString(mLocalState.mDocument));
mModel.onLoaderReset();
mRefreshLayout.setRefreshing(false);
}
-
- private static final class Config {
-
- 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 mConfigKey;
-
- 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 getConfigKey() {
- if (mConfigKey == 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");
- mConfigKey = builder.toString();
- }
- return mConfigKey;
- }
- }
}
diff --git a/src/com/android/documentsui/dirlist/DirectoryState.java b/src/com/android/documentsui/dirlist/DirectoryState.java
new file mode 100644
index 0000000..6b849a2
--- /dev/null
+++ b/src/com/android/documentsui/dirlist/DirectoryState.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.documentsui.dirlist;
+
+import android.os.Bundle;
+
+import com.android.documentsui.base.DocumentInfo;
+import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.Shared;
+import com.android.documentsui.dirlist.DirectoryFragment.ResultType;
+import com.android.documentsui.services.FileOperation;
+import com.android.documentsui.services.FileOperationService;
+import com.android.documentsui.sorting.SortModel;
+import com.android.documentsui.sorting.SortDimension.SortDirection;
+
+import javax.annotation.Nullable;
+
+final class DirectoryState {
+
+ 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
+ @ResultType int mType = DirectoryFragment.TYPE_NORMAL;
+ RootInfo mRoot;
+ // Null when viewing Recents directory.
+ @Nullable DocumentInfo mDocument;
+ String mQuery = null;
+ // Here we save the clip details of moveTo/copyTo actions when picker shows up.
+ // This will be written to saved instance.
+ @Nullable FileOperation mPendingOperation;
+ boolean mSearchMode;
+ int mLastSortDimensionId = SortModel.SORT_DIMENSION_ID_UNKNOWN;
+ @SortDirection int mLastSortDirection;
+
+ private String mConfigKey;
+
+ 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;
+ }
+
+ String getConfigKey() {
+ if (mConfigKey == 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");
+ mConfigKey = builder.toString();
+ }
+ return mConfigKey;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/documentsui/dirlist/UserInputHandler.java b/src/com/android/documentsui/dirlist/UserInputHandler.java
index 4af1fcd..03d40fa 100644
--- a/src/com/android/documentsui/dirlist/UserInputHandler.java
+++ b/src/com/android/documentsui/dirlist/UserInputHandler.java
@@ -45,14 +45,13 @@
private static final String TAG = "UserInputHandler";
- private ActionHandler mActionHandler;
+ private ActionHandler mActions;
private final FocusHandler mFocusHandler;
private final SelectionManager mSelectionMgr;
private final Function<MotionEvent, T> mEventConverter;
private final Predicate<DocumentDetails> mSelectable;
private final EventHandler<InputEvent> mRightClickHandler;
- private final EventHandler<DocumentDetails> mDeleteHandler;
private final EventHandler<InputEvent> mTouchDragListener;
private final EventHandler<InputEvent> mGestureSelectHandler;
@@ -62,23 +61,21 @@
private final KeyInputHandler mKeyListener;
public UserInputHandler(
- ActionHandler actionHandler,
+ ActionHandler actions,
FocusHandler focusHandler,
SelectionManager selectionMgr,
Function<MotionEvent, T> eventConverter,
Predicate<DocumentDetails> selectable,
EventHandler<InputEvent> rightClickHandler,
- EventHandler<DocumentDetails> deleteHandler,
EventHandler<InputEvent> touchDragListener,
EventHandler<InputEvent> gestureSelectHandler) {
- mActionHandler = actionHandler;
+ mActions = actions;
mFocusHandler = focusHandler;
mSelectionMgr = selectionMgr;
mEventConverter = eventConverter;
mSelectable = selectable;
mRightClickHandler = rightClickHandler;
- mDeleteHandler = deleteHandler;
mTouchDragListener = touchDragListener;
mGestureSelectHandler = gestureSelectHandler;
@@ -246,7 +243,7 @@
// otherwise they activate.
return doc.isInSelectionHotspot(event)
? selectDocument(doc)
- : mActionHandler.openDocument(doc);
+ : mActions.openDocument(doc);
}
boolean onSingleTapConfirmed(T event) {
@@ -380,7 +377,7 @@
}
DocumentDetails doc = event.getDocumentDetails();
- return mActionHandler.viewDocument(doc);
+ return mActions.viewDocument(doc);
}
final void onLongPress(T event) {
@@ -447,14 +444,14 @@
// For non-shifted enter keypresses, fall through.
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_BUTTON_A:
- return mActionHandler.viewDocument(doc);
+ return mActions.viewDocument(doc);
case KeyEvent.KEYCODE_SPACE:
- return mActionHandler.previewDocument(doc);
+ return mActions.previewDocument(doc);
case KeyEvent.KEYCODE_FORWARD_DEL:
// This has to be handled here instead of in a keyboard shortcut, because
// keyboard shortcuts all have to be modified with the 'Ctrl' key.
if (mSelectionMgr.hasSelection()) {
- mDeleteHandler.accept(doc);
+ mActions.deleteSelectedDocuments();
}
// Always handle the key, even if there was nothing to delete. This is a
// precaution to prevent other handlers from potentially picking up the event
diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java
index e31d587..993af36 100644
--- a/src/com/android/documentsui/files/ActionHandler.java
+++ b/src/com/android/documentsui/files/ActionHandler.java
@@ -27,6 +27,7 @@
import android.util.Log;
import com.android.documentsui.AbstractActionHandler;
+import com.android.documentsui.ActionModeAddons;
import com.android.documentsui.ActivityConfig;
import com.android.documentsui.DocumentsAccess;
import com.android.documentsui.DocumentsApplication;
@@ -69,6 +70,7 @@
private static final String TAG = "ManagerActionHandler";
+ private final ActionModeAddons mActionModeAddons;
private final DialogController mDialogs;
private final ActivityConfig mActConfig;
private final DocumentClipper mClipper;
@@ -82,6 +84,7 @@
DocumentsAccess docs,
SelectionManager selectionMgr,
Lookup<String, Executor> executors,
+ ActionModeAddons actionModeAddons,
DialogController dialogs,
ActivityConfig tuner,
DocumentClipper clipper,
@@ -89,6 +92,7 @@
super(activity, state, roots, docs, selectionMgr, executors);
+ mActionModeAddons = actionModeAddons;
mDialogs = dialogs;
mActConfig = tuner;
mClipper = clipper;
@@ -181,7 +185,7 @@
}
@Override
- public void deleteSelectedDocuments(Model model, ConfirmationCallback callback) {
+ public void deleteSelectedDocuments() {
assert(mSelectionMgr.hasSelection());
Metrics.logUserAction(mActivity, Metrics.USER_ACTION_DELETE);
@@ -193,11 +197,11 @@
assert(srcParent != null);
// Model must be accessed in UI thread, since underlying cursor is not threadsafe.
- List<DocumentInfo> docs = model.getDocuments(selection);
+ List<DocumentInfo> docs = mScope.model.getDocuments(selection);
ConfirmationCallback result = (@Result int code) -> {
// share the news with our caller, be it good or bad.
- callback.accept(code);
+ mActionModeAddons.finishOnConfirmed(code);
if (code != ConfirmationCallback.CONFIRM) {
return;
@@ -207,7 +211,7 @@
try {
srcs = UrisSupplier.create(
selection,
- model::getItemUri,
+ mScope.model::getItemUri,
mClipStore);
} catch (IOException e) {
throw new RuntimeException("Failed to create uri supplier.", e);
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 448b7e9..500bcf7 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -32,6 +32,7 @@
import android.view.MenuItem;
import android.view.View;
+import com.android.documentsui.ActionModeController;
import com.android.documentsui.ActivityConfig;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.DocumentsAccess;
@@ -49,7 +50,6 @@
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
import com.android.documentsui.clipping.DocumentClipper;
-import com.android.documentsui.dirlist.ActionModeController;
import com.android.documentsui.dirlist.AnimationView.AnimationType;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.DocumentsAdapter;
@@ -100,6 +100,13 @@
});
mDialogs = DialogController.create(this, getMessages());
+
+ mActionModeController = new ActionModeController(
+ this,
+ mSelectionMgr,
+ mMenuManager,
+ getMessages());
+
mActions = new ActionHandler<>(
this,
mState,
@@ -107,17 +114,12 @@
DocumentsAccess.create(this),
mSelectionMgr,
ProviderExecutor::forAuthority,
+ mActionModeController,
mDialogs,
mConfig,
mClipper,
DocumentsApplication.getClipStore(this));
- mActionModeController = new ActionModeController(
- this,
- mSelectionMgr,
- mMenuManager,
- getMessages());
-
RootsFragment.show(getFragmentManager(), null);
final Intent intent = getIntent();
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 12dd19b..42d32ae 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -43,6 +43,7 @@
import android.view.MenuItem;
import android.view.View;
+import com.android.documentsui.ActionModeController;
import com.android.documentsui.ActivityConfig;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.DocumentsAccess;
@@ -58,7 +59,6 @@
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
-import com.android.documentsui.dirlist.ActionModeController;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.DocumentsAdapter;
import com.android.documentsui.dirlist.Model;