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