// into the selection manager.
mSelectionManager = new MultiSelectManager(
mRecView,
+ mAdapter,
state.allowMultiple
? MultiSelectManager.MODE_MULTIPLE
: MultiSelectManager.MODE_SINGLE);
@Override
protected Void doInBackground(Selection... selected) {
- List<DocumentInfo> toDelete = getDocuments(selected[0]);
- mHadTrouble = false;
+ List<DocumentInfo> toDelete = null;
+ try {
+ toDelete = getDocuments(selected[0]);
+ } catch (NullPointerException e) {
+ Log.w(TAG, "Failed to retrieve documents for delete.");
+ mHadTrouble = true;
+ return null;
+ }
for (DocumentInfo doc : toDelete) {
if (!doc.isDeleteSupported()) {
private final Selection mSelection = new Selection();
- private Range mRanger;
- private SelectionEnvironment mEnvironment;
-
+ private final SelectionEnvironment mEnvironment;
+ private final DocumentsAdapter mAdapter;
private final List<MultiSelectManager.Callback> mCallbacks = new ArrayList<>(1);
+ private Range mRanger;
private boolean mSingleSelect;
@Nullable private BandController mBandManager;
+
/**
* @param recyclerView
* @param mode Selection mode
*/
- public MultiSelectManager(final RecyclerView recyclerView, int mode) {
- this(new RuntimeSelectionEnvironment(recyclerView), mode);
+ public MultiSelectManager(
+ final RecyclerView recyclerView, DocumentsAdapter adapter, int mode) {
+ this(new RuntimeSelectionEnvironment(recyclerView), adapter, mode);
if (mode == MODE_MULTIPLE) {
mBandManager = new BandController();
* @hide
*/
@VisibleForTesting
- MultiSelectManager(SelectionEnvironment environment, int mode) {
+ MultiSelectManager(SelectionEnvironment environment, DocumentsAdapter adapter, int mode) {
mEnvironment = checkNotNull(environment, "'environment' cannot be null.");
+ mAdapter = checkNotNull(adapter, "'adapter' cannot be null.");
mSingleSelect = mode == MODE_SINGLE;
- mEnvironment.registerDataObserver(
+ mAdapter.registerAdapterDataObserver(
new RecyclerView.AdapterDataObserver() {
private List<String> mModelIds;
public void onChanged() {
// TODO: This is causing b/22765812
mSelection.clear();
- mModelIds = mEnvironment.getModelIds();
+ mModelIds = mAdapter.getModelIds();
}
@Override
if (DEBUG) Log.d(TAG, "Ignoring toggle for element with no position.");
return;
}
- String id = mEnvironment.getModelIdFromAdapterPosition(position);
+ String id = mAdapter.getModelId(position);
if (id != null) {
toggleSelection(id);
}
return;
}
- if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(position))) {
+ if (mSelection.contains(mAdapter.getModelId(position))) {
mRanger = new Range(position);
}
}
private void updateRange(int begin, int end, boolean selected) {
checkState(end >= begin);
for (int i = begin; i <= end; i++) {
- String id = mEnvironment.getModelIdFromAdapterPosition(i);
+ String id = mAdapter.getModelId(i);
if (id == null) {
continue;
}
for (int i = lastListener; i > -1; i--) {
mCallbacks.get(i).onItemStateChanged(id, selected);
}
- mEnvironment.notifyItemChanged(id);
+ mAdapter.onItemSelectionChanged(id);
}
/**
int getChildCount();
int getVisibleChildCount();
void focusItem(int position);
- /**
- * Returns null if non-useful item.
- * @param position
- * @return
- */
- @Nullable String getModelIdFromAdapterPosition(int position);
- int getItemCount();
- List<String> getModelIds();
- void notifyItemChanged(String id);
- void registerDataObserver(RecyclerView.AdapterDataObserver observer);
}
/** Recycler view facade implementation backed by good ol' RecyclerView. */
private final Drawable mBand;
private boolean mIsOverlayShown = false;
- private DocumentsAdapter mAdapter;
- RuntimeSelectionEnvironment(RecyclerView rv) {
- mView = rv;
- mAdapter = (DocumentsAdapter) rv.getAdapter();
+ RuntimeSelectionEnvironment(RecyclerView view) {
+ mView = view;
mBand = mView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay);
}
}
@Override
- public @Nullable String getModelIdFromAdapterPosition(int position) {
- return mAdapter.getModelId(position);
- }
-
- @Override
public void addOnScrollListener(RecyclerView.OnScrollListener listener) {
mView.addOnScrollListener(listener);
}
});
}
}
-
- @Override
- public void notifyItemChanged(String id) {
- mAdapter.onItemSelectionChanged(id);
- }
-
- @Override
- public int getItemCount() {
- return mAdapter.getItemCount();
- }
-
- @Override
- public void registerDataObserver(RecyclerView.AdapterDataObserver observer) {
- mAdapter.registerAdapterDataObserver(observer);
- }
-
- @Override
- public List<String> getModelIds() {
- return mAdapter.getModelIds();
- }
}
public interface Callback {
mModelBuilder = new Runnable() {
@Override
public void run() {
- mModel = new GridModel(mEnvironment);
+ mModel = new GridModel(mEnvironment, mAdapter);
mModel.addOnSelectionChangedListener(BandController.this);
}
};
return !isActive()
&& e.isMouseEvent() // a mouse
&& e.isActionDown() // the initial button press
- && mEnvironment.getItemCount() > 0
+ && mAdapter.getItemCount() > 0
&& e.getItemPosition() == RecyclerView.NO_ID; // in empty space
}
mModel.endSelection();
int firstSelected = mModel.getPositionNearestOrigin();
if (firstSelected != NOT_SET) {
- if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(firstSelected))) {
+ if (mSelection.contains(mAdapter.getModelId(firstSelected))) {
// TODO: firstSelected should really be lastSelected, we want to anchor the item
// where the mouse-up occurred.
setSelectionRangeBegin(firstSelected);
private static final int LOWER_RIGHT = LOWER | RIGHT;
private final SelectionEnvironment mHelper;
+ private final DocumentsAdapter mAdapter;
+
private final List<OnSelectionChangedListener> mOnSelectionChangedListeners =
new ArrayList<>();
// should expand from when Shift+click is used.
private int mPositionNearestOrigin = NOT_SET;
- GridModel(SelectionEnvironment helper) {
+ GridModel(SelectionEnvironment helper, DocumentsAdapter adapter) {
mHelper = helper;
+ mAdapter = adapter;
mHelper.addOnScrollListener(this);
}
// position. Use a sentry value to prevent erroneously selecting item 0.
int position = items.get(items.keyAt(row), NOT_SET);
if (position != NOT_SET) {
- String id = mHelper.getModelIdFromAdapterPosition(position);
+ String id = mAdapter.getModelId(position);
if (id != null) {
// The adapter inserts items for UI layout purposes that aren't associated
// with files. Those will have a null model ID. Don't select them.
if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
position = 0;
} else if (keyCode == KeyEvent.KEYCODE_MOVE_END) {
- position = mEnvironment.getItemCount() - 1;
+ position = mAdapter.getItemCount() - 1;
} else {
// Find a navigation target based on the arrow key that the user pressed. Ignore
// navigation targets that aren't items in the recycler view.
// Tests that the items can be hidden and unhidden.
public void testUnhide_PreservesOrder() {
List<String> ids = mModel.getModelIds();
- SparseArray<String> hidden = mAdapter.hide(ids.toArray(new String[ids.size()]));
+ SparseArray<String> hidden = mAdapter.hide(
+ ids.get(0), ids.get(1), ids.get(5), ids.get(9));
mAdapter.unhide(hidden);
// Finally ensure the restored items are in the original order
import com.android.documentsui.TestInputEvent;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+
import com.google.common.collect.Lists;
import java.util.ArrayList;
private MultiSelectManager mManager;
private TestCallback mCallback;
private TestSelectionEnvironment mEnv;
+ private TestDocumentsAdapter mAdapter;
public void setUp() throws Exception {
mCallback = new TestCallback();
mEnv = new TestSelectionEnvironment(items);
- mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_MULTIPLE);
+ mAdapter = new TestDocumentsAdapter(items);
+ mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_MULTIPLE);
mManager.addCallback(mCallback);
}
}
public void testSingleSelectMode() {
- mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE);
+ mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE);
mManager.addCallback(mCallback);
longPress(20);
tap(13);
}
public void testSingleSelectMode_ShiftTap() {
- mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE);
+ mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE);
mManager.addCallback(mCallback);
longPress(13);
shiftTap(20);
}
public void testSingleSelectMode_ShiftDoesNotExtendSelection() {
- mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE);
+ mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE);
mManager.addCallback(mCallback);
longPress(20);
keyToPosition(22, true);
import android.graphics.Point;
import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView.AdapterDataObserver;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.documentsui.dirlist.MultiSelectManager.GridModel;
-import java.util.List;
+import java.util.ArrayList;
import java.util.Set;
@SmallTest
private static final int CHILD_VIEW_EDGE_PX = 100;
private static final int VIEWPORT_HEIGHT = 500;
- private static GridModel model;
- private static TestEnvironment env;
- private static Set<String> lastSelection;
- private static int viewWidth;
+ private GridModel model;
+ private TestEnvironment env;
+ private TestDocumentsAdapter adapter;
+ private Set<String> lastSelection;
+ private int viewWidth;
- private static void setUp(int numChildren, int numColumns) {
+ private void initData(final int numChildren, int numColumns) {
env = new TestEnvironment(numChildren, numColumns);
+ adapter = new TestDocumentsAdapter(new ArrayList<String>()) {
+ @Override
+ public String getModelId(int position) {
+ return Integer.toString(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return numChildren;
+ }
+ };
+
viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
- model = new GridModel(env);
+ model = new GridModel(env, adapter);
model.addOnSelectionChangedListener(
new GridModel.OnSelectionChangedListener() {
@Override
}
public void testSelectionLeftOfItems() {
- setUp(20, 5);
+ initData(20, 5);
model.startSelection(new Point(0, 10));
model.resizeSelection(new Point(1, 11));
assertSelected();
}
public void testSelectionRightOfItems() {
- setUp(20, 4);
+ initData(20, 4);
model.startSelection(new Point(viewWidth - 1, 10));
model.resizeSelection(new Point(viewWidth - 2, 11));
assertSelected();
}
public void testSelectionAboveItems() {
- setUp(20, 4);
+ initData(20, 4);
model.startSelection(new Point(10, 0));
model.resizeSelection(new Point(11, 1));
assertSelected();
}
public void testSelectionBelowItems() {
- setUp(5, 4);
+ initData(5, 4);
model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
assertSelected();
}
public void testVerticalSelectionBetweenItems() {
- setUp(20, 4);
+ initData(20, 4);
model.startSelection(new Point(106, 0));
model.resizeSelection(new Point(107, 200));
assertSelected();
}
public void testHorizontalSelectionBetweenItems() {
- setUp(20, 4);
+ initData(20, 4);
model.startSelection(new Point(0, 105));
model.resizeSelection(new Point(200, 106));
assertSelected();
}
public void testGrowingAndShrinkingSelection() {
- setUp(20, 4);
+ initData(20, 4);
model.startSelection(new Point(0, 0));
model.resizeSelection(new Point(5, 5));
assertSelected(0);
}
public void testSelectionMovingAroundOrigin() {
- setUp(16, 4);
+ initData(16, 4);
model.startSelection(new Point(210, 210));
model.resizeSelection(new Point(viewWidth - 1, 0));
assertSelected(2, 3, 6, 7);
}
public void testScrollingBandSelect() {
- setUp(40, 4);
+ initData(40, 4);
model.startSelection(new Point(0, 0));
model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
assertSelected(0, 4, 8, 12, 16);
assertEquals(0, model.getPositionNearestOrigin());
}
- private static void assertSelected(int... selectedPositions) {
+ private void assertSelected(int... selectedPositions) {
assertEquals(selectedPositions.length, lastSelection.size());
for (int position : selectedPositions) {
assertTrue(lastSelection.contains(Integer.toString(position)));
}
}
- private static void scroll(int dy) {
+ private void scroll(int dy) {
assertTrue(env.verticalOffset + VIEWPORT_HEIGHT + dy <= env.getTotalHeight());
env.verticalOffset += dy;
model.onScrolled(null, 0, dy);
public void focusItem(int i) {
throw new UnsupportedOperationException();
}
-
- @Override
- public String getModelIdFromAdapterPosition(int position) {
- return Integer.toString(position);
- }
-
- @Override
- public int getItemCount() {
- return mNumChildren;
- }
-
- @Override
- public List<String> getModelIds() {
- return null;
- }
-
- @Override
- public void notifyItemChanged(String id) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void registerDataObserver(AdapterDataObserver observer) {
- throw new UnsupportedOperationException();
- }
}
}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.util.SparseArray;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A skeletal {@link DocumentsAdapter} test double.
+ */
+public class TestDocumentsAdapter extends DocumentsAdapter {
+
+ List<String> mModelIds = new ArrayList<>();
+
+ public TestDocumentsAdapter(List<String> modelIds) {
+ mModelIds = modelIds;
+ }
+
+ @Override
+ public void onModelUpdate(Model model) {
+ }
+
+ @Override
+ public void onModelUpdateFailed(Exception e) {
+ }
+
+ @Override
+ List<String> getModelIds() {
+ return mModelIds;
+ }
+
+ @Override
+ void onItemSelectionChanged(String id) {
+ }
+
+ @Override
+ String getModelId(int position) {
+ return mModelIds.get(position);
+ }
+
+ @Override
+ public SparseArray<String> hide(String... ids) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ void unhide(SparseArray<String> ids) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void onBindViewHolder(DocumentHolder holder, int position) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getItemCount() {
+ return mModelIds.size();
+ }
+}
import android.graphics.Point;
import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView.AdapterDataObserver;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.view.View;
public class TestSelectionEnvironment implements SelectionEnvironment {
- private List<String> mItems;
-
public TestSelectionEnvironment(List<String> items) {
- mItems = items;
}
@Override
@Override
public void focusItem(int position) {
}
-
- @Override
- public String getModelIdFromAdapterPosition(int position) {
- return mItems.get(position);
- }
-
- @Override
- public int getItemCount() {
- return mItems.size();
- }
-
- @Override
- public List<String> getModelIds() {
- return null;
- }
-
- @Override
- public void notifyItemChanged(String id) {
- }
-
- @Override
- public void registerDataObserver(AdapterDataObserver observer) {
- }
}