blob: ff0f4b10b060a300b47c176a4d5c9aeabed87f89 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.documentsui.dirlist;
import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.State.MODE_GRID;
import static com.android.documentsui.State.MODE_LIST;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.ImageView;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.IconUtils;
import com.android.documentsui.MimePredicate;
import com.android.documentsui.ProviderExecutor;
import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.R;
import com.android.documentsui.State;
import com.android.documentsui.State.ViewMode;
import com.android.documentsui.ThumbnailCache;
/**
* A class to assist with loading and managing the Images (i.e. thumbnails and icons) associated
* with items in the directory listing.
*/
public class IconHelper {
private static String TAG = "IconHelper";
private final Context mContext;
// Updated when icon size is set.
private ThumbnailCache mCache;
private Point mThumbSize;
// The display mode (MODE_GRID, MODE_LIST, etc).
private int mMode;
private boolean mThumbnailsEnabled = true;
/**
* @param context
* @param mode MODE_GRID or MODE_LIST
*/
public IconHelper(Context context, int mode) {
mContext = context;
setViewMode(mode);
mCache = DocumentsApplication.getThumbnailsCache(context, mThumbSize);
}
/**
* Enables or disables thumbnails. When thumbnails are disabled, mime icons (or custom icons, if
* specified by the document) are used instead.
*
* @param enabled
*/
public void setThumbnailsEnabled(boolean enabled) {
mThumbnailsEnabled = enabled;
}
/**
* Sets the current display mode. This affects the thumbnail sizes that are loaded.
* @param mode See {@link State.MODE_LIST} and {@link State.MODE_GRID}.
*/
public void setViewMode(@ViewMode int mode) {
mMode = mode;
int thumbSize = getThumbSize(mode);
mThumbSize = new Point(thumbSize, thumbSize);
mCache = DocumentsApplication.getThumbnailsCache(mContext, mThumbSize);
}
private int getThumbSize(int mode) {
int thumbSize;
switch (mode) {
case MODE_GRID:
thumbSize = mContext.getResources().getDimensionPixelSize(R.dimen.grid_width);
break;
case MODE_LIST:
thumbSize = mContext.getResources().getDimensionPixelSize(
R.dimen.list_item_thumbnail_size);
break;
default:
throw new IllegalArgumentException("Unsupported layout mode: " + mode);
}
return thumbSize;
}
/**
* Cancels any ongoing load operations associated with the given ImageView.
* @param icon
*/
public void stopLoading(ImageView icon) {
final LoaderTask oldTask = (LoaderTask) icon.getTag();
if (oldTask != null) {
oldTask.preempt();
icon.setTag(null);
}
}
/** Internal task for loading thumbnails asynchronously. */
private static class LoaderTask
extends AsyncTask<Uri, Void, Bitmap>
implements Preemptable {
private final Uri mUri;
private final ImageView mIconMime;
private final ImageView mIconThumb;
private final Point mThumbSize;
private final CancellationSignal mSignal;
public LoaderTask(Uri uri, ImageView iconMime, ImageView iconThumb,
Point thumbSize) {
mUri = uri;
mIconMime = iconMime;
mIconThumb = iconThumb;
mThumbSize = thumbSize;
mSignal = new CancellationSignal();
if (DEBUG) Log.d(TAG, "Starting icon loader task for " + mUri);
}
@Override
public void preempt() {
if (DEBUG) Log.d(TAG, "Icon loader task for " + mUri + " was cancelled.");
cancel(false);
mSignal.cancel();
}
@Override
protected Bitmap doInBackground(Uri... params) {
if (isCancelled())
return null;
final Context context = mIconThumb.getContext();
final ContentResolver resolver = context.getContentResolver();
ContentProviderClient client = null;
Bitmap result = null;
try {
client = DocumentsApplication.acquireUnstableProviderOrThrow(
resolver, mUri.getAuthority());
result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
if (result != null) {
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
context, mThumbSize);
thumbs.put(mUri, result);
}
} catch (Exception e) {
if (!(e instanceof OperationCanceledException)) {
Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
}
} finally {
ContentProviderClient.releaseQuietly(client);
}
return result;
}
@Override
protected void onPostExecute(Bitmap result) {
if (DEBUG) Log.d(TAG, "Loader task for " + mUri + " completed");
if (mIconThumb.getTag() == this && result != null) {
mIconThumb.setTag(null);
mIconThumb.setImageBitmap(result);
float alpha = mIconMime.getAlpha();
mIconMime.animate().alpha(0f).start();
mIconThumb.setAlpha(0f);
mIconThumb.animate().alpha(alpha).start();
}
}
}
/**
* Load thumbnails for a directory list item.
* @param uri The URI for the file being represented.
* @param mimeType The mime type of the file being represented.
* @param docFlags Flags for the file being represented.
* @param docIcon Custom icon (if any) for the file being requested.
* @param iconThumb The itemview's thumbnail icon.
* @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
* @param subIconMime The second itemview's mime icon. Always visible.
* @return
*/
public void loadThumbnail(Uri uri, String mimeType, int docFlags, int docIcon,
ImageView iconThumb, ImageView iconMime, @Nullable ImageView subIconMime) {
boolean cacheHit = false;
final String docAuthority = uri.getAuthority();
final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
final boolean allowThumbnail = (mMode == MODE_GRID)
|| MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mimeType);
final boolean showThumbnail = supportsThumbnail && allowThumbnail && mThumbnailsEnabled;
if (showThumbnail) {
final Bitmap cachedResult = mCache.get(uri);
if (cachedResult != null) {
iconThumb.setImageBitmap(cachedResult);
cacheHit = true;
} else {
iconThumb.setImageDrawable(null);
final LoaderTask task = new LoaderTask(uri, iconMime, iconThumb, mThumbSize);
iconThumb.setTag(task);
ProviderExecutor.forAuthority(docAuthority).execute(task);
}
}
final Drawable icon = getDocumentIcon(mContext, docAuthority,
DocumentsContract.getDocumentId(uri), mimeType, docIcon);
if (subIconMime != null) {
subIconMime.setImageDrawable(icon);
}
if (cacheHit) {
iconMime.setImageDrawable(null);
iconMime.setAlpha(0f);
iconThumb.setAlpha(1f);
} else {
// Add a mime icon if the thumbnail is being loaded in the background.
iconThumb.setImageDrawable(null);
iconMime.setImageDrawable(icon);
iconMime.setAlpha(1f);
iconThumb.setAlpha(0f);
}
}
/**
* Gets a mime icon or package icon for a file.
* @param context
* @param authority The authority string of the file.
* @param id The document ID of the file.
* @param mimeType The mime type of the file.
* @param icon The custom icon (if any) of the file.
* @return
*/
public Drawable getDocumentIcon(Context context, String authority, String id,
String mimeType, int icon) {
if (icon != 0) {
return IconUtils.loadPackageIcon(context, authority, icon);
} else {
return IconUtils.loadMimeIcon(context, mimeType, authority, id, mMode);
}
}
}