• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.dirlist;
18 
19 import static com.android.documentsui.base.DocumentInfo.getCursorInt;
20 import static com.android.documentsui.base.DocumentInfo.getCursorString;
21 import static com.android.documentsui.base.State.MODE_GRID;
22 import static com.android.documentsui.base.State.MODE_LIST;
23 import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled;
24 
25 import android.database.Cursor;
26 import android.provider.DocumentsContract.Document;
27 import android.util.Log;
28 import android.view.ViewGroup;
29 
30 import androidx.recyclerview.selection.SelectionTracker;
31 import androidx.recyclerview.widget.RecyclerView;
32 
33 import com.android.documentsui.ConfigStore;
34 import com.android.documentsui.Model;
35 import com.android.documentsui.Model.Update;
36 import com.android.documentsui.base.EventListener;
37 import com.android.documentsui.base.Lookup;
38 import com.android.documentsui.base.State;
39 import com.android.documentsui.roots.RootCursorWrapper;
40 import com.android.modules.utils.build.SdkLevel;
41 
42 import java.util.ArrayList;
43 import java.util.List;
44 
45 /**
46  * Adapts from dirlist.Model to something RecyclerView understands.
47  */
48 final class ModelBackedDocumentsAdapter extends DocumentsAdapter {
49 
50     private static final String TAG = "ModelBackedDocuments";
51 
52     // Provides access to information needed when creating and view holders. This
53     // isn't an ideal pattern (more transitive dependency stuff) but good enough for now.
54     private final Environment mEnv;
55     private final IconHelper mIconHelper;  // a transitive dependency of the holders.
56     private final Lookup<String, String> mFileTypeLookup;
57     private final ConfigStore mConfigStore;
58 
59     /**
60      * An ordered list of model IDs. This is the data structure that determines what shows up in
61      * the UI, and where.
62      */
63     private List<String> mModelIds = new ArrayList<>();
64     private EventListener<Model.Update> mModelUpdateListener;
65 
ModelBackedDocumentsAdapter( Environment env, IconHelper iconHelper, Lookup<String, String> fileTypeLookup, ConfigStore configStore)66     public ModelBackedDocumentsAdapter(
67             Environment env, IconHelper iconHelper, Lookup<String, String> fileTypeLookup,
68             ConfigStore configStore) {
69         mEnv = env;
70         mIconHelper = iconHelper;
71         mFileTypeLookup = fileTypeLookup;
72         mConfigStore = configStore;
73 
74         mModelUpdateListener = new EventListener<Model.Update>() {
75             @Override
76             public void accept(Update event) {
77                 if (event.hasException()) {
78                     onModelUpdateFailed(event.getException());
79                 } else {
80                     onModelUpdate(mEnv.getModel());
81                 }
82             }
83         };
84     }
85 
86     @Override
getModelUpdateListener()87     EventListener<Update> getModelUpdateListener() {
88         return mModelUpdateListener;
89     }
90 
91     @Override
onCreateViewHolder(ViewGroup parent, int viewType)92     public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
93         DocumentHolder holder = null;
94         final State state = mEnv.getDisplayState();
95         switch (state.derivedMode) {
96             case MODE_GRID:
97                 switch (viewType) {
98                     case ITEM_TYPE_DIRECTORY:
99                         // Under the Material3 flag, the GridDocumentHolder is the holder for all
100                         // grid items.
101                         holder = isUseMaterial3FlagEnabled()
102                                 ? new GridDocumentHolder(
103                                 mEnv.getContext(), parent, mIconHelper, mConfigStore)
104                                 : new GridDirectoryHolder(
105                                         mEnv.getContext(), parent, mIconHelper, mConfigStore);
106                         break;
107                     case ITEM_TYPE_DOCUMENT:
108                         holder = state.isPhotoPicking()
109                                 ? new GridPhotoHolder(mEnv.getContext(), parent, mIconHelper,
110                                 mConfigStore)
111                                 : new GridDocumentHolder(mEnv.getContext(), parent, mIconHelper,
112                                         mConfigStore);
113                         break;
114                     default:
115                         throw new IllegalStateException("Unsupported layout type.");
116                 }
117                 break;
118             case MODE_LIST:
119                 holder = new ListDocumentHolder(
120                         mEnv.getContext(), parent, mIconHelper, mFileTypeLookup, mConfigStore);
121                 break;
122             default:
123                 throw new IllegalStateException("Unsupported layout mode.");
124         }
125 
126         mEnv.initDocumentHolder(holder);
127         return holder;
128     }
129 
130     @Override
onBindViewHolder(DocumentHolder holder, int position, List<Object> payload)131     public void onBindViewHolder(DocumentHolder holder, int position, List<Object> payload) {
132         if (payload.contains(SelectionTracker.SELECTION_CHANGED_MARKER)) {
133             final boolean selected = mEnv.isSelected(mModelIds.get(position));
134             holder.setSelected(selected, true);
135         } else {
136             onBindViewHolder(holder, position);
137         }
138     }
139 
140     @Override
onBindViewHolder(DocumentHolder holder, int position)141     public void onBindViewHolder(DocumentHolder holder, int position) {
142         String modelId = mModelIds.get(position);
143         Cursor cursor = mEnv.getModel().getItem(modelId);
144         holder.bind(cursor, modelId);
145 
146         final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
147         final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
148         final int userIdIdentifier = getCursorInt(cursor, RootCursorWrapper.COLUMN_USER_ID);
149 
150         boolean enabled = mEnv.isDocumentEnabled(docMimeType, docFlags);
151         boolean selected = mEnv.isSelected(modelId);
152         if (!enabled) {
153             assert (!selected);
154         }
155         holder.setEnabled(enabled);
156         holder.setSelected(mEnv.isSelected(modelId), false);
157         holder.setAction(mEnv.getDisplayState().action);
158         holder.bindPreviewIcon(mEnv.getDisplayState().shouldShowPreview() && enabled,
159                 view -> mEnv.getActionHandler().previewItem(holder.getItemDetails()));
160         if (mConfigStore.isPrivateSpaceInDocsUIEnabled() && SdkLevel.isAtLeastS()) {
161             holder.bindProfileIcon(mIconHelper.shouldShowBadge(userIdIdentifier), userIdIdentifier);
162         } else {
163             holder.bindBriefcaseIcon(mIconHelper.shouldShowBadge(userIdIdentifier));
164         }
165 
166         mEnv.onBindDocumentHolder(holder, cursor);
167     }
168 
169     @Override
getItemCount()170     public int getItemCount() {
171         return mModelIds.size();
172     }
173 
onModelUpdate(Model model)174     private void onModelUpdate(Model model) {
175         String[] modelIds = model.getModelIds();
176         mModelIds = new ArrayList<>(modelIds.length);
177         for (String id : modelIds) {
178             mModelIds.add(id);
179         }
180     }
181 
onModelUpdateFailed(Exception e)182     private void onModelUpdateFailed(Exception e) {
183         Log.w(TAG, "Model update failed.", e);
184         mModelIds.clear();
185     }
186 
187     @Override
getStableId(int adapterPosition)188     public String getStableId(int adapterPosition) {
189         return mModelIds.get(adapterPosition);
190     }
191 
192     @Override
getAdapterPosition(String modelId)193     public int getAdapterPosition(String modelId) {
194         return mModelIds.indexOf(modelId);
195     }
196 
197     @Override
getStableIds()198     public List<String> getStableIds() {
199         return mModelIds;
200     }
201 
202     @Override
getPosition(String id)203     public int getPosition(String id) {
204         int position = mModelIds.indexOf(id);
205         return position >= 0 ? position : RecyclerView.NO_POSITION;
206     }
207 
208     @Override
getItemViewType(int position)209     public int getItemViewType(int position) {
210         return isDirectory(mEnv.getModel(), position)
211                 ? ITEM_TYPE_DIRECTORY
212                 : ITEM_TYPE_DOCUMENT;
213     }
214 
215     /**
216      * @return true if the item type is either document or directory, false for all other
217      * possible types.
218      */
isContentType(int type)219     public static boolean isContentType(int type) {
220         switch (type) {
221             case ModelBackedDocumentsAdapter.ITEM_TYPE_DOCUMENT:
222             case ModelBackedDocumentsAdapter.ITEM_TYPE_DIRECTORY:
223                 return true;
224         }
225         return false;
226     }
227 }
228