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.base.SharedMinimal.VERBOSE; 20 import static com.android.documentsui.base.State.MODE_GRID; 21 import static com.android.documentsui.base.State.MODE_LIST; 22 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.provider.DocumentsContract; 29 import android.provider.DocumentsContract.Document; 30 import android.util.Log; 31 import android.view.View; 32 import android.widget.ImageView; 33 34 import androidx.annotation.Nullable; 35 36 import com.android.documentsui.DocumentsApplication; 37 import com.android.documentsui.IconUtils; 38 import com.android.documentsui.ProviderExecutor; 39 import com.android.documentsui.R; 40 import com.android.documentsui.ThumbnailCache; 41 import com.android.documentsui.ThumbnailCache.Result; 42 import com.android.documentsui.ThumbnailLoader; 43 import com.android.documentsui.base.DocumentInfo; 44 import com.android.documentsui.base.MimeTypes; 45 import com.android.documentsui.base.State; 46 import com.android.documentsui.base.State.ViewMode; 47 48 import java.util.function.BiConsumer; 49 50 /** 51 * A class to assist with loading and managing the Images (i.e. thumbnails and icons) associated 52 * with items in the directory listing. 53 */ 54 public class IconHelper { 55 private static final String TAG = "IconHelper"; 56 57 private final Context mContext; 58 private final ThumbnailCache mThumbnailCache; 59 60 // The display mode (MODE_GRID, MODE_LIST, etc). 61 private int mMode; 62 private Point mCurrentSize; 63 private boolean mThumbnailsEnabled = true; 64 65 /** 66 * @param context 67 * @param mode MODE_GRID or MODE_LIST 68 */ IconHelper(Context context, int mode)69 public IconHelper(Context context, int mode) { 70 mContext = context; 71 setViewMode(mode); 72 mThumbnailCache = DocumentsApplication.getThumbnailCache(context); 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 */ setThumbnailsEnabled(boolean enabled)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 * 88 * @param mode See {@link State.MODE_LIST} and {@link State.MODE_GRID}. 89 */ setViewMode(@iewMode int mode)90 public void setViewMode(@ViewMode int mode) { 91 mMode = mode; 92 int thumbSize = getThumbSize(mode); 93 mCurrentSize = new Point(thumbSize, thumbSize); 94 } 95 getThumbSize(int mode)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 * 115 * @param icon 116 */ stopLoading(ImageView icon)117 public void stopLoading(ImageView icon) { 118 final ThumbnailLoader oldTask = (ThumbnailLoader) icon.getTag(); 119 if (oldTask != null) { 120 oldTask.preempt(); 121 icon.setTag(null); 122 } 123 } 124 125 /** 126 * Load thumbnails for a directory list item. 127 * 128 * @param doc The document 129 * @param iconThumb The itemview's thumbnail icon. 130 * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown. 131 * @param subIconMime The second itemview's mime icon. Always visible. 132 * @return 133 */ load( DocumentInfo doc, ImageView iconThumb, ImageView iconMime, @Nullable ImageView subIconMime)134 public void load( 135 DocumentInfo doc, 136 ImageView iconThumb, 137 ImageView iconMime, 138 @Nullable ImageView subIconMime) { 139 load(doc.derivedUri, doc.mimeType, doc.flags, doc.icon, doc.lastModified, 140 iconThumb, iconMime, subIconMime); 141 } 142 143 /** 144 * Load thumbnails for a directory list item. 145 * 146 * @param uri The URI for the file being represented. 147 * @param mimeType The mime type of the file being represented. 148 * @param docFlags Flags for the file being represented. 149 * @param docIcon Custom icon (if any) for the file being requested. 150 * @param docLastModified the last modified value of the file being requested. 151 * @param iconThumb The itemview's thumbnail icon. 152 * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown. 153 * @param subIconMime The second itemview's mime icon. Always visible. 154 * @return 155 */ load(Uri uri, String mimeType, int docFlags, int docIcon, long docLastModified, ImageView iconThumb, ImageView iconMime, @Nullable ImageView subIconMime)156 public void load(Uri uri, String mimeType, int docFlags, int docIcon, long docLastModified, 157 ImageView iconThumb, ImageView iconMime, @Nullable ImageView subIconMime) { 158 boolean loadedThumbnail = false; 159 160 final String docAuthority = uri.getAuthority(); 161 162 final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0; 163 final boolean allowThumbnail = (mMode == MODE_GRID) 164 || MimeTypes.mimeMatches(MimeTypes.VISUAL_MIMES, mimeType); 165 final boolean showThumbnail = supportsThumbnail && allowThumbnail && mThumbnailsEnabled; 166 if (showThumbnail) { 167 loadedThumbnail = 168 loadThumbnail(uri, docAuthority, docLastModified, iconThumb, iconMime); 169 } 170 171 final Drawable mimeIcon = getDocumentIcon(mContext, docAuthority, 172 DocumentsContract.getDocumentId(uri), mimeType, docIcon); 173 if (subIconMime != null) { 174 setMimeIcon(subIconMime, mimeIcon); 175 } 176 177 if (loadedThumbnail) { 178 hideImageView(iconMime); 179 } else { 180 // Add a mime icon if the thumbnail is not shown. 181 setMimeIcon(iconMime, mimeIcon); 182 hideImageView(iconThumb); 183 } 184 } 185 loadThumbnail(Uri uri, String docAuthority, long docLastModified, ImageView iconThumb, ImageView iconMime)186 private boolean loadThumbnail(Uri uri, String docAuthority, long docLastModified, 187 ImageView iconThumb, ImageView iconMime) { 188 final Result result = mThumbnailCache.getThumbnail(uri, mCurrentSize); 189 190 try { 191 final Bitmap cachedThumbnail = result.getThumbnail(); 192 iconThumb.setImageBitmap(cachedThumbnail); 193 194 boolean stale = (docLastModified > result.getLastModified()); 195 if (VERBOSE) Log.v(TAG, 196 String.format("Load thumbnail for %s, got result %d and stale %b.", 197 uri.toString(), result.getStatus(), stale)); 198 if (!result.isExactHit() || stale) { 199 final BiConsumer<View, View> animator = 200 (cachedThumbnail == null ? ThumbnailLoader.ANIM_FADE_IN : 201 ThumbnailLoader.ANIM_NO_OP); 202 203 final ThumbnailLoader task = new ThumbnailLoader(uri, iconThumb, 204 mCurrentSize, docLastModified, 205 bitmap -> { 206 if (bitmap != null) { 207 iconThumb.setImageBitmap(bitmap); 208 animator.accept(iconMime, iconThumb); 209 } 210 }, true /* addToCache */); 211 212 ProviderExecutor.forAuthority(docAuthority).execute(task); 213 } 214 215 return result.isHit(); 216 } finally { 217 result.recycle(); 218 } 219 } 220 setMimeIcon(ImageView view, Drawable icon)221 private void setMimeIcon(ImageView view, Drawable icon) { 222 view.setImageDrawable(icon); 223 view.setAlpha(1f); 224 } 225 hideImageView(ImageView view)226 private void hideImageView(ImageView view) { 227 view.setImageDrawable(null); 228 view.setAlpha(0f); 229 } 230 getDocumentIcon( Context context, String authority, String id, String mimeType, int icon)231 private Drawable getDocumentIcon( 232 Context context, String authority, String id, String mimeType, int icon) { 233 if (icon != 0) { 234 return IconUtils.loadPackageIcon(context, authority, icon); 235 } else { 236 return IconUtils.loadMimeIcon(context, mimeType, authority, id, mMode); 237 } 238 } 239 240 /** 241 * Returns a mime icon or package icon for a {@link DocumentInfo}. 242 */ getDocumentIcon(Context context, DocumentInfo doc)243 public Drawable getDocumentIcon(Context context, DocumentInfo doc) { 244 return getDocumentIcon( 245 context, doc.authority, doc.documentId, doc.mimeType, doc.icon); 246 } 247 } 248