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