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