1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui;
18 
19 import android.os.Bundle;
20 import android.view.MotionEvent;
21 
22 import androidx.annotation.VisibleForTesting;
23 import androidx.recyclerview.selection.ItemDetailsLookup;
24 import androidx.recyclerview.selection.ItemKeyProvider;
25 import androidx.recyclerview.selection.MutableSelection;
26 import androidx.recyclerview.selection.Selection;
27 import androidx.recyclerview.selection.SelectionTracker;
28 import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
29 
30 import java.util.Set;
31 
32 /**
33  * DocumentsUI SelectManager implementation that creates delegate instances
34  * each time reset is called.
35  */
36 public final class DocsSelectionHelper extends SelectionTracker<String> {
37 
38     private final DelegateFactory mFactory;
39 
40     // initialize to a dummy object incase we get some input
41     // event drive calls before we're properly initialized.
42     // See: b/69306667.
43     private SelectionTracker<String> mDelegate = new DummySelectionTracker<>();
44 
45     @VisibleForTesting
DocsSelectionHelper(DelegateFactory factory)46     DocsSelectionHelper(DelegateFactory factory) {
47         mFactory = factory;
48     }
49 
reset(SelectionTracker<String> selectionTracker)50     public void reset(SelectionTracker<String> selectionTracker) {
51         if (mDelegate != null) {
52             mDelegate.clearSelection();
53         }
54         mDelegate = mFactory.create(selectionTracker);
55     }
56 
57     @Override
addObserver(SelectionObserver listener)58     public void addObserver(SelectionObserver listener) {
59         mDelegate.addObserver(listener);
60     }
61 
62     @Override
hasSelection()63     public boolean hasSelection() {
64         return mDelegate.hasSelection();
65     }
66 
67     @Override
getSelection()68     public Selection<String> getSelection() {
69         return mDelegate.getSelection();
70     }
71 
72     @Override
copySelection(MutableSelection<String> dest)73     public void copySelection(MutableSelection<String> dest) {
74         mDelegate.copySelection(dest);
75     }
76 
77     @Override
isSelected(String id)78     public boolean isSelected(String id) {
79         return mDelegate.isSelected(id);
80     }
81 
82     @VisibleForTesting
replaceSelection(Iterable<String> ids)83     public void replaceSelection(Iterable<String> ids) {
84         mDelegate.clearSelection();
85         mDelegate.setItemsSelected(ids, true);
86     }
87 
88     @Override
setItemsSelected(Iterable<String> ids, boolean selected)89     public boolean setItemsSelected(Iterable<String> ids, boolean selected) {
90         return mDelegate.setItemsSelected(ids, selected);
91     }
92 
93     @Override
clearSelection()94     public boolean clearSelection() {
95         return mDelegate.clearSelection();
96     }
97 
98     @Override
select(String modelId)99     public boolean select(String modelId) {
100         return mDelegate.select(modelId);
101     }
102 
103     @Override
deselect(String modelId)104     public boolean deselect(String modelId) {
105         return mDelegate.deselect(modelId);
106     }
107 
108     @Override
startRange(int pos)109     public void startRange(int pos) {
110         mDelegate.startRange(pos);
111     }
112 
113     @Override
extendRange(int pos)114     public void extendRange(int pos) {
115         mDelegate.extendRange(pos);
116     }
117 
118     @Override
endRange()119     public void endRange() {
120         mDelegate.endRange();
121     }
122 
123     @Override
isRangeActive()124     public boolean isRangeActive() {
125         return mDelegate.isRangeActive();
126     }
127 
128     @Override
anchorRange(int position)129     public void anchorRange(int position) {
130         mDelegate.anchorRange(position);
131     }
132 
133     @Override
onSaveInstanceState(Bundle state)134     public void onSaveInstanceState(Bundle state) {
135         mDelegate.onSaveInstanceState(state);
136     }
137 
138     @Override
onRestoreInstanceState(Bundle state)139     public void onRestoreInstanceState(Bundle state) {
140         mDelegate.onRestoreInstanceState(state);
141     }
142 
143     // Below overridden protected methods are not used for delegation. These empty implementations
144     // are just required by abstract declaration of parent class.
145     @Override
restoreSelection(Selection<String> selection)146     protected void restoreSelection(Selection<String> selection) {
147     }
148 
149     @Override
getAdapterDataObserver()150     protected AdapterDataObserver getAdapterDataObserver() {
151         return null;
152     }
153 
154     @Override
extendProvisionalRange(int position)155     protected void extendProvisionalRange(int position) {
156     }
157 
158     @Override
setProvisionalSelection(Set<String> newSelection)159     protected void setProvisionalSelection(Set<String> newSelection) {
160     }
161 
162     @Override
clearProvisionalSelection()163     protected void clearProvisionalSelection() {
164     }
165 
166     @Override
mergeProvisionalSelection()167     protected void mergeProvisionalSelection() {
168     }
169 
create()170     public static DocsSelectionHelper create() {
171         return new DocsSelectionHelper(DelegateFactory.INSTANCE);
172     }
173 
174     /**
175      * Use of a factory to create selection manager instances allows testable instances to
176      * be inject from tests.
177      */
178     @VisibleForTesting
179     static class DelegateFactory {
180         static final DelegateFactory INSTANCE = new DelegateFactory();
181 
create(SelectionTracker<String> selectionTracker)182         SelectionTracker<String> create(SelectionTracker<String> selectionTracker) {
183             return selectionTracker;
184         }
185     }
186 
187     /**
188      * Facilitates the use of ItemDetailsLookup.
189      */
190     public static abstract class DocDetailsLookup extends ItemDetailsLookup<String> {
191 
192         // Override as public for usages in other packages.
193         @Override
overItemWithSelectionKey(MotionEvent e)194         public boolean overItemWithSelectionKey(MotionEvent e) {
195             return super.overItemWithSelectionKey(e);
196         }
197     }
198 
199     /**
200      * Facilitates the use of stable ids.
201      */
202     public static abstract class StableIdProvider extends ItemKeyProvider<String> {
203 
StableIdProvider()204         protected StableIdProvider() {
205             super(ItemKeyProvider.SCOPE_MAPPED);
206         }
207     }
208 }
209