diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 13cfbe4a16e57..9617582a1f3bc 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -332,6 +332,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi // into the selection manager. mSelectionManager = new MultiSelectManager( mRecView, + mAdapter, state.allowMultiple ? MultiSelectManager.MODE_MULTIPLE : MultiSelectManager.MODE_SINGLE); diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java index 4bef51bf50cd4..f2bade588f232 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java @@ -414,8 +414,14 @@ public class Model implements SiblingProvider { @Override protected Void doInBackground(Selection... selected) { - List toDelete = getDocuments(selected[0]); - mHadTrouble = false; + List 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()) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java index 125352f4bd977..5994df952de98 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java @@ -68,21 +68,23 @@ public final class MultiSelectManager implements View.OnKeyListener { private final Selection mSelection = new Selection(); - private Range mRanger; - private SelectionEnvironment mEnvironment; - + private final SelectionEnvironment mEnvironment; + private final DocumentsAdapter mAdapter; private final List 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(); @@ -133,11 +135,12 @@ public final class MultiSelectManager implements View.OnKeyListener { * @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 mModelIds; @@ -146,7 +149,7 @@ public final class MultiSelectManager implements View.OnKeyListener { public void onChanged() { // TODO: This is causing b/22765812 mSelection.clear(); - mModelIds = mEnvironment.getModelIds(); + mModelIds = mAdapter.getModelIds(); } @Override @@ -336,7 +339,7 @@ public final class MultiSelectManager implements View.OnKeyListener { 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); } @@ -388,7 +391,7 @@ public final class MultiSelectManager implements View.OnKeyListener { return; } - if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(position))) { + if (mSelection.contains(mAdapter.getModelId(position))) { mRanger = new Range(position); } } @@ -405,7 +408,7 @@ public final class MultiSelectManager implements View.OnKeyListener { 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; } @@ -473,7 +476,7 @@ public final class MultiSelectManager implements View.OnKeyListener { for (int i = lastListener; i > -1; i--) { mCallbacks.get(i).onItemStateChanged(id, selected); } - mEnvironment.notifyItemChanged(id); + mAdapter.onItemSelectionChanged(id); } /** @@ -811,16 +814,6 @@ public final class MultiSelectManager implements View.OnKeyListener { 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 getModelIds(); - void notifyItemChanged(String id); - void registerDataObserver(RecyclerView.AdapterDataObserver observer); } /** Recycler view facade implementation backed by good ol' RecyclerView. */ @@ -830,11 +823,9 @@ public final class MultiSelectManager implements View.OnKeyListener { 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); } @@ -852,11 +843,6 @@ public final class MultiSelectManager implements View.OnKeyListener { return getAdapterPositionForChildView(mView.getChildAt(index)); } - @Override - public @Nullable String getModelIdFromAdapterPosition(int position) { - return mAdapter.getModelId(position); - } - @Override public void addOnScrollListener(RecyclerView.OnScrollListener listener) { mView.addOnScrollListener(listener); @@ -973,26 +959,6 @@ public final class MultiSelectManager implements View.OnKeyListener { }); } } - - @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 getModelIds() { - return mAdapter.getModelIds(); - } } public interface Callback { @@ -1049,7 +1015,7 @@ public final class MultiSelectManager implements View.OnKeyListener { mModelBuilder = new Runnable() { @Override public void run() { - mModel = new GridModel(mEnvironment); + mModel = new GridModel(mEnvironment, mAdapter); mModel.addOnSelectionChangedListener(BandController.this); } }; @@ -1100,7 +1066,7 @@ public final class MultiSelectManager implements View.OnKeyListener { 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 } @@ -1177,7 +1143,7 @@ public final class MultiSelectManager implements View.OnKeyListener { 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); @@ -1339,6 +1305,8 @@ public final class MultiSelectManager implements View.OnKeyListener { private static final int LOWER_RIGHT = LOWER | RIGHT; private final SelectionEnvironment mHelper; + private final DocumentsAdapter mAdapter; + private final List mOnSelectionChangedListeners = new ArrayList<>(); @@ -1376,8 +1344,9 @@ public final class MultiSelectManager implements View.OnKeyListener { // 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); } @@ -1580,7 +1549,7 @@ public final class MultiSelectManager implements View.OnKeyListener { // 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. @@ -1989,7 +1958,7 @@ public final class MultiSelectManager implements View.OnKeyListener { 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. diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java index e2a90946699a9..5ce1823d6a21e 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java @@ -84,7 +84,8 @@ public class ModelBackedDocumentsAdapterTest extends AndroidTestCase { // Tests that the items can be hidden and unhidden. public void testUnhide_PreservesOrder() { List ids = mModel.getModelIds(); - SparseArray hidden = mAdapter.hide(ids.toArray(new String[ids.size()])); + SparseArray 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 diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java index 59891350838e0..e06199efbc3bb 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java @@ -23,6 +23,7 @@ import android.util.SparseBooleanArray; import com.android.documentsui.TestInputEvent; import com.android.documentsui.dirlist.MultiSelectManager.Selection; + import com.google.common.collect.Lists; import java.util.ArrayList; @@ -44,11 +45,13 @@ public class MultiSelectManagerTest extends AndroidTestCase { 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); } @@ -164,7 +167,7 @@ public class MultiSelectManagerTest extends AndroidTestCase { } 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); @@ -172,7 +175,7 @@ public class MultiSelectManagerTest extends AndroidTestCase { } 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); @@ -180,7 +183,7 @@ public class MultiSelectManagerTest extends AndroidTestCase { } 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); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java index 3d6923fbc9841..5c04db9d513ef 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java @@ -20,7 +20,6 @@ import static com.android.documentsui.dirlist.MultiSelectManager.GridModel.NOT_S 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; @@ -28,7 +27,7 @@ import android.view.View; import com.android.documentsui.dirlist.MultiSelectManager.GridModel; -import java.util.List; +import java.util.ArrayList; import java.util.Set; @SmallTest @@ -38,15 +37,28 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { 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 lastSelection; - private static int viewWidth; + private GridModel model; + private TestEnvironment env; + private TestDocumentsAdapter adapter; + private Set 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()) { + @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 @@ -64,7 +76,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionLeftOfItems() { - setUp(20, 5); + initData(20, 5); model.startSelection(new Point(0, 10)); model.resizeSelection(new Point(1, 11)); assertSelected(); @@ -72,7 +84,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionRightOfItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(viewWidth - 1, 10)); model.resizeSelection(new Point(viewWidth - 2, 11)); assertSelected(); @@ -80,7 +92,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionAboveItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(10, 0)); model.resizeSelection(new Point(11, 1)); assertSelected(); @@ -88,7 +100,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } 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(); @@ -96,7 +108,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testVerticalSelectionBetweenItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(106, 0)); model.resizeSelection(new Point(107, 200)); assertSelected(); @@ -104,7 +116,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testHorizontalSelectionBetweenItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(0, 105)); model.resizeSelection(new Point(200, 106)); assertSelected(); @@ -112,7 +124,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testGrowingAndShrinkingSelection() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(0, 0)); model.resizeSelection(new Point(5, 5)); assertSelected(0); @@ -142,7 +154,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } 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); @@ -156,7 +168,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } 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); @@ -173,14 +185,14 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { 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); @@ -322,30 +334,5 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { 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 getModelIds() { - return null; - } - - @Override - public void notifyItemChanged(String id) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerDataObserver(AdapterDataObserver observer) { - throw new UnsupportedOperationException(); - } } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java new file mode 100644 index 0000000000000..267f47d80d3fe --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java @@ -0,0 +1,82 @@ +/* + * 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 mModelIds = new ArrayList<>(); + + public TestDocumentsAdapter(List modelIds) { + mModelIds = modelIds; + } + + @Override + public void onModelUpdate(Model model) { + } + + @Override + public void onModelUpdateFailed(Exception e) { + } + + @Override + List getModelIds() { + return mModelIds; + } + + @Override + void onItemSelectionChanged(String id) { + } + + @Override + String getModelId(int position) { + return mModelIds.get(position); + } + + @Override + public SparseArray hide(String... ids) { + throw new UnsupportedOperationException(); + } + + @Override + void unhide(SparseArray 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(); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java index fc85f2b2962ee..c4cfd3a06104f 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java @@ -18,7 +18,6 @@ package com.android.documentsui.dirlist; 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; @@ -28,10 +27,7 @@ import java.util.List; public class TestSelectionEnvironment implements SelectionEnvironment { - private List mItems; - public TestSelectionEnvironment(List items) { - mItems = items; } @Override @@ -114,27 +110,4 @@ public class TestSelectionEnvironment implements SelectionEnvironment { @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 getModelIds() { - return null; - } - - @Override - public void notifyItemChanged(String id) { - } - - @Override - public void registerDataObserver(AdapterDataObserver observer) { - } }