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