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

Commit 7bc013b6 authored by Jared Duke's avatar Jared Duke Committed by Android (Google) Code Review
Browse files

Merge changes from topic "revert-17083643-large-icons-tm-XHQQYXZDHH" into tm-dev

* changes:
  Revert "Support downscaling of Drawable icons in LocalImageResolver"
  Revert "Downscale large bitmaps in CachingIconView"
  Revert "Size restrict right notification icon size"
parents 4bb44e08 b68458f6
Loading
Loading
Loading
Loading
+7 −116
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
@@ -36,9 +35,6 @@ import android.view.RemotableViewMethod;
import android.widget.ImageView;
import android.widget.RemoteViews;

import com.android.internal.R;

import java.io.IOException;
import java.util.Objects;
import java.util.function.Consumer;

@@ -59,42 +55,9 @@ public class CachingIconView extends ImageView {
    private int mBackgroundColor;
    private boolean mWillBeForceHidden;

    private int mMaxDrawableWidth = -1;
    private int mMaxDrawableHeight = -1;

    public CachingIconView(Context context) {
        this(context, null, 0, 0);
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public CachingIconView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0, 0);
    }

    public CachingIconView(Context context, @Nullable AttributeSet attrs,
            int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public CachingIconView(Context context, @Nullable AttributeSet attrs,
            int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }

    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        if (attrs == null) {
            return;
        }

        TypedArray ta = context.obtainStyledAttributes(attrs,
                R.styleable.CachingIconView, defStyleAttr, defStyleRes);
        mMaxDrawableWidth = ta.getDimensionPixelSize(R.styleable
                .CachingIconView_maxDrawableWidth, -1);
        mMaxDrawableHeight = ta.getDimensionPixelSize(R.styleable
                .CachingIconView_maxDrawableHeight, -1);
        ta.recycle();
        super(context, attrs);
    }

    @Override
