Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f7bb22b authored by android-build-team Robot's avatar android-build-team Robot
Browse files

release-request-fda658d8-eecf-4c78-a2e9-e5093ca10451-for-git_oc-mr1-release-41...

release-request-fda658d8-eecf-4c78-a2e9-e5093ca10451-for-git_oc-mr1-release-4165363 snap-temp-L49300000080728237

Change-Id: I8d0d1af5ce5a3ffeab1da0f32dc7836d39005d1b
parents e0f4f580 1394f620
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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"
+134 −0
Original line number Diff line number Diff line
/*
 * 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);
            }
    }
}
+6 −97
Original line number Diff line number Diff line
@@ -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);
            }
+4 −0
Original line number Diff line number Diff line
@@ -73,5 +73,9 @@ public class DetailsView extends TableView implements Consumer<DocumentInfo> {
        if(info.numberOfChildren != -1) {
            setRow(R.string.directory_children, String.valueOf(info.numberOfChildren));
        }

        if (info.summary != null) {
            setRow(R.string.sort_dimension_summary, info.summary);
        }
    }
}
 No newline at end of file
+36 −12
Original line number Diff line number Diff line
@@ -21,9 +21,13 @@ import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;

import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.inspector.InspectorController.Loader;
import com.android.internal.util.Preconditions;
@@ -32,13 +36,14 @@ import java.util.List;
import java.util.function.Consumer;

/**
 * Asynchronously loads a documents metadata into a DocumentInfo object.
 * Asynchronously loads a document's metadata into a DocumentInfo object.
 */
public class DocumentLoader implements Loader {

    private final Context mContext;
    private final LoaderManager mLoader;
    private List<Integer> loaderIds;
    private Callbacks mCallbacks;

    public DocumentLoader(Context context, LoaderManager loader) {
        checkArgument(context != null);
@@ -69,7 +74,8 @@ public class DocumentLoader implements Loader {
        int loadId = getNextLoaderId();
        checkArgument(mLoader.getLoader(loadId) == null);
        loaderIds.add(loadId);
        mLoader.restartLoader(loadId, null, new Callbacks(mContext, uri, mLoader, callback));
        mCallbacks = new Callbacks(mContext, uri, callback);
        mLoader.restartLoader(loadId, null, mCallbacks);
    }

    @Override
@@ -78,6 +84,9 @@ public class DocumentLoader implements Loader {
            mLoader.destroyLoader(id);
        }
        loaderIds.clear();
        if (mCallbacks.getObserver() != null) {
            mContext.getContentResolver().unregisterContentObserver(mCallbacks.getObserver());
        }
    }

    /**
@@ -88,19 +97,15 @@ public class DocumentLoader implements Loader {
        private final Context mContext;
        private final Uri mDocUri;
        private final Consumer<DocumentInfo> mDocConsumer;
        private final LoaderManager mManager;

        Callbacks(Context context, Uri uri, LoaderManager manager,
                Consumer<DocumentInfo> docConsumer) {
        private ContentObserver mObserver;

        Callbacks(Context context, Uri uri, Consumer<DocumentInfo> docConsumer) {
            checkArgument(context != null);
            checkArgument(uri != null);
            checkArgument(manager != null);
            checkArgument(docConsumer != null);
            mContext = context;
            mDocUri = uri;
            mDocConsumer = docConsumer;
            mManager = manager;
        }

        @Override
@@ -114,18 +119,37 @@ public class DocumentLoader implements Loader {
            //returns DocumentInfo null if the cursor is null or isEmpty.
            if (cursor == null || !cursor.moveToFirst()) {
                mDocConsumer.accept(null);
            }
            else {
            } else {
                mObserver = new InspectorContentObserver(loader::onContentChanged);
                cursor.registerContentObserver(mObserver);
                DocumentInfo docInfo = DocumentInfo.fromCursor(cursor, mDocUri.getAuthority());
                mDocConsumer.accept(docInfo);
            }

            mManager.destroyLoader(loader.getId());
        }

        @Override
        public void onLoaderReset(android.content.Loader<Cursor> loader) {
            if (mObserver != null) {
                mContext.getContentResolver().unregisterContentObserver(mObserver);
            }
        }

        public ContentObserver getObserver() {
            return mObserver;
        }
    }

    private static final class InspectorContentObserver extends ContentObserver {
        private final Runnable mContentChangedCallback;

        public InspectorContentObserver(Runnable contentChangedCallback) {
            super(new Handler(Looper.getMainLooper()));
            mContentChangedCallback = contentChangedCallback;
        }

        @Override
        public void onChange(boolean selfChange) {
            mContentChangedCallback.run();
        }
    }
}
 No newline at end of file
Loading