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.base.SharedMinimal.VERBOSE;
     20 import static com.android.documentsui.base.State.MODE_GRID;
     21 import static com.android.documentsui.base.State.MODE_LIST;
     22 
     23 import android.content.Context;
     24 import android.graphics.Bitmap;
     25 import android.graphics.Point;
     26 import android.graphics.drawable.Drawable;
     27 import android.net.Uri;
     28 import android.provider.DocumentsContract;
     29 import android.provider.DocumentsContract.Document;
     30 import android.support.annotation.Nullable;
     31 import android.util.Log;
     32 import android.view.View;
     33 import android.widget.ImageView;
     34 
     35 import com.android.documentsui.DocumentsApplication;
     36 import com.android.documentsui.IconUtils;
     37 import com.android.documentsui.ProviderExecutor;
     38 import com.android.documentsui.R;
     39 import com.android.documentsui.ThumbnailCache;
     40 import com.android.documentsui.ThumbnailCache.Result;
     41 import com.android.documentsui.ThumbnailLoader;
     42 import com.android.documentsui.base.DocumentInfo;
     43 import com.android.documentsui.base.MimeTypes;
     44 import com.android.documentsui.base.State;
     45 import com.android.documentsui.base.State.ViewMode;
     46 
     47 import java.util.function.BiConsumer;
     48 import java.util.function.Consumer;
     49 
     50 /**
     51  * A class to assist with loading and managing the Images (i.e. thumbnails and icons) associated
     52  * with items in the directory listing.
     53  */
     54 public class IconHelper {
     55     private static final String TAG = "IconHelper";
     56 
     57     private final Context mContext;
     58     private final ThumbnailCache mThumbnailCache;
     59 
     60     // The display mode (MODE_GRID, MODE_LIST, etc).
     61     private int mMode;
     62     private Point mCurrentSize;
     63     private boolean mThumbnailsEnabled = true;
     64 
     65     /**
     66      * @param context
     67      * @param mode MODE_GRID or MODE_LIST
     68      */
     69     public IconHelper(Context context, int mode) {
     70         mContext = context;
     71         setViewMode(mode);
     72         mThumbnailCache = DocumentsApplication.getThumbnailCache(context);
     73     }
     74 
     75     /**
     76      * Enables or disables thumbnails. When thumbnails are disabled, mime icons (or custom icons, if
     77      * specified by the document) are used instead.
     78      *
     79      * @param enabled
     80      */
     81     public void setThumbnailsEnabled(boolean enabled) {
     82         mThumbnailsEnabled = enabled;
     83     }
     84 
     85     /**
     86      * Sets the current display mode. This affects the thumbnail sizes that are loaded.
     87      *
     88      * @param mode See {@link State.MODE_LIST} and {@link State.MODE_GRID}.
     89      */
     90     public void setViewMode(@ViewMode int mode) {
     91         mMode = mode;
     92         int thumbSize = getThumbSize(mode);
     93         mCurrentSize = new Point(thumbSize, thumbSize);
     94     }
     95 
     96     private int getThumbSize(int mode) {
     97         int thumbSize;
     98         switch (mode) {
     99             case MODE_GRID:
    100                 thumbSize = mContext.getResources().getDimensionPixelSize(R.dimen.grid_width);
    101                 break;
    102             case MODE_LIST:
    103                 thumbSize = mContext.getResources().getDimensionPixelSize(
    104                         R.dimen.list_item_thumbnail_size);
    105                 break;
    106             default:
    107                 throw new IllegalArgumentException("Unsupported layout mode: " + mode);
    108         }
    109         return thumbSize;
    110     }
    111 
    112     /**
    113      * Cancels any ongoing load operations associated with the given ImageView.
    114      *
    115      * @param icon
    116      */
    117     public void stopLoading(ImageView icon) {
    118         final ThumbnailLoader oldTask = (ThumbnailLoader) icon.getTag();
    119         if (oldTask != null) {
    120             oldTask.preempt();
    121             icon.setTag(null);
    122         }
    123     }
    124 
    125     /**
    126      * Load thumbnails for a directory list item.
    127      *
    128      * @param doc The document
    129      * @param iconThumb The itemview's thumbnail icon.
    130      * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
    131      * @param subIconMime The second itemview's mime icon. Always visible.
    132      * @return
    133      */
    134     public void load(
    135             DocumentInfo doc,
    136             ImageView iconThumb,
    137             ImageView iconMime,
    138             @Nullable ImageView subIconMime) {
    139         load(doc.derivedUri, doc.mimeType, doc.flags, doc.icon, doc.lastModified,
    140                 iconThumb, iconMime, subIconMime);
    141     }
    142 
    143     /**
    144      * Load thumbnails for a directory list item.
    145      *
    146      * @param uri The URI for the file being represented.
    147      * @param mimeType The mime type of the file being represented.
    148      * @param docFlags Flags for the file being represented.
    149      * @param docIcon Custom icon (if any) for the file being requested.
    150      * @param docLastModified the last modified value of the file being requested.
    151      * @param iconThumb The itemview's thumbnail icon.
    152      * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
    153      * @param subIconMime The second itemview's mime icon. Always visible.
    154      * @return
    155      */
    156     public void load(Uri uri, String mimeType, int docFlags, int docIcon, long docLastModified,
    157             ImageView iconThumb, ImageView iconMime, @Nullable ImageView subIconMime) {
    158         boolean loadedThumbnail = false;
    159 
    160         final String docAuthority = uri.getAuthority();
    161 
    162         final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
    163         final boolean allowThumbnail = (mMode == MODE_GRID)
    164                 || MimeTypes.mimeMatches(MimeTypes.VISUAL_MIMES, mimeType);
    165         final boolean showThumbnail = supportsThumbnail && allowThumbnail && mThumbnailsEnabled;
    166         if (showThumbnail) {
    167             loadedThumbnail =
    168                 loadThumbnail(uri, docAuthority, docLastModified, iconThumb, iconMime);
    169         }
    170 
    171         final Drawable mimeIcon = getDocumentIcon(mContext, docAuthority,
    172                 DocumentsContract.getDocumentId(uri), mimeType, docIcon);
    173         if (subIconMime != null) {
    174             setMimeIcon(subIconMime, mimeIcon);
    175         }
    176 
    177         if (loadedThumbnail) {
    178             hideImageView(iconMime);
    179         } else {
    180             // Add a mime icon if the thumbnail is not shown.
    181             setMimeIcon(iconMime, mimeIcon);
    182             hideImageView(iconThumb);
    183         }
    184     }
    185 
    186     private boolean loadThumbnail(Uri uri, String docAuthority, long docLastModified,
    187             ImageView iconThumb, ImageView iconMime) {
    188         final Result result = mThumbnailCache.getThumbnail(uri, mCurrentSize);
    189 
    190         try {
    191             final Bitmap cachedThumbnail = result.getThumbnail();
    192             iconThumb.setImageBitmap(cachedThumbnail);
    193 
    194             boolean stale = (docLastModified > result.getLastModified());
    195             if (VERBOSE) Log.v(TAG,
    196                     String.format("Load thumbnail for %s, got result %d and stale %b.",
    197                             uri.toString(), result.getStatus(), stale));
    198             if (!result.isExactHit() || stale) {
    199                 final BiConsumer<View, View> animator =
    200                         (cachedThumbnail == null ? ThumbnailLoader.ANIM_FADE_IN :
    201                                 ThumbnailLoader.ANIM_NO_OP);
    202 
    203                 Consumer<Bitmap> callback = new Consumer<Bitmap>() {
    204                     @Override
    205                     public void accept(Bitmap bitmap) {
    206                         if (result != null) {
    207                             iconThumb.setImageBitmap(bitmap);
    208                             animator.accept(iconMime, iconThumb);
    209                         }
    210                     }
    211                 };
    212 
    213                 final ThumbnailLoader task = new ThumbnailLoader(uri, iconThumb,
    214                     mCurrentSize, docLastModified, callback, true);
    215 
    216                 ProviderExecutor.forAuthority(docAuthority).execute(task);
    217             }
    218 
    219             return result.isHit();
    220         } finally {
    221             result.recycle();
    222         }
    223     }
    224 
    225     private void setMimeIcon(ImageView view, Drawable icon) {
    226         view.setImageDrawable(icon);
    227         view.setAlpha(1f);
    228     }
    229 
    230     private void hideImageView(ImageView view) {
    231         view.setImageDrawable(null);
    232         view.setAlpha(0f);
    233     }
    234 
    235     private Drawable getDocumentIcon(
    236         Context context, String authority, String id, String mimeType, int icon) {
    237         if (icon != 0) {
    238             return IconUtils.loadPackageIcon(context, authority, icon);
    239         } else {
    240             return IconUtils.loadMimeIcon(context, mimeType, authority, id, mMode);
    241         }
    242     }
    243 
    244     /**
    245      * Returns a mime icon or package icon for a {@link DocumentInfo}.
    246      */
    247     public Drawable getDocumentIcon(Context context, DocumentInfo doc) {
    248         return getDocumentIcon(
    249                 context, doc.authority, doc.documentId, doc.mimeType, doc.icon);
    250     }
    251 }
    252