Generalizing monochrome icon into Theme icons
Bug: 381897614
Flag: EXEMPT refactor
Test: atest MonoIconThemeControllerTest
atest MonoThemedBitmapTest
Change-Id: Iaa0cbe3523ee26a6d4f77e002d0cca90657f89b2
This commit is contained in:
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction;
|
||||
|
||||
import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
|
||||
import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
|
||||
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
|
||||
@@ -36,9 +34,6 @@ import android.content.pm.LauncherApps;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BlendMode;
|
||||
import android.graphics.BlendModeColorFilter;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.LightingColorFilter;
|
||||
@@ -49,10 +44,8 @@ import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.InsetDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.DeadObjectException;
|
||||
@@ -85,7 +78,6 @@ import com.android.launcher3.graphics.TintedDrawableSpan;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.CacheableShortcutInfo;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.icons.ThemedIconDrawable;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
|
||||
@@ -696,29 +688,19 @@ public final class Utilities {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Inject monochrome icon drawable
|
||||
// Inject theme icon drawable
|
||||
if (ATLEAST_T && useTheme) {
|
||||
result.mutate();
|
||||
int[] colors = ThemedIconDrawable.getColors(context);
|
||||
Drawable mono = result.getMonochrome();
|
||||
|
||||
if (mono != null) {
|
||||
mono.setTint(colors[1]);
|
||||
} else if (info instanceof ItemInfoWithIcon iiwi) {
|
||||
// Inject a previously generated monochrome icon
|
||||
Bitmap monoBitmap = iiwi.bitmap.getMono();
|
||||
if (monoBitmap != null) {
|
||||
// Use BitmapDrawable instead of FastBitmapDrawable so that the colorState is
|
||||
// preserved in constantState
|
||||
mono = new BitmapDrawable(monoBitmap);
|
||||
mono.setColorFilter(new BlendModeColorFilter(colors[1], BlendMode.SRC_IN));
|
||||
// Inset the drawable according to the AdaptiveIconDrawable layers
|
||||
mono = new InsetDrawable(mono, getExtraInsetFraction() / 2);
|
||||
try (LauncherIcons li = LauncherIcons.obtain(context)) {
|
||||
if (li.getThemeController() != null) {
|
||||
AdaptiveIconDrawable themed = li.getThemeController().createThemedAdaptiveIcon(
|
||||
context,
|
||||
result,
|
||||
info instanceof ItemInfoWithIcon iiwi ? iiwi.bitmap : null);
|
||||
if (themed != null) {
|
||||
result = themed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mono != null) {
|
||||
result = new AdaptiveIconDrawable(new ColorDrawable(colors[0]), mono);
|
||||
}
|
||||
}
|
||||
|
||||
if (badge == null) {
|
||||
|
||||
@@ -17,15 +17,13 @@
|
||||
package com.android.launcher3.icons;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.Flags;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.graphics.IconShape;
|
||||
import com.android.launcher3.icons.mono.MonoIconThemeController;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.SafeCloseable;
|
||||
@@ -57,13 +55,13 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
||||
|
||||
private final ConcurrentLinkedQueue<LauncherIcons> mPool;
|
||||
|
||||
private MonochromeIconFactory mMonochromeIconFactory;
|
||||
|
||||
protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize,
|
||||
ConcurrentLinkedQueue<LauncherIcons> pool) {
|
||||
super(context, fillResIconDpi, iconBitmapSize,
|
||||
IconShape.INSTANCE.get(context).getShape().enableShapeDetection());
|
||||
mMonoIconEnabled = Themes.isThemedIconEnabled(context);
|
||||
if (Themes.isThemedIconEnabled(context)) {
|
||||
mThemeController = new MonoIconThemeController();
|
||||
}
|
||||
mPool = pool;
|
||||
}
|
||||
|
||||
@@ -75,18 +73,6 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
||||
mPool.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Drawable getMonochromeDrawable(AdaptiveIconDrawable base) {
|
||||
Drawable mono = super.getMonochromeDrawable(base);
|
||||
if (mono != null || !Flags.forceMonochromeAppIcons()) {
|
||||
return mono;
|
||||
}
|
||||
if (mMonochromeIconFactory == null) {
|
||||
mMonochromeIconFactory = new MonochromeIconFactory(mIconBitmapSize);
|
||||
}
|
||||
return mMonochromeIconFactory.wrap(base);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected UserIconInfo getUserInfo(@NonNull UserHandle user) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.folder
|
||||
|
||||
import android.R
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Process
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
@@ -26,9 +27,9 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.android.launcher3.LauncherPrefs.Companion.THEMED_ICONS
|
||||
import com.android.launcher3.LauncherPrefs.Companion.get
|
||||
import com.android.launcher3.graphics.PreloadIconDrawable
|
||||
import com.android.launcher3.icons.BaseIconFactory
|
||||
import com.android.launcher3.icons.FastBitmapDrawable
|
||||
import com.android.launcher3.icons.UserBadgeDrawable
|
||||
import com.android.launcher3.icons.mono.MonoThemedBitmap
|
||||
import com.android.launcher3.model.data.FolderInfo
|
||||
import com.android.launcher3.model.data.ItemInfo
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED
|
||||
@@ -88,54 +89,26 @@ class PreviewItemManagerTest {
|
||||
// Use getAppContents() to "cast" contents to WorkspaceItemInfo so we can set bitmaps
|
||||
val folderApps = modelHelper.bgDataModel.collections.valueAt(0).getAppContents()
|
||||
// Set first icon to be themed.
|
||||
folderApps[0]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
folderApps[0].bitmap.themedBitmap =
|
||||
MonoThemedBitmap(
|
||||
folderApps[0].bitmap.icon,
|
||||
BaseIconFactory(
|
||||
context,
|
||||
context.resources.configuration.densityDpi,
|
||||
previewItemManager.mIconSize,
|
||||
),
|
||||
Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
|
||||
)
|
||||
|
||||
// Set second icon to be non-themed.
|
||||
folderApps[1]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
null,
|
||||
BaseIconFactory(
|
||||
context,
|
||||
context.resources.configuration.densityDpi,
|
||||
previewItemManager.mIconSize,
|
||||
),
|
||||
)
|
||||
folderApps[1].bitmap.themedBitmap = null
|
||||
|
||||
// Set third icon to be themed with badge.
|
||||
folderApps[2]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
folderApps[2].bitmap.themedBitmap =
|
||||
MonoThemedBitmap(
|
||||
folderApps[2].bitmap.icon,
|
||||
BaseIconFactory(
|
||||
context,
|
||||
context.resources.configuration.densityDpi,
|
||||
previewItemManager.mIconSize,
|
||||
),
|
||||
Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
|
||||
)
|
||||
folderApps[2].bitmap = folderApps[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
|
||||
|
||||
// Set fourth icon to be non-themed with badge.
|
||||
folderApps[3].bitmap = folderApps[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
|
||||
folderApps[3]
|
||||
.bitmap
|
||||
.setMonoIcon(
|
||||
null,
|
||||
BaseIconFactory(
|
||||
context,
|
||||
context.resources.configuration.densityDpi,
|
||||
previewItemManager.mIconSize,
|
||||
),
|
||||
)
|
||||
folderApps[3].bitmap.themedBitmap = null
|
||||
|
||||
defaultThemedIcons = get(context).get(THEMED_ICONS)
|
||||
}
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.icons.mono
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.AdaptiveIconDrawable
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.platform.test.annotations.DisableFlags
|
||||
import android.platform.test.annotations.EnableFlags
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import android.platform.uiautomatorhelpers.DeviceHelpers.context
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.Flags
|
||||
import com.android.launcher3.icons.BaseIconFactory
|
||||
import com.android.launcher3.icons.BitmapInfo
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assume.assumeFalse
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MonoIconThemeControllerTest {
|
||||
|
||||
@get:Rule val mSetFlagsRule = SetFlagsRule()
|
||||
|
||||
private val iconFactory = BaseIconFactory(context, DisplayMetrics.DENSITY_MEDIUM, 30)
|
||||
|
||||
@Test
|
||||
fun `createThemedBitmap when mono drawable is present`() {
|
||||
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, ColorDrawable(Color.RED))
|
||||
assertNotNull(
|
||||
MonoIconThemeController().createThemedBitmap(icon, BitmapInfo.LOW_RES_INFO, iconFactory)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(Flags.FLAG_FORCE_MONOCHROME_APP_ICONS)
|
||||
fun `createThemedBitmap when mono generation is disabled`() {
|
||||
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, null)
|
||||
assertNull(
|
||||
MonoIconThemeController().createThemedBitmap(icon, BitmapInfo.LOW_RES_INFO, iconFactory)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_FORCE_MONOCHROME_APP_ICONS)
|
||||
fun `createThemedBitmap when mono generation is enabled`() {
|
||||
ensureBitmapSerializationSupported()
|
||||
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, null)
|
||||
assertNotNull(
|
||||
MonoIconThemeController().createThemedBitmap(icon, BitmapInfo.LOW_RES_INFO, iconFactory)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decode bitmap after serialization valid data`() {
|
||||
ensureBitmapSerializationSupported()
|
||||
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, ColorDrawable(Color.RED))
|
||||
val iconInfo = iconFactory.createBadgedIconBitmap(icon)
|
||||
|
||||
val themeBitmap =
|
||||
MonoIconThemeController().createThemedBitmap(icon, iconInfo, iconFactory)!!
|
||||
assertNotNull(
|
||||
MonoIconThemeController().decode(themeBitmap.serialize(), iconInfo, iconFactory)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decode bitmap after serialization invalid data`() {
|
||||
ensureBitmapSerializationSupported()
|
||||
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, ColorDrawable(Color.RED))
|
||||
val iconInfo = iconFactory.createBadgedIconBitmap(icon)
|
||||
assertNull(MonoIconThemeController().decode(byteArrayOf(1, 1, 1, 1), iconInfo, iconFactory))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createThemedAdaptiveIcon with monochrome drawable`() {
|
||||
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, ColorDrawable(Color.RED))
|
||||
assertNotNull(MonoIconThemeController().createThemedAdaptiveIcon(context, icon, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createThemedAdaptiveIcon with bitmap info`() {
|
||||
val icon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, ColorDrawable(Color.RED))
|
||||
val iconInfo = iconFactory.createBadgedIconBitmap(icon)
|
||||
iconInfo.themedBitmap =
|
||||
MonoIconThemeController().createThemedBitmap(icon, iconInfo, iconFactory)
|
||||
|
||||
val nonMonoIcon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, null)
|
||||
assertNotNull(
|
||||
MonoIconThemeController().createThemedAdaptiveIcon(context, nonMonoIcon, iconInfo)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createThemedAdaptiveIcon invalid bitmap info`() {
|
||||
val nonMonoIcon = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null, null)
|
||||
assertNull(
|
||||
MonoIconThemeController()
|
||||
.createThemedAdaptiveIcon(context, nonMonoIcon, BitmapInfo.LOW_RES_INFO)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun ensureBitmapSerializationSupported() {
|
||||
// Robolectric doesn't support serializing 8-bit bitmaps
|
||||
assumeFalse(isRunningInRobolectric)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.icons.mono
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.platform.uiautomatorhelpers.DeviceHelpers.context
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.icons.BitmapInfo
|
||||
import com.android.launcher3.icons.mono.MonoIconThemeControllerTest.Companion.ensureBitmapSerializationSupported
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MonoThemedBitmapTest {
|
||||
|
||||
@Test
|
||||
fun `newDrawable returns valid drawable`() {
|
||||
val bitmap =
|
||||
MonoThemedBitmap(
|
||||
Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8),
|
||||
Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
|
||||
)
|
||||
val d = bitmap.newDrawable(BitmapInfo.LOW_RES_INFO, context)
|
||||
assertTrue(d is ThemedIconDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `serialize returns valid bytes`() {
|
||||
ensureBitmapSerializationSupported()
|
||||
val bitmap =
|
||||
MonoThemedBitmap(
|
||||
Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8),
|
||||
Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888),
|
||||
)
|
||||
assertTrue(bitmap.serialize().isNotEmpty())
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.allapps.AllAppsRecyclerView;
|
||||
import com.android.launcher3.celllayout.FavoriteItemsTransaction;
|
||||
import com.android.launcher3.icons.ThemedIconDrawable;
|
||||
import com.android.launcher3.icons.mono.ThemedIconDrawable;
|
||||
import com.android.launcher3.popup.ArrowPopup;
|
||||
import com.android.launcher3.util.BaseLauncherActivityTest;
|
||||
import com.android.launcher3.util.Executors;
|
||||
|
||||
Reference in New Issue
Block a user