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 
     23 import android.content.ContentProviderClient;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.graphics.Bitmap;
     27 import android.graphics.Point;
     28 import android.graphics.drawable.Drawable;
     29 import android.net.Uri;
     30 import android.os.AsyncTask;
     31 import android.os.CancellationSignal;
     32 import android.os.OperationCanceledException;
     33 import android.provider.DocumentsContract;
     34 import android.provider.DocumentsContract.Document;
     35 import android.support.annotation.Nullable;
     36 import android.util.Log;
     37 import android.widget.ImageView;
     38 
     39 import com.android.documentsui.DocumentsApplication;
     40 import com.android.documentsui.IconUtils;
     41 import com.android.documentsui.MimePredicate;
     42 import com.android.documentsui.ProviderExecutor;
     43 import com.android.documentsui.ProviderExecutor.Preemptable;
     44 import com.android.documentsui.R;
     45 import com.android.documentsui.State;
     46 import com.android.documentsui.State.ViewMode;
     47 import com.android.documentsui.ThumbnailCache;
     48 
     49 /**
     50  * A class to assist with loading and managing the Images (i.e. thumbnails and icons) associated
     51  * with items in the directory listing.
     52  */
     53 public class IconHelper {
     54     private static String TAG = "IconHelper";
     55 
     56     private final Context mContext;
     57 
     58     // Updated when icon size is set.
     59     private ThumbnailCache mCache;
     60     private Point mThumbSize;
     61     // The display mode (MODE_GRID, MODE_LIST, etc).
     62     private int mMode;
     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         mCache = DocumentsApplication.getThumbnailsCache(context, mThumbSize);
     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      * @param mode See {@link State.MODE_LIST} and {@link State.MODE_GRID}.
     88      */
     89     public void setViewMode(@ViewMode int mode) {
     90         mMode = mode;
     91         int thumbSize = getThumbSize(mode);
     92         mThumbSize = new Point(thumbSize, thumbSize);
     93         mCache = DocumentsApplication.getThumbnailsCache(mContext, mThumbSize);
     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      * @param icon
    115      */
    116     public void stopLoading(ImageView icon) {
    117         final LoaderTask oldTask = (LoaderTask) icon.getTag();
    118         if (oldTask != null) {
    119             oldTask.preempt();
    120             icon.setTag(null);
    121         }
    122     }
    123 
    124     /** Internal task for loading thumbnails asynchronously. */
    125     private static class LoaderTask
    126             extends AsyncTask<Uri, Void, Bitmap>
    127             implements Preemptable {
    128         private final Uri mUri;
    129         private final ImageView mIconMime;
    130         private final ImageView mIconThumb;
    131         private final Point mThumbSize;
    132         private final CancellationSignal mSignal;
    133 
    134         public LoaderTask(Uri uri, ImageView iconMime, ImageView iconThumb,
    135                 Point thumbSize) {
    136             mUri = uri;
    137             mIconMime = iconMime;
    138             mIconThumb = iconThumb;
    139             mThumbSize = thumbSize;
    140             mSignal = new CancellationSignal();
    141             if (DEBUG) Log.d(TAG, "Starting icon loader task for " + mUri);
    142         }
    143 
    144         @Override
    145         public void preempt() {
    146             if (DEBUG) Log.d(TAG, "Icon loader task for " + mUri + " was cancelled.");
    147             cancel(false);
    148             mSignal.cancel();
    149         }
    150 
    151         @Override
    152         protected Bitmap doInBackground(Uri... params) {
    153             if (isCancelled())
    154                 return null;
    155 
    156             final Context context = mIconThumb.getContext();
    157             final ContentResolver resolver = context.getContentResolver();
    158 
    159             ContentProviderClient client = null;
    160             Bitmap result = null;
    161             try {
    162                 client = DocumentsApplication.acquireUnstableProviderOrThrow(
    163                         resolver, mUri.getAuthority());
    164                 result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
    165                 if (result != null) {
    166                     final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
    167                             context, mThumbSize);
    168                     thumbs.put(mUri, result);
    169                 }
    170             } catch (Exception e) {
    171                 if (!(e instanceof OperationCanceledException)) {
    172                     Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
    173                 }
    174             } finally {
    175                 ContentProviderClient.releaseQuietly(client);
    176             }
    177             return result;
    178         }
    179 
    180         @Override
    181         protected void onPostExecute(Bitmap result) {
    182             if (DEBUG) Log.d(TAG, "Loader task for " + mUri + " completed");
    183 
    184             if (mIconThumb.getTag() == this && result != null) {
    185                 mIconThumb.setTag(null);
    186                 mIconThumb.setImageBitmap(result);
    187 
    188                 float alpha = mIconMime.getAlpha();
    189                 mIconMime.animate().alpha(0f).start();
    190                 mIconThumb.setAlpha(0f);
    191                 mIconThumb.animate().alpha(alpha).start();
    192             }
    193         }
    194     }
    195 
    196     /**
    197      * Load thumbnails for a directory list item.
    198      * @param uri The URI for the file being represented.
    199      * @param mimeType The mime type of the file being represented.
    200      * @param docFlags Flags for the file being represented.
    201      * @param docIcon Custom icon (if any) for the file being requested.
    202      * @param iconThumb The itemview's thumbnail icon.
    203      * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
    204      * @param subIconMime The second itemview's mime icon. Always visible.
    205      * @return
    206      */
    207     public void loadThumbnail(Uri uri, String mimeType, int docFlags, int docIcon,
    208             ImageView iconThumb, ImageView iconMime, @Nullable ImageView subIconMime) {
    209         boolean cacheHit = false;
    210 
    211         final String docAuthority = uri.getAuthority();
    212 
    213         final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
    214         final boolean allowThumbnail = (mMode == MODE_GRID)
    215                 || MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mimeType);
    216         final boolean showThumbnail = supportsThumbnail && allowThumbnail && mThumbnailsEnabled;
    217         if (showThumbnail) {
    218             final Bitmap cachedResult = mCache.get(uri);
    219             if (cachedResult != null) {
    220                 iconThumb.setImageBitmap(cachedResult);
    221                 cacheHit = true;
    222             } else {
    223                 iconThumb.setImageDrawable(null);
    224                 final LoaderTask task = new LoaderTask(uri, iconMime, iconThumb, mThumbSize);
    225                 iconThumb.setTag(task);
    226                 ProviderExecutor.forAuthority(docAuthority).execute(task);
    227             }
    228         }
    229 
    230         final Drawable icon = getDocumentIcon(mContext, docAuthority,
    231                 DocumentsContract.getDocumentId(uri), mimeType, docIcon);
    232         if (subIconMime != null) {
    233             subIconMime.setImageDrawable(icon);
    234         }
    235 
    236         if (cacheHit) {
    237             iconMime.setImageDrawable(null);
    238             iconMime.setAlpha(0f);
    239             iconThumb.setAlpha(1f);
    240         } else {
    241             // Add a mime icon if the thumbnail is being loaded in the background.
    242             iconThumb.setImageDrawable(null);
    243             iconMime.setImageDrawable(icon);
    244             iconMime.setAlpha(1f);
    245             iconThumb.setAlpha(0f);
    246         }
    247     }
    248 
    249     /**
    250      * Gets a mime icon or package icon for a file.
    251      * @param context
    252      * @param authority The authority string of the file.
    253      * @param id The document ID of the file.
    254      * @param mimeType The mime type of the file.
    255      * @param icon The custom icon (if any) of the file.
    256      * @return
    257      */
    258     public Drawable getDocumentIcon(Context context, String authority, String id,
    259             String mimeType, int icon) {
    260         if (icon != 0) {
    261             return IconUtils.loadPackageIcon(context, authority, icon);
    262         } else {
    263             return IconUtils.loadMimeIcon(context, mimeType, authority, id, mMode);
    264         }
    265     }
    266 
    267 }
    268