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