@@ -103,31 +66,15 @@ public class CachingIconView extends ImageView {
        if (!testAndSetCache(icon)) {
            mInternalSetDrawable = true;
            // This calls back to setImageDrawable, make sure we don't clear the cache there.
            Drawable drawable = loadSizeRestrictedIcon(icon);
            if (drawable == null) {
            super.setImageIcon(icon);
            } else {
                super.setImageDrawable(drawable);
            }
            mInternalSetDrawable = false;
        }
    }

    @Nullable
    private Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
        try {
            return LocalImageResolver.resolveImage(icon, getContext(), mMaxDrawableWidth,
                    mMaxDrawableHeight);
        } catch (IOException e) {
            return null;
        }
    }

    @Override
    public Runnable setImageIconAsync(@Nullable final Icon icon) {
    public Runnable setImageIconAsync(@Nullable Icon icon) {
        resetCache();
        Drawable drawable = loadSizeRestrictedIcon(icon);
        return () -> setImageDrawable(drawable);
        return super.setImageIconAsync(icon);
    }

    @Override
@@ -136,34 +83,14 @@ public class CachingIconView extends ImageView {
        if (!testAndSetCache(resId)) {
            mInternalSetDrawable = true;
            // This calls back to setImageDrawable, make sure we don't clear the cache there.
            Drawable drawable = loadSizeRestrictedDrawable(resId);
            if (drawable == null) {
            super.setImageResource(resId);
            } else {
                super.setImageDrawable(drawable);
            }
            mInternalSetDrawable = false;
        }
    }

    @Nullable
    private Drawable loadSizeRestrictedDrawable(@DrawableRes int resId) {
        try {
            return LocalImageResolver.resolveImage(resId, getContext(), mMaxDrawableWidth,
                    mMaxDrawableHeight);
        } catch (IOException e) {
            return null;
        }
    }

    @Override
    public Runnable setImageResourceAsync(@DrawableRes int resId) {
        resetCache();
        Drawable drawable = loadSizeRestrictedDrawable(resId);
        if (drawable != null) {
            return () -> setImageDrawable(drawable);
        }

        return super.setImageResourceAsync(resId);
    }

@@ -171,35 +98,13 @@ public class CachingIconView extends ImageView {
    @RemotableViewMethod(asyncImpl="setImageURIAsync")
    public void setImageURI(@Nullable Uri uri) {
        resetCache();
        Drawable drawable = loadSizeRestrictedUri(uri);
        if (drawable == null) {
        super.setImageURI(uri);
        } else {
            mInternalSetDrawable = true;
            super.setImageDrawable(drawable);
            mInternalSetDrawable = false;
        }
    }

    @Nullable
    private Drawable loadSizeRestrictedUri(@Nullable Uri uri) {
        try {
            return LocalImageResolver.resolveImage(uri, getContext(), mMaxDrawableWidth,
                    mMaxDrawableHeight);
        } catch (IOException e) {
            return null;
        }
    }

    @Override
    public Runnable setImageURIAsync(@Nullable Uri uri) {
        resetCache();
        Drawable drawable = loadSizeRestrictedUri(uri);
        if (drawable == null) {
        return super.setImageURIAsync(uri);
        } else {
            return () -> setImageDrawable(drawable);
        }
    }

    @Override
@@ -402,18 +307,4 @@ public class CachingIconView extends ImageView {
    public void setWillBeForceHidden(boolean forceHidden) {
        mWillBeForceHidden = forceHidden;
    }

    /**
     * Returns the set maximum width of drawable in pixels. -1 if not set.
     */
    public int getMaxDrawableWidth() {
        return mMaxDrawableWidth;
    }

    /**
     * Returns the set maximum height of drawable in pixels. -1 if not set.
     */
    public int getMaxDrawableHeight() {
        return mMaxDrawableHeight;
    }
}
+18 −119
Original line number Diff line number Diff line
@@ -16,25 +16,21 @@

package com.android.internal.widget;

import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ImageDecoder;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.util.Size;

import com.android.internal.annotations.VisibleForTesting;

import java.io.IOException;

/** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */
public class LocalImageResolver {
    private static final String TAG = LocalImageResolver.class.getSimpleName();

    @VisibleForTesting
    static final int DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480;
    private static final int MAX_SAFE_ICON_SIZE_PX = 480;

    /**
     * Resolve an image from the given Uri using {@link ImageDecoder}
@@ -42,20 +38,9 @@ public class LocalImageResolver {
    public static Drawable resolveImage(Uri uri, Context context) throws IOException {
        final ImageDecoder.Source source =
                ImageDecoder.createSource(context.getContentResolver(), uri);
        return ImageDecoder.decodeDrawable(source,
                (decoder, info, s) -> LocalImageResolver.onHeaderDecoded(decoder, info,
                        DEFAULT_MAX_SAFE_ICON_SIZE_PX, DEFAULT_MAX_SAFE_ICON_SIZE_PX));
    }

    /**
     * Get the drawable from Icon using {@link ImageDecoder} if it contains a Uri, or
     * using {@link Icon#loadDrawable(Context)} otherwise.  This will correctly apply the Icon's,
     * tint, if present, to the drawable.
     */
    public static Drawable resolveImage(@Nullable Icon icon, Context context)
            throws IOException {
        return resolveImage(icon, context, DEFAULT_MAX_SAFE_ICON_SIZE_PX,
                DEFAULT_MAX_SAFE_ICON_SIZE_PX);
        final Drawable drawable =
                ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded);
        return drawable;
    }

    /**
@@ -63,38 +48,17 @@ public class LocalImageResolver {
     * using {@link Icon#loadDrawable(Context)} otherwise.  This will correctly apply the Icon's,
     * tint, if present, to the drawable.
     */
    @Nullable
    public static Drawable resolveImage(@Nullable Icon icon, Context context, int maxWidth,
            int maxHeight)
            throws IOException {
        if (icon == null) {
            return null;
        }

        switch (icon.getType()) {
            case Icon.TYPE_URI:
            case Icon.TYPE_URI_ADAPTIVE_BITMAP:
    public static Drawable resolveImage(Icon icon, Context context) throws IOException {
        Uri uri = getResolvableUri(icon);
        if (uri != null) {
                    Drawable result = resolveImage(uri, context, maxWidth, maxHeight);
                    return tintDrawable(icon, result);
                }
                break;
            case Icon.TYPE_RESOURCE:
                Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight);
                if (result != null) {
                    return tintDrawable(icon, result);
            Drawable result = resolveImage(uri, context);
            if (icon.hasTint()) {
                result.mutate();
                result.setTintList(icon.getTintList());
                result.setTintBlendMode(icon.getTintBlendMode());
            }
                break;
            case Icon.TYPE_BITMAP:
            case Icon.TYPE_ADAPTIVE_BITMAP:
                return resolveBitmapImage(icon, context, maxWidth, maxHeight);
            case Icon.TYPE_DATA:    // We can't really improve on raw data images.
            default:
                break;
            return result;
        }

        // Fallback to straight drawable load if we fail with more efficient approach.
        return icon.loadDrawable(context);
    }

@@ -102,71 +66,7 @@ public class LocalImageResolver {
            throws IOException {
        final ImageDecoder.Source source =
                ImageDecoder.createSource(context.getContentResolver(), uri);
        return resolveImage(source, maxWidth, maxHeight);
    }

    /**
     * Attempts to resolve the resource as a bitmap drawable constrained within max sizes.
     *
     * @return decoded drawable or null if the passed resource is not a straight bitmap
     */
    @Nullable
    public static Drawable resolveImage(@DrawableRes int resId, Context context, int maxWidth,
            int maxHeight)
            throws IOException {
        final ImageDecoder.Source source = ImageDecoder.createSource(context.getResources(), resId);
        // It's possible that the resource isn't an actual bitmap drawable so this decode can fail.
        // Return null in that case.
        try {
            return resolveImage(source, maxWidth, maxHeight);
        } catch (ImageDecoder.DecodeException e) {
            return null;
        }
    }

    @Nullable
    private static Drawable resolveBitmapImage(Icon icon, Context context, int maxWidth,
            int maxHeight) {
        Bitmap bitmap = icon.getBitmap();
        if (bitmap == null) {
            return null;
        }

        if (bitmap.getWidth() > maxWidth || bitmap.getHeight() > maxHeight) {
            Icon smallerIcon = icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP
                    ? Icon.createWithAdaptiveBitmap(bitmap) : Icon.createWithBitmap(bitmap);
            // We don't want to modify the source icon, create a copy.
            smallerIcon.setTintList(icon.getTintList())
                    .setTintBlendMode(icon.getTintBlendMode())
                    .scaleDownIfNecessary(maxWidth, maxHeight);
            return smallerIcon.loadDrawable(context);
        }

        return icon.loadDrawable(context);
    }

    @Nullable
    private static Drawable tintDrawable(Icon icon, @Nullable Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (icon.hasTint()) {
            drawable.mutate();
            drawable.setTintList(icon.getTintList());
            drawable.setTintBlendMode(icon.getTintBlendMode());
        }

        return drawable;
    }

    private static Drawable resolveImage(ImageDecoder.Source source, int maxWidth, int maxHeight)
            throws IOException {
        return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> {
            if (maxWidth <= 0 || maxHeight <= 0) {
                return;
            }

            final Size size = info.getSize();
            if (size.getWidth() > size.getHeight()) {
                if (size.getWidth() > maxWidth) {
@@ -188,12 +88,11 @@ public class LocalImageResolver {
    }

    private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
            int maxWidth, int maxHeight) {
            ImageDecoder.Source source) {
        final Size size = info.getSize();
        final int originalSize = Math.max(size.getHeight(), size.getWidth());
        final int maxSize = Math.max(maxWidth, maxHeight);
        final double ratio = (originalSize > maxSize)
                ? originalSize * 1f / maxSize
        final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
                ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX
                : 1.0;
        decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
    }
+0 −2
Original line number Diff line number Diff line
@@ -49,8 +49,6 @@
        android:layout_marginStart="@dimen/notification_icon_circle_start"
        android:background="@drawable/notification_icon_circle"
        android:padding="@dimen/notification_icon_circle_padding"
        android:maxDrawableWidth="@dimen/notification_icon_circle_size"
        android:maxDrawableHeight="@dimen/notification_icon_circle_size"
        />

    <!-- extends ViewGroup -->
+1 −5
Original line number Diff line number Diff line
@@ -45,8 +45,6 @@
        android:layout_marginStart="@dimen/notification_icon_circle_start"
        android:background="@drawable/notification_icon_circle"
        android:padding="@dimen/notification_icon_circle_padding"
        android:maxDrawableWidth="@dimen/notification_icon_circle_size"
        android:maxDrawableHeight="@dimen/notification_icon_circle_size"
        />

    <FrameLayout
@@ -138,7 +136,7 @@

        </LinearLayout>

        <com.android.internal.widget.CachingIconView
        <ImageView
            android:id="@+id/right_icon"
            android:layout_width="@dimen/notification_right_icon_size"
            android:layout_height="@dimen/notification_right_icon_size"
@@ -150,8 +148,6 @@
            android:clipToOutline="true"
            android:importantForAccessibility="no"
            android:scaleType="centerCrop"
            android:maxDrawableWidth="@dimen/notification_right_icon_size"
            android:maxDrawableHeight="@dimen/notification_right_icon_size"
            />

        <FrameLayout
+1 −3
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License
  -->
<com.android.internal.widget.CachingIconView
<ImageView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/right_icon"
    android:layout_width="@dimen/notification_right_icon_size"
@@ -25,6 +25,4 @@
    android:clipToOutline="true"
    android:importantForAccessibility="no"
    android:scaleType="centerCrop"
    android:maxDrawableWidth="@dimen/notification_right_icon_size"
    android:maxDrawableHeight="@dimen/notification_right_icon_size"
    />
Loading