chromium/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/glue/ThumbnailRequestGlue.java

// Copyright 2018 The Chromium Authors
// 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.home.glue;

import android.graphics.Bitmap;

import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.chrome.browser.thumbnail.generator.ThumbnailProvider;
import org.chromium.chrome.browser.thumbnail.generator.ThumbnailProvider.ThumbnailRequest;
import org.chromium.chrome.browser.thumbnail.generator.ThumbnailProviderImpl;
import org.chromium.components.offline_items_collection.OfflineContentProvider;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItemVisuals;
import org.chromium.components.offline_items_collection.VisualsCallback;
import org.chromium.ui.display.DisplayAndroid;

/**
 * Glue class responsible for connecting the current downloads and {@link OfflineContentProvider}
 * thumbnail work to the {@link ThumbnailProvider} via a custom {@link ThumbnailProviderImpl}.
 */
public class ThumbnailRequestGlue implements ThumbnailRequest {
    private final OfflineContentProvider mProvider;
    private final OfflineItem mItem;
    private final int mIconWidthPx;
    private final int mIconHeightPx;
    private final float mMaxThumbnailScaleFactor;
    private final VisualsCallback mCallback;

    /** Creates a {@link ThumbnailRequestGlue} instance. */
    public ThumbnailRequestGlue(
            OfflineContentProvider provider,
            OfflineItem item,
            int iconWidthPx,
            int iconHeightPx,
            float maxThumbnailScaleFactor,
            VisualsCallback callback) {
        mProvider = provider;
        mItem = item;

        mIconWidthPx = iconWidthPx;
        mIconHeightPx = iconHeightPx;
        mMaxThumbnailScaleFactor = maxThumbnailScaleFactor;

        mCallback = callback;
    }

    // ThumbnailRequest implementation.
    @Override
    public String getFilePath() {
        return mItem.filePath;
    }

    @Override
    public String getMimeType() {
        return mItem.mimeType;
    }

    @Override
    public String getContentId() {
        return mItem.id.id;
    }

    @Override
    public void onThumbnailRetrieved(String contentId, Bitmap thumbnail) {
        OfflineItemVisuals visuals = null;
        if (thumbnail != null) {
            visuals = new OfflineItemVisuals();
            visuals.icon = thumbnail;
        }

        mCallback.onVisualsAvailable(mItem.id, visuals);
    }

    @Override
    public int getIconSize() {
        return mIconWidthPx;
    }

    @Override
    public boolean getThumbnail(Callback<Bitmap> callback) {
        mProvider.getVisualsForItem(
                mItem.id,
                (id, visuals) -> {
                    if (visuals == null || visuals.icon == null) {
                        callback.onResult(null);
                    } else {
                        Bitmap bitmap = visuals.icon;
                        int newWidth = bitmap.getWidth();
                        int newHeight = bitmap.getHeight();

                        // Downscale to save memory if the bitmap is not smaller than the icon view.
                        if (newWidth > mIconWidthPx && newHeight > mIconHeightPx) {
                            newWidth = downscaleThumbnailSize(bitmap.getWidth());
                            newHeight = downscaleThumbnailSize(bitmap.getHeight());
                        }

                        // Fit the bitmap into the icon view. Note that we have to use width here
                        // because the ThumbnailProviderImpl only keys off of width as well.
                        int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());
                        if (minDimension > mIconWidthPx) {
                            newWidth =
                                    (int)
                                            (((long) bitmap.getWidth())
                                                    * mIconWidthPx
                                                    / minDimension);
                            newHeight =
                                    (int)
                                            (((long) bitmap.getHeight())
                                                    * mIconWidthPx
                                                    / minDimension);
                        }

                        if (bitmap.getWidth() != newWidth || bitmap.getHeight() != newHeight) {
                            bitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
                        }

                        callback.onResult(bitmap);
                    }
                });
        return true;
    }

    /**
     * Returns size in pixel used by the thumbnail request, considering dip scale factor.
     * @param currentSize The current size before considering the dip scale factor.
     */
    private int downscaleThumbnailSize(int currentSize) {
        DisplayAndroid display =
                DisplayAndroid.getNonMultiDisplay(ContextUtils.getApplicationContext());
        float scale = display.getDipScale();
        if (scale <= mMaxThumbnailScaleFactor) return currentSize;
        return (int) (mMaxThumbnailScaleFactor * currentSize / scale);
    }
}