blob: 42dba4591326e29d97a032636f328e059f4011b1 [file] [log] [blame]
Steve McKayef16f5f2015-12-22 18:15:31 -08001/*
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
17package com.android.documentsui.dirlist;
18
19import static com.android.documentsui.Shared.DEBUG;
20import static com.android.documentsui.State.MODE_GRID;
21import static com.android.documentsui.State.MODE_LIST;
Steve McKayef16f5f2015-12-22 18:15:31 -080022import static com.android.documentsui.model.DocumentInfo.getCursorInt;
23import static com.android.documentsui.model.DocumentInfo.getCursorString;
24
25import android.database.Cursor;
26import android.provider.DocumentsContract.Document;
Steve McKay58fe5372016-02-26 17:23:44 -080027import android.support.annotation.VisibleForTesting;
Steve McKayef16f5f2015-12-22 18:15:31 -080028import android.util.Log;
29import android.util.SparseArray;
30import android.view.ViewGroup;
31
32import com.android.documentsui.State;
Steve McKayef16f5f2015-12-22 18:15:31 -080033import com.google.common.collect.Sets;
34
35import java.util.ArrayList;
36import java.util.HashSet;
37import java.util.List;
38import java.util.Set;
39
40/**
41 * Adapts from dirlist.Model to something RecyclerView understands.
42 */
43final class ModelBackedDocumentsAdapter extends DocumentsAdapter {
44
Ben Kwae3aee182016-02-02 12:11:10 -080045 private static final String TAG = "ModelBackedDocuments";
Steve McKayef16f5f2015-12-22 18:15:31 -080046 public static final int ITEM_TYPE_DOCUMENT = 1;
47 public static final int ITEM_TYPE_DIRECTORY = 2;
48
49 // Provides access to information needed when creating and view holders. This
50 // isn't an ideal pattern (more transitive dependency stuff) but good enough for now.
51 private final Environment mEnv;
52 private final IconHelper mIconHelper; // a transitive dependency of the holders.
53
54 /**
55 * An ordered list of model IDs. This is the data structure that determines what shows up in
56 * the UI, and where.
57 */
58 private List<String> mModelIds = new ArrayList<>();
59
60 // List of files that have been deleted. Some transient directory updates
61 // may happen while files are being deleted. During this time we don't
62 // want once-hidden files to be re-shown. We only remove
63 // items from this list when we get a model update where the model
64 // does not contain a corresponding id. This ensures hidden entries
65 // don't momentarily re-appear if we get intermediate updates from
66 // the file system.
67 private Set<String> mHiddenIds = new HashSet<>();
68
69 public ModelBackedDocumentsAdapter(Environment env, IconHelper iconHelper) {
70 mEnv = env;
71 mIconHelper = iconHelper;
72 }
73
74 @Override
75 public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
76 DocumentHolder holder = null;
77 final State state = mEnv.getDisplayState();
78 switch (state.derivedMode) {
79 case MODE_GRID:
80 switch (viewType) {
81 case ITEM_TYPE_DIRECTORY:
82 holder = new GridDirectoryHolder(mEnv.getContext(), parent);
83 break;
84 case ITEM_TYPE_DOCUMENT:
85 holder = new GridDocumentHolder(mEnv.getContext(), parent, mIconHelper);
86 break;
87 default:
88 throw new IllegalStateException("Unsupported layout type.");
89 }
90 break;
91 case MODE_LIST:
92 holder = new ListDocumentHolder(mEnv.getContext(), parent, mIconHelper);
93 break;
Steve McKayef16f5f2015-12-22 18:15:31 -080094 default:
95 throw new IllegalStateException("Unsupported layout mode.");
96 }
97
98 mEnv.initDocumentHolder(holder);
99 return holder;
100 }
101
102 @Override
103 public void onBindViewHolder(DocumentHolder holder, int position, List<Object> payload) {
104 if (payload.contains(SELECTION_CHANGED_MARKER)) {
105 final boolean selected = mEnv.isSelected(mModelIds.get(position));
106 holder.setSelected(selected);
107 } else {
108 onBindViewHolder(holder, position);
109 }
110 }
111
112 @Override
113 public void onBindViewHolder(DocumentHolder holder, int position) {
114 String modelId = mModelIds.get(position);
115 Cursor cursor = mEnv.getModel().getItem(modelId);
116 holder.bind(cursor, modelId, mEnv.getDisplayState());
117
118 final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
119 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
120
121 holder.setSelected(mEnv.isSelected(modelId));
122 holder.setEnabled(mEnv.isDocumentEnabled(docMimeType, docFlags));
123
124 mEnv.onBindDocumentHolder(holder, cursor);
125 }
126
127 @Override
128 public int getItemCount() {
129 return mModelIds.size();
130 }
131
132 @Override
133 public void onModelUpdate(Model model) {
134 if (DEBUG && mHiddenIds.size() > 0) {
135 Log.d(TAG, "Updating model with hidden ids: " + mHiddenIds);
136 }
137
138 List<String> modelIds = model.getModelIds();
139 mModelIds = new ArrayList<>(modelIds.size());
140 for (String id : modelIds) {
141 if (!mHiddenIds.contains(id)) {
142 mModelIds.add(id);
143 } else {
144 if (DEBUG) Log.d(TAG, "Omitting hidden id from model during update: " + id);
145 }
146 }
147
148 // Finally remove any hidden ids that aren't present in the model.
149 // This assumes that model updates represent a complete set of files.
150 mHiddenIds.retainAll(mModelIds);
151 }
152
153 @Override
154 public void onModelUpdateFailed(Exception e) {
155 Log.w(TAG, "Model update failed.", e);
156 mModelIds.clear();
157 }
158
159 @Override
160 public String getModelId(int adapterPosition) {
161 return mModelIds.get(adapterPosition);
162 }
163
164 @Override
165 public SparseArray<String> hide(String... ids) {
166 if (DEBUG) Log.d(TAG, "Hiding ids: " + ids);
167 Set<String> toHide = Sets.newHashSet(ids);
168
169 // Proceed backwards through the list of items, because each removal causes the
170 // positions of all subsequent items to change.
171 SparseArray<String> hiddenItems = new SparseArray<>();
172 for (int i = mModelIds.size() - 1; i >= 0; --i) {
173 String id = mModelIds.get(i);
174 if (toHide.contains(id)) {
175 mHiddenIds.add(id);
176 hiddenItems.put(i, mModelIds.remove(i));
177 notifyItemRemoved(i);
178 }
179 }
180
181 return hiddenItems;
182 }
183
Steve McKay58fe5372016-02-26 17:23:44 -0800184 @VisibleForTesting
Steve McKayef16f5f2015-12-22 18:15:31 -0800185 @Override
186 public void unhide(SparseArray<String> ids) {
Ben Kwaa4acc902016-02-10 15:48:25 -0800187 if (DEBUG) Log.d(TAG, "Unhiding ids: " + ids);
Steve McKay955e46d2016-01-05 12:53:35 -0800188
189 // An ArrayList can shrink at runtime...and in fact
190 // it does when we clear it completely.
191 // This means we can't call add(pos, id) without
192 // first checking the list size.
193 List<String> oldIds = mModelIds;
194 mModelIds = new ArrayList<>(oldIds.size() + ids.size());
195 mModelIds.addAll(oldIds);
196
197 // Finally insert the unhidden items.
198 for (int i = 0; i < ids.size(); i++) {
Steve McKayef16f5f2015-12-22 18:15:31 -0800199 int pos = ids.keyAt(i);
200 String id = ids.get(pos);
201 mHiddenIds.remove(id);
202 mModelIds.add(pos, id);
203 notifyItemInserted(pos);
204 }
205 }
206
207 @Override
208 public List<String> getModelIds() {
209 return mModelIds;
210 }
211
212 @Override
213 public int getItemViewType(int position) {
214 return isDirectory(mEnv.getModel(), position)
215 ? ITEM_TYPE_DIRECTORY
216 : ITEM_TYPE_DOCUMENT;
217 }
218
219 @Override
Steve McKay955e46d2016-01-05 12:53:35 -0800220 public void onItemSelectionChanged(String id) {
Steve McKayef16f5f2015-12-22 18:15:31 -0800221 int position = mModelIds.indexOf(id);
222
223 if (position >= 0) {
224 notifyItemChanged(position, SELECTION_CHANGED_MARKER);
225 } else {
226 Log.w(TAG, "Item change notification received for unknown item: " + id);
227 }
228 }
229}