diff options
author | 2017-07-05 14:31:37 -0700 | |
---|---|---|
committer | 2017-07-07 17:21:25 -0700 | |
commit | 4143e37643f34775a43650a650e6a032239e1ad2 (patch) | |
tree | 0b4059b06b80dce3d40f66b1e949d7142c6c5e21 | |
parent | 62a4aed395b8cb726df1a89d7d699272ca7bf45f (diff) |
Add support to display thumbnail or mime icon in the document inspector.
Displays the thumbnail or mime icon in the header.
Bug: 62862758
Test: --
Change-Id: I8cc5807a002cba19f544fc404181e8c212384381
-rw-r--r-- | res/layout/inspector_header.xml | 11 | ||||
-rw-r--r-- | src/com/android/documentsui/ThumbnailLoader.java | 134 | ||||
-rw-r--r-- | src/com/android/documentsui/dirlist/IconHelper.java | 103 | ||||
-rw-r--r-- | src/com/android/documentsui/inspector/HeaderView.java | 43 |
4 files changed, 193 insertions, 98 deletions
diff --git a/res/layout/inspector_header.xml b/res/layout/inspector_header.xml index cfbc29f98..bb6acb4da 100644 --- a/res/layout/inspector_header.xml +++ b/res/layout/inspector_header.xml @@ -17,11 +17,22 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> + + <ImageView + android:id="@+id/inspector_mime" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="fitCenter" + android:background="@android:color/black" /> + <ImageView android:id="@+id/inspector_thumbnail" android:layout_width="match_parent" android:layout_height="match_parent" + android:alpha="0.0" + android:scaleType="centerCrop" android:background="@android:color/black" /> + <TextView android:id="@+id/inspector_file_title" android:layout_width="wrap_content" diff --git a/src/com/android/documentsui/ThumbnailLoader.java b/src/com/android/documentsui/ThumbnailLoader.java new file mode 100644 index 000000000..bb1e5d5a4 --- /dev/null +++ b/src/com/android/documentsui/ThumbnailLoader.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 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; + +import static com.android.documentsui.base.Shared.VERBOSE; + +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.CancellationSignal; +import android.os.OperationCanceledException; +import android.provider.DocumentsContract; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import com.android.documentsui.ProviderExecutor.Preemptable; +import java.util.function.BiConsumer; + +/** + * Loads a Thumbnails asynchronously then animates from the mime icon to the thumbnail + */ +public final class ThumbnailLoader extends AsyncTask<Uri, Void, Bitmap> implements Preemptable { + + private static final String TAG = ThumbnailLoader.class.getCanonicalName(); + + /** + * Two animations applied to image views. The first is used to switch mime icon and thumbnail. + * The second is used when we need to update thumbnail. + */ + public static final BiConsumer<View, View> ANIM_FADE_IN = (mime, thumb) -> { + float alpha = mime.getAlpha(); + mime.animate().alpha(0f).start(); + thumb.setAlpha(0f); + thumb.animate().alpha(alpha).start(); + }; + public static final BiConsumer<View, View> ANIM_NO_OP = (mime, thumb) -> {}; + + private final ImageView mIconThumb; + private final Point mThumbSize; + // A callback to apply animation to image views after the thumbnail is loaded. + private final BiConsumer<View, View> mImageAnimator; + private final Uri mUri; + private final ImageView mIconMime; + private final long mLastModified; + private final boolean mAddToCache; + private final CancellationSignal mSignal; + + /** + * @param uri - to a thumbnail. + * @param iconMime - ImageView for displaying a mime type. + * @param iconThumb - ImageView to display the thumbnail. + * @param thumbSize - size of the thumbnail. + * @param lastModified - used for updating thumbnail caches. + * @param animator - used to animate from the mime icon to the thumbnail. + * @param addToCache - flag that determines if the loader saves the thumbnail to the cache. + */ + public ThumbnailLoader(Uri uri, ImageView iconMime, ImageView iconThumb, + Point thumbSize, long lastModified, BiConsumer<View, View> animator, boolean addToCache) { + mUri = uri; + mIconMime = iconMime; + mIconThumb = iconThumb; + mThumbSize = thumbSize; + mImageAnimator = animator; + mLastModified = lastModified; + mAddToCache = addToCache; + mSignal = new CancellationSignal(); + mIconThumb.setTag(this); + if (VERBOSE) Log.v(TAG, "Starting icon loader task for " + mUri); + } + + @Override + public void preempt() { + if (VERBOSE) Log.v(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 && mAddToCache) { + final ThumbnailCache cache = DocumentsApplication.getThumbnailCache(context); + cache.putThumbnail(mUri, mThumbSize, result, mLastModified); + } + } 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 (VERBOSE) Log.v(TAG, "Loader task for " + mUri + " completed"); + + if (mIconThumb.getTag() == this && result != null) { + mIconThumb.setTag(null); + mIconThumb.setImageBitmap(result); + mImageAnimator.accept(mIconMime, mIconThumb); + } + } +} diff --git a/src/com/android/documentsui/dirlist/IconHelper.java b/src/com/android/documentsui/dirlist/IconHelper.java index 77a646695..d9eabae25 100644 --- a/src/com/android/documentsui/dirlist/IconHelper.java +++ b/src/com/android/documentsui/dirlist/IconHelper.java @@ -20,16 +20,11 @@ import static com.android.documentsui.base.Shared.VERBOSE; import static com.android.documentsui.base.State.MODE_GRID; import static com.android.documentsui.base.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; @@ -40,10 +35,10 @@ import android.widget.ImageView; import com.android.documentsui.DocumentsApplication; import com.android.documentsui.IconUtils; import com.android.documentsui.ProviderExecutor; -import com.android.documentsui.ProviderExecutor.Preemptable; import com.android.documentsui.R; import com.android.documentsui.ThumbnailCache; import com.android.documentsui.ThumbnailCache.Result; +import com.android.documentsui.ThumbnailLoader; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.MimeTypes; import com.android.documentsui.base.State; @@ -58,16 +53,6 @@ import java.util.function.BiConsumer; public class IconHelper { private static final String TAG = "IconHelper"; - // Two animations applied to image views. The first is used to switch mime icon and thumbnail. - // The second is used when we need to update thumbnail. - private static final BiConsumer<View, View> ANIM_FADE_IN = (mime, thumb) -> { - float alpha = mime.getAlpha(); - mime.animate().alpha(0f).start(); - thumb.setAlpha(0f); - thumb.animate().alpha(alpha).start(); - }; - private static final BiConsumer<View, View> ANIM_NO_OP = (mime, thumb) -> {}; - private final Context mContext; private final ThumbnailCache mThumbnailCache; @@ -129,89 +114,13 @@ public class IconHelper { * @param icon */ public void stopLoading(ImageView icon) { - final LoaderTask oldTask = (LoaderTask) icon.getTag(); + final ThumbnailLoader oldTask = (ThumbnailLoader) 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 long mLastModified; - - // A callback to apply animation to image views after the thumbnail is loaded. - private final BiConsumer<View, View> mImageAnimator; - - private final CancellationSignal mSignal; - - public LoaderTask(Uri uri, ImageView iconMime, ImageView iconThumb, - Point thumbSize, long lastModified, BiConsumer<View, View> animator) { - mUri = uri; - mIconMime = iconMime; - mIconThumb = iconThumb; - mThumbSize = thumbSize; - mImageAnimator = animator; - mLastModified = lastModified; - mSignal = new CancellationSignal(); - if (VERBOSE) Log.v(TAG, "Starting icon loader task for " + mUri); - } - - @Override - public void preempt() { - if (VERBOSE) Log.v(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 cache = DocumentsApplication.getThumbnailCache(context); - cache.putThumbnail(mUri, mThumbSize, result, mLastModified); - } - } 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 (VERBOSE) Log.v(TAG, "Loader task for " + mUri + " completed"); - - if (mIconThumb.getTag() == this && result != null) { - mIconThumb.setTag(null); - mIconThumb.setImageBitmap(result); - - mImageAnimator.accept(mIconMime, mIconThumb); - } - } - } - /** * Load thumbnails for a directory list item. * @@ -287,11 +196,11 @@ public class IconHelper { uri.toString(), result.getStatus(), stale)); if (!result.isExactHit() || stale) { final BiConsumer<View, View> animator = - (cachedThumbnail == null ? ANIM_FADE_IN : ANIM_NO_OP); - final LoaderTask task = new LoaderTask(uri, iconMime, iconThumb, mCurrentSize, - docLastModified, animator); + (cachedThumbnail == null ? ThumbnailLoader.ANIM_FADE_IN : + ThumbnailLoader.ANIM_NO_OP); - iconThumb.setTag(task); + final ThumbnailLoader task = new ThumbnailLoader(uri, iconMime, iconThumb, + mCurrentSize, docLastModified, animator, true); ProviderExecutor.forAuthority(docAuthority).execute(task); } diff --git a/src/com/android/documentsui/inspector/HeaderView.java b/src/com/android/documentsui/inspector/HeaderView.java index a9f36f285..c9c6b3ec3 100644 --- a/src/com/android/documentsui/inspector/HeaderView.java +++ b/src/com/android/documentsui/inspector/HeaderView.java @@ -15,13 +15,20 @@ */ package com.android.documentsui.inspector; +import android.app.Activity; import android.content.Context; +import android.graphics.Point; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import com.android.documentsui.ProviderExecutor; +import com.android.documentsui.ThumbnailLoader; +import com.android.documentsui.base.Display; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.R; import java.util.function.Consumer; @@ -31,8 +38,14 @@ import java.util.function.Consumer; */ public final class HeaderView extends RelativeLayout implements Consumer<DocumentInfo> { + private static final String TAG = HeaderView.class.getCanonicalName(); + + private final Context mContext; private final View mHeader; + private ImageView mMime; + private ImageView mThumbnail; private final TextView mTitle; + private Point mImageDimensions; public HeaderView(Context context) { this(context, null); @@ -46,8 +59,15 @@ public final class HeaderView extends RelativeLayout implements Consumer<Documen super(context, attrs, defStyleAttr); LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE); + mContext = context; mHeader = inflater.inflate(R.layout.inspector_header, null); + mMime = (ImageView) mHeader.findViewById(R.id.inspector_mime); + mThumbnail = (ImageView) mHeader.findViewById(R.id.inspector_thumbnail); mTitle = (TextView) mHeader.findViewById(R.id.inspector_file_title); + + int width = (int) Display.screenWidth((Activity)context); + int height = mContext.getResources().getDimensionPixelSize(R.dimen.inspector_header_height); + mImageDimensions = new Point(width, height); } @Override @@ -55,6 +75,10 @@ public final class HeaderView extends RelativeLayout implements Consumer<Documen if (!hasHeader()) { addView(mHeader); } + + if(!hasHeaderImage()) { + loadHeaderImage(info); + } mTitle.setText(info.displayName); } @@ -66,4 +90,21 @@ public final class HeaderView extends RelativeLayout implements Consumer<Documen } return false; } -} + + private void loadHeaderImage(DocumentInfo info) { + + // load the mime icon. + Drawable d = mContext.getContentResolver().getTypeDrawable(info.mimeType); + mMime.setImageDrawable(d); + + // load the thumbnail async. + final ThumbnailLoader task = new ThumbnailLoader(info.derivedUri, mMime, mThumbnail, + mImageDimensions, info.lastModified, ThumbnailLoader.ANIM_FADE_IN, false); + task.executeOnExecutor(ProviderExecutor.forAuthority(info.derivedUri.getAuthority()), + info.derivedUri); + } + + private boolean hasHeaderImage() { + return mThumbnail.getAlpha() == 1.0f; + } +}
\ No newline at end of file |