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 */ 16package com.android.documentsui; 17 18import static com.android.documentsui.base.Shared.VERBOSE; 19 20import android.annotation.Nullable; 21import android.content.ContentProviderClient; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.graphics.Bitmap; 25import android.graphics.Point; 26import android.graphics.drawable.Drawable; 27import android.net.Uri; 28import android.os.AsyncTask; 29import android.os.CancellationSignal; 30import android.os.OperationCanceledException; 31import android.provider.DocumentsContract; 32import android.util.Log; 33import android.view.View; 34import android.widget.ImageView; 35import com.android.documentsui.ProviderExecutor.Preemptable; 36import java.util.function.BiConsumer; 37import java.util.function.Consumer; 38 39/** 40 * Loads a Thumbnails asynchronously then animates from the mime icon to the thumbnail 41 */ 42public 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}