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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,11 +106,16 @@ 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) {
|
||||
final float scale = (float) iconSize / srcIconSize;
|
||||
mMatrix.setScale(scale, scale);
|
||||
splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, srcIconSize));
|
||||
// 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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user