org.chromium.chrome.browser.download.ui.ThumbnailProviderImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.chromium.chrome.browser.download.ui.ThumbnailProviderImpl.java

Source

// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.download.ui;

import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.support.v4.util.LruCache;
import android.text.TextUtils;

import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.CalledByNative;

import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
import java.util.Deque;

/**
 * Concrete implementation of {@link ThumbnailProvider}.
 *
 * Thumbnails are cached and shared across all ThumbnailProviderImpls.  The cache itself is LRU and
 * limited in size.  It is automatically garbage collected under memory pressure.
 *
 * A queue of requests is maintained in FIFO order.  Missing thumbnails are retrieved asynchronously
 * by the native ThumbnailProvider, which is owned and destroyed by the Java class.
 *
 * TODO(dfalcantara): Figure out how to send requests simultaneously to the utility process without
 *                    duplicating work to decode the same image for two different requests.
 */
public class ThumbnailProviderImpl implements ThumbnailProvider {
    /** 5 MB of thumbnails should be enough for everyone. */
    private static final int MAX_CACHE_BYTES = 5 * 1024 * 1024;

    /** Weakly referenced cache containing thumbnails that can be deleted under memory pressure. */
    private static WeakReference<LruCache<String, Bitmap>> sBitmapCache = new WeakReference<>(null);

    /** Enqueues requests. */
    private final Handler mHandler;

    /** Maximum size in pixels of the smallest side of the thumbnail. */
    private final int mIconSizePx;

    /** Queue of files to retrieve thumbnails for. */
    private final Deque<ThumbnailRequest> mRequestQueue;

    /** The native side pointer that is owned and destroyed by the Java class. */
    private long mNativeThumbnailProvider;

    /** Request that is currently having its thumbnail retrieved. */
    private ThumbnailRequest mCurrentRequest;

    public ThumbnailProviderImpl(int iconSizePx) {
        mIconSizePx = iconSizePx;
        mHandler = new Handler(Looper.getMainLooper());
        mRequestQueue = new ArrayDeque<>();
        mNativeThumbnailProvider = nativeInit();
    }

    @Override
    public void destroy() {
        ThreadUtils.assertOnUiThread();
        nativeDestroy(mNativeThumbnailProvider);
        mNativeThumbnailProvider = 0;
    }

    @Override
    public Bitmap getThumbnail(ThumbnailRequest request) {
        String filePath = request.getFilePath();
        if (TextUtils.isEmpty(filePath))
            return null;

        Bitmap cachedBitmap = getBitmapCache().get(filePath);
        if (cachedBitmap != null)
            return cachedBitmap;

        mRequestQueue.offer(request);
        processQueue();
        return null;
    }

    /** Removes a particular file from the pending queue. */
    @Override
    public void cancelRetrieval(ThumbnailRequest request) {
        if (mRequestQueue.contains(request))
            mRequestQueue.remove(request);
    }

    private void processQueue() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                processNextRequest();
            }
        });
    }

    private void processNextRequest() {
        if (!isInitialized() || mCurrentRequest != null || mRequestQueue.isEmpty())
            return;

        mCurrentRequest = mRequestQueue.poll();
        String currentFilePath = mCurrentRequest.getFilePath();

        Bitmap cachedBitmap = getBitmapCache().get(currentFilePath);
        if (cachedBitmap == null) {
            // Asynchronously process the file to make a thumbnail.
            nativeRetrieveThumbnail(mNativeThumbnailProvider, currentFilePath, mIconSizePx);
        } else {
            // Send back the already-processed file.
            onThumbnailRetrieved(currentFilePath, cachedBitmap);
        }
    }

    @CalledByNative
    private void onThumbnailRetrieved(String filePath, @Nullable Bitmap bitmap) {
        if (bitmap != null) {
            getBitmapCache().put(filePath, bitmap);
            mCurrentRequest.onThumbnailRetrieved(filePath, bitmap);
        }

        mCurrentRequest = null;
        processQueue();
    }

    private boolean isInitialized() {
        return mNativeThumbnailProvider != 0;
    }

    private static LruCache<String, Bitmap> getBitmapCache() {
        ThreadUtils.assertOnUiThread();

        LruCache<String, Bitmap> cache = sBitmapCache == null ? null : sBitmapCache.get();
        if (cache != null)
            return cache;

        // Create a new weakly-referenced cache.
        cache = new LruCache<String, Bitmap>(MAX_CACHE_BYTES) {
            @Override
            protected int sizeOf(String key, Bitmap thumbnail) {
                return thumbnail == null ? 0 : thumbnail.getByteCount();
            }
        };
        sBitmapCache = new WeakReference<>(cache);
        return cache;
    }

    private native long nativeInit();

    private native void nativeDestroy(long nativeThumbnailProvider);

    private native void nativeRetrieveThumbnail(long nativeThumbnailProvider, String filePath, int thumbnailSize);
}