Home | History | Annotate | Download | only in documentsui
      1 /*
      2  * Copyright (C) 2017 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 package com.android.documentsui;
     17 
     18 import static com.android.documentsui.base.Shared.VERBOSE;
     19 
     20 import android.annotation.Nullable;
     21 import android.content.ContentProviderClient;
     22 import android.content.ContentResolver;
     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.os.AsyncTask;
     29 import android.os.CancellationSignal;
     30 import android.os.OperationCanceledException;
     31 import android.provider.DocumentsContract;
     32 import android.util.Log;
     33 import android.view.View;
     34 import android.widget.ImageView;
     35 import com.android.documentsui.ProviderExecutor.Preemptable;
     36 import java.util.function.BiConsumer;
     37 import java.util.function.Consumer;
     38 
     39 /**
     40  *  Loads a Thumbnails asynchronously then animates from the mime icon to the thumbnail
     41  */
     42 public final class ThumbnailLoader extends AsyncTask<Uri, Void, Bitmap> implements Preemptable {
     43 
     44     private static final String TAG = ThumbnailLoader.class.getCanonicalName();
     45 
     46     /**
     47      * Two animations applied to image views. The first is used to switch mime icon and thumbnail.
     48      * The second is used when we need to update thumbnail.
     49      */
     50     public static final BiConsumer<View, View> ANIM_FADE_IN = (mime, thumb) -> {
     51         float alpha = mime.getAlpha();
     52         mime.animate().alpha(0f).start();
     53         thumb.setAlpha(0f);
     54         thumb.animate().alpha(alpha).start();
     55     };
     56     public static final BiConsumer<View, View> ANIM_NO_OP = (mime, thumb) -> {};
     57 
     58     private final ImageView mIconThumb;
     59     private final Point mThumbSize;
     60     private final Uri mUri;
     61     private final long mLastModified;
     62     private final Consumer<Bitmap> mCallback;
     63     private final boolean mAddToCache;
     64     private final CancellationSignal mSignal;
     65 
     66     /**
     67      * @param uri - to a thumbnail.
     68      * @param iconThumb - ImageView to display the thumbnail.
     69      * @param thumbSize - size of the thumbnail.
     70      * @param lastModified - used for updating thumbnail caches.
     71      * @param addToCache - flag that determines if the loader saves the thumbnail to the cache.
     72      */
     73     public ThumbnailLoader(Uri uri, ImageView iconThumb, Point thumbSize, long lastModified,
     74         Consumer<Bitmap> callback, boolean addToCache) {
     75 
     76         mUri = uri;
     77         mIconThumb = iconThumb;
     78         mThumbSize = thumbSize;
     79         mLastModified = lastModified;
     80         mCallback = callback;
     81         mAddToCache = addToCache;
     82         mSignal = new CancellationSignal();
     83         mIconThumb.setTag(this);
     84 
     85         if (VERBOSE) Log.v(TAG, "Starting icon loader task for " + mUri);
     86     }
     87 
     88     @Override
     89     public void preempt() {
     90         if (VERBOSE) Log.v(TAG, "Icon loader task for " + mUri + " was cancelled.");
     91         cancel(false);
     92         mSignal.cancel();
     93     }
     94 
     95     @Override
     96     protected Bitmap doInBackground(Uri... params) {
     97         if (isCancelled()) {
     98             return null;
     99         }
    100 
    101         final Context context = mIconThumb.getContext();
    102         final ContentResolver resolver = context.getContentResolver();
    103 
    104         ContentProviderClient client = null;
    105         Bitmap result = null;
    106         try {
    107             client = DocumentsApplication.acquireUnstableProviderOrThrow(
    108                 resolver, mUri.getAuthority());
    109             result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
    110             if (result != null && mAddToCache) {
    111                 final ThumbnailCache cache = DocumentsApplication.getThumbnailCache(context);
    112                 cache.putThumbnail(mUri, mThumbSize, result, mLastModified);
    113             }
    114         } catch (Exception e) {
    115             if (!(e instanceof OperationCanceledException)) {
    116                 Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
    117             }
    118         } finally {
    119             ContentProviderClient.releaseQuietly(client);
    120         }
    121         return result;
    122     }
    123 
    124     @Override
    125     protected void onPostExecute(Bitmap result) {
    126         if (VERBOSE) Log.v(TAG, "Loader task for " + mUri + " completed");
    127 
    128         if (mIconThumb.getTag() == this) {
    129             mIconThumb.setTag(null);
    130             mCallback.accept(result);
    131         }
    132     }
    133 }