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

Commit 3dc7364e authored by wilsonshih's avatar wilsonshih
Browse files

Update density dpi before load drawable.

When load a BitmapDrawable object with specific density, there will
decode the image based on the density from display metrics, so even
when load with higher override density, the final intrinsic size of the
BitmapDrawable can still not big enough to draw on expect size.

In order to load a bigger size BitmapDrawable object from a resources,
there should also update the densityDpi on the display metrics. But
since this kind of use case is relative rare, we use a standalone
IconProvider object to load the Drawable object for higher density, so
this resources object won't affect the entire system.

Also, since this symptom is not noticeable at high density, the
standalone icon provider only used for low density situation, so there
should be no performance loss for most high denstiy device.

Bug: 215673281
Test: atest StartingSurfaceDrawerTests
Test: snapshot to see the loaded bitmap.
Change-Id: Idd769327a8b0987f921c7d421ea1d9bb38a507ba
parent 68e57d45
Loading
Loading
Loading
Loading
+91 −18
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -53,6 +55,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.ContextThemeWrapper;
import android.view.SurfaceControl;
@@ -68,7 +71,6 @@ import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

@@ -102,7 +104,7 @@ public class SplashscreenContentDrawer {
     */
    private static final float NO_BACKGROUND_SCALE = 192f / 160;
    private final Context mContext;
    private final IconProvider mIconProvider;
    private final HighResIconProvider mHighResIconProvider;

    private int mIconSize;
    private int mDefaultIconSize;
@@ -115,12 +117,10 @@ public class SplashscreenContentDrawer {
    private final Handler mSplashscreenWorkerHandler;
    @VisibleForTesting
    final ColorCache mColorCache;
    private final ShellExecutor mSplashScreenExecutor;

    SplashscreenContentDrawer(Context context, IconProvider iconProvider, TransactionPool pool,
            ShellExecutor splashScreenExecutor) {
    SplashscreenContentDrawer(Context context, IconProvider iconProvider, TransactionPool pool) {
        mContext = context;
        mIconProvider = iconProvider;
        mHighResIconProvider = new HighResIconProvider(mContext, iconProvider);
        mTransactionPool = pool;

        // Initialize Splashscreen worker thread
@@ -131,7 +131,6 @@ public class SplashscreenContentDrawer {
        shellSplashscreenWorkerThread.start();
        mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler();
        mColorCache = new ColorCache(mContext, mSplashscreenWorkerHandler);
        mSplashScreenExecutor = splashScreenExecutor;
    }

    /**
@@ -416,18 +415,16 @@ public class SplashscreenContentDrawer {
                        || mTmpAttrs.mIconBgColor == mThemeColor) {
                    mFinalIconSize *= NO_BACKGROUND_SCALE;
                }
                createIconDrawable(iconDrawable, false);
                createIconDrawable(iconDrawable, false /* legacy */, false /* loadInDetail */);
            } else {
                final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
                final int densityDpi = mContext.getResources().getConfiguration().densityDpi;
                final int scaledIconDpi =
                        (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "getIcon");
                iconDrawable = mIconProvider.getIcon(mActivityInfo, scaledIconDpi);
                iconDrawable = mHighResIconProvider.getIcon(
                        mActivityInfo, densityDpi, scaledIconDpi);
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                if (iconDrawable == null) {
                    iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
                }
                if (!processAdaptiveIcon(iconDrawable)) {
                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
                            "The icon is not an AdaptiveIconDrawable");
@@ -437,7 +434,8 @@ public class SplashscreenContentDrawer {
                            scaledIconDpi, mFinalIconSize);
                    final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(iconDrawable);
                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                    createIconDrawable(new BitmapDrawable(bitmap), true);
                    createIconDrawable(new BitmapDrawable(bitmap), true,
                            mHighResIconProvider.mLoadInDetail);
                }
            }

@@ -450,14 +448,16 @@ public class SplashscreenContentDrawer {
            }
        }

        private void createIconDrawable(Drawable iconDrawable, boolean legacy) {
        private void createIconDrawable(Drawable iconDrawable, boolean legacy,
                boolean loadInDetail) {
            if (legacy) {
                mFinalIconDrawables = SplashscreenIconDrawableFactory.makeLegacyIconDrawable(
                        iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
                        iconDrawable, mDefaultIconSize, mFinalIconSize, loadInDetail,
                        mSplashscreenWorkerHandler);
            } else {
                mFinalIconDrawables = SplashscreenIconDrawableFactory.makeIconDrawable(
                        mTmpAttrs.mIconBgColor, mThemeColor, iconDrawable, mDefaultIconSize,
                        mFinalIconSize, mSplashscreenWorkerHandler);
                        mFinalIconSize, loadInDetail, mSplashscreenWorkerHandler);
            }
        }

@@ -506,11 +506,11 @@ public class SplashscreenContentDrawer {
                // Using AdaptiveIconDrawable here can help keep the shape consistent with the
                // current settings.
                mFinalIconSize = (int) (0.5f + mIconSize * noBgScale);
                createIconDrawable(iconForeground, false);
                createIconDrawable(iconForeground, false, mHighResIconProvider.mLoadInDetail);
            } else {
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
                        "processAdaptiveIcon: draw whole icon");
                createIconDrawable(iconDrawable, false);
                createIconDrawable(iconDrawable, false, mHighResIconProvider.mLoadInDetail);
            }
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            return true;
@@ -1015,4 +1015,77 @@ public class SplashscreenContentDrawer {
            playAnimation.run();
        }
    }

    /**
     * When loading a BitmapDrawable object with specific density, there will decode the image based
     * on the density from display metrics, so even when load with higher override density, the
     * final intrinsic size of a BitmapDrawable can still not big enough to draw on expect size.
     *
     * So here we use a standalone IconProvider object to load the Drawable object for higher
     * density, and the resources object won't affect the entire system.
     *
     */
    private static class HighResIconProvider {
        private final Context mSharedContext;
        private final IconProvider mSharedIconProvider;
        private boolean mLoadInDetail;

        // only create standalone icon provider when the density dpi is low.
        private Context mStandaloneContext;
        private IconProvider mStandaloneIconProvider;

        HighResIconProvider(Context context, IconProvider sharedIconProvider) {
            mSharedContext = context;
            mSharedIconProvider = sharedIconProvider;
        }

        Drawable getIcon(ActivityInfo activityInfo, int currentDpi, int iconDpi) {
            mLoadInDetail = false;
            Drawable drawable;
            if (currentDpi < iconDpi && currentDpi < DisplayMetrics.DENSITY_XHIGH) {
                drawable = loadFromStandalone(activityInfo, currentDpi, iconDpi);
            } else {
                drawable = mSharedIconProvider.getIcon(activityInfo, iconDpi);
            }

            if (drawable == null) {
                drawable = mSharedContext.getPackageManager().getDefaultActivityIcon();
            }
            return drawable;
        }

        private Drawable loadFromStandalone(ActivityInfo activityInfo, int currentDpi,
                int iconDpi) {
            if (mStandaloneContext == null) {
                final Configuration defConfig = mSharedContext.getResources().getConfiguration();
                mStandaloneContext = mSharedContext.createConfigurationContext(defConfig);
                mStandaloneIconProvider = new IconProvider(mStandaloneContext);
            }
            Resources resources;
            try {
                resources = mStandaloneContext.getPackageManager()
                        .getResourcesForApplication(activityInfo.applicationInfo);
            } catch (PackageManager.NameNotFoundException | Resources.NotFoundException exc) {
                resources = null;
            }
            if (resources != null) {
                updateResourcesDpi(resources, iconDpi);
            }
            final Drawable drawable = mStandaloneIconProvider.getIcon(activityInfo, iconDpi);
            mLoadInDetail = true;
            // reset density dpi
            if (resources != null) {
                updateResourcesDpi(resources, currentDpi);
            }
            return drawable;
        }

        private void updateResourcesDpi(Resources resources, int densityDpi) {
            final Configuration config = resources.getConfiguration();
            final DisplayMetrics displayMetrics = resources.getDisplayMetrics();
            config.densityDpi = densityDpi;
            displayMetrics.densityDpi = densityDpi;
            resources.updateConfiguration(config, displayMetrics);
        }
    }
}
+14 −9
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ public class SplashscreenIconDrawableFactory {
     */
    static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor,
            @NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
            Handler splashscreenWorkerHandler) {
            boolean loadInDetail, Handler splashscreenWorkerHandler) {
        Drawable foreground;
        Drawable background = null;
        boolean drawBackground =
@@ -74,13 +74,13 @@ public class SplashscreenIconDrawableFactory {
            // If the icon is Adaptive, we already use the icon background.
            drawBackground = false;
            foreground = new ImmobileIconDrawable(foregroundDrawable,
                    srcIconSize, iconSize, splashscreenWorkerHandler);
                    srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler);
        } else {
            // Adaptive icon don't handle transparency so we draw the background of the adaptive
            // icon with the same color as the window background color instead of using two layers
            foreground = new ImmobileIconDrawable(
                    new AdaptiveForegroundDrawable(foregroundDrawable),
                    srcIconSize, iconSize, splashscreenWorkerHandler);
                    srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler);
        }

        if (drawBackground) {
@@ -91,9 +91,9 @@ public class SplashscreenIconDrawableFactory {
    }

    static Drawable[] makeLegacyIconDrawable(@NonNull Drawable iconDrawable, int srcIconSize,
            int iconSize, Handler splashscreenWorkerHandler) {
            int iconSize, boolean loadInDetail, Handler splashscreenWorkerHandler) {
        return new Drawable[]{new ImmobileIconDrawable(iconDrawable, srcIconSize, iconSize,
                splashscreenWorkerHandler)};
                loadInDetail, splashscreenWorkerHandler)};
    }

    /**
@@ -106,12 +106,17 @@ public class SplashscreenIconDrawableFactory {
        private final Matrix mMatrix = new Matrix();
        private Bitmap mIconBitmap;

        ImmobileIconDrawable(Drawable drawable, int srcIconSize, int iconSize,
        ImmobileIconDrawable(Drawable drawable, int srcIconSize, int iconSize, boolean loadInDetail,
                Handler splashscreenWorkerHandler) {
            // This icon has lower density, don't scale it.
            if (loadInDetail) {
                splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, iconSize));
            } else {
                final float scale = (float) iconSize / srcIconSize;
                mMatrix.setScale(scale, scale);
                splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, srcIconSize));
            }
        }

        private void preDrawIcon(Drawable drawable, int size) {
            synchronized (mPaint) {
+1 −2
Original line number Diff line number Diff line
@@ -153,8 +153,7 @@ public class StartingSurfaceDrawer {
        mContext = context;
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        mSplashScreenExecutor = splashScreenExecutor;
        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool,
                mSplashScreenExecutor);
        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool);
        mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
        mWindowManagerGlobal = WindowManagerGlobal.getInstance();
        mDisplayManager.getDisplay(DEFAULT_DISPLAY);