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