Home | History | Annotate | Download | only in dirlist
      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.Shared.DEBUG;
     20 import static com.android.documentsui.State.MODE_GRID;
     21 import static com.android.documentsui.State.MODE_LIST;
     22 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
     23 import static com.android.documentsui.model.DocumentInfo.getCursorString;
     24 
     25 import android.database.Cursor;
     26 import android.provider.DocumentsContract.Document;
     27 import android.util.Log;
     28 import android.util.SparseArray;
     29 import android.view.ViewGroup;
     30 
     31 import com.android.documentsui.State;
     32 import com.google.common.collect.Sets;
     33 
     34 import java.util.ArrayList;
     35 import java.util.HashSet;
     36 import java.util.List;
     37 import java.util.Set;
     38 
     39 /**
     40  * Adapts from dirlist.Model to something RecyclerView understands.
     41  */
     42 final class ModelBackedDocumentsAdapter extends DocumentsAdapter {
     43 
     44     private static final String TAG = "ModelBackedDocuments";
     45     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;
     93             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, true);
    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         boolean enabled = mEnv.isDocumentEnabled(docMimeType, docFlags);
    121         boolean selected = mEnv.isSelected(modelId);
    122         if (!enabled) {
    123             assert(!selected);
    124         }
    125         holder.setEnabled(enabled);
    126         holder.setSelected(mEnv.isSelected(modelId), false);
    127 
    128         mEnv.onBindDocumentHolder(holder, cursor);
    129     }
    130 
    131     @Override
    132     public int getItemCount() {
    133         return mModelIds.size();
    134     }
    135 
    136     @Override
    137     public void onModelUpdate(Model model) {
    138         if (DEBUG && mHiddenIds.size() > 0) {
    139             Log.d(TAG, "Updating model with hidden ids: " + mHiddenIds);
    140         }
    141 
    142         String[] modelIds = model.getModelIds();
    143         mModelIds = new ArrayList<>(modelIds.length);
    144         for (String id : modelIds) {
    145             if (!mHiddenIds.contains(id)) {
    146                 mModelIds.add(id);
    147             } else {
    148                 if (DEBUG) Log.d(TAG, "Omitting hidden id from model during update: " + id);
    149             }
    150         }
    151 
    152         // Finally remove any hidden ids that aren't present in the model.
    153         // This assumes that model updates represent a complete set of files.
    154         mHiddenIds.retainAll(mModelIds);
    155     }
    156 
    157     @Override
    158     public void onModelUpdateFailed(Exception e) {
    159         Log.w(TAG, "Model update failed.", e);
    160         mModelIds.clear();
    161     }
    162 
    163     @Override
    164     public String getModelId(int adapterPosition) {
    165         return mModelIds.get(adapterPosition);
    166     }
    167 
    168     @Override
    169     public SparseArray<String> hide(String... ids) {
    170         if (DEBUG) Log.d(TAG, "Hiding ids: " + ids);
    171         Set<String> toHide = Sets.newHashSet(ids);
    172 
    173         // Proceed backwards through the list of items, because each removal causes the
    174         // positions of all subsequent items to change.
    175         SparseArray<String> hiddenItems = new SparseArray<>();
    176         for (int i = mModelIds.size() - 1; i >= 0; --i) {
    177             String id = mModelIds.get(i);
    178             if (toHide.contains(id)) {
    179                 mHiddenIds.add(id);
    180                 hiddenItems.put(i, mModelIds.remove(i));
    181                 notifyItemRemoved(i);
    182             }
    183         }
    184 
    185         return hiddenItems;
    186     }
    187 
    188     @Override
    189     public List<String> getModelIds() {
    190         return mModelIds;
    191     }
    192 
    193     @Override
    194     public int getItemViewType(int position) {
    195         return isDirectory(mEnv.getModel(), position)
    196                 ? ITEM_TYPE_DIRECTORY
    197                 : ITEM_TYPE_DOCUMENT;
    198     }
    199 
    200     @Override
    201     public void onItemSelectionChanged(String id) {
    202         int position = mModelIds.indexOf(id);
    203 
    204         if (position >= 0) {
    205             notifyItemChanged(position, SELECTION_CHANGED_MARKER);
    206         } else {
    207             Log.w(TAG, "Item change notification received for unknown item: " + id);
    208         }
    209     }
    210 }
    211