diff --git a/res/xml/grayscale_icon_map.xml b/res/xml/grayscale_icon_map.xml new file mode 100644 index 0000000000..f6383ceb1e --- /dev/null +++ b/res/xml/grayscale_icon_map.xml @@ -0,0 +1,17 @@ + + + diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 10023b43d9..521bf13349 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -36,6 +36,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.IconShape; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconProvider; +import com.android.launcher3.icons.LauncherIconProvider; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pm.InstallSessionHelper; @@ -61,7 +62,7 @@ public class LauncherAppState implements SafeCloseable { private final Context mContext; private final LauncherModel mModel; - private final IconProvider mIconProvider; + private final LauncherIconProvider mIconProvider; private final IconCache mIconCache; private final InvariantDeviceProfile mInvariantDeviceProfile; private final RunnableList mOnTerminateCallback = new RunnableList(); @@ -138,7 +139,7 @@ public class LauncherAppState implements SafeCloseable { mContext = context; mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); - mIconProvider = new IconProvider(context, Themes.isThemedIconEnabled(context)); + mIconProvider = new LauncherIconProvider(context); mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName, mIconProvider); mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext), diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index ac194d2077..b062b9176f 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -76,9 +76,8 @@ import androidx.core.os.BuildCompat; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.graphics.GridCustomizationsProvider; import com.android.launcher3.graphics.TintedDrawableSpan; -import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.icons.ShortcutCachingLogic; +import com.android.launcher3.icons.ThemedIconDrawable; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.SearchActionItemInfo; @@ -87,6 +86,7 @@ import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.widget.PendingAddShortcutInfo; @@ -123,8 +123,9 @@ public final class Utilities { public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R; - public static final boolean ATLEAST_S = BuildCompat.isAtLeastS() - || Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; + public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; + + public static final boolean ATLEAST_T = BuildCompat.isAtLeastT(); /** * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}. @@ -672,11 +673,19 @@ public final class Utilities { * @param outObj this is set to the internal data associated with {@param info}, * eg {@link LauncherActivityInfo} or {@link ShortcutInfo}. */ + @TargetApi(Build.VERSION_CODES.TIRAMISU) public static Drawable getFullDrawable(Context context, ItemInfo info, int width, int height, Object[] outObj) { Drawable icon = loadFullDrawableWithoutTheme(context, info, width, height, outObj); - if (icon instanceof BitmapInfo.Extender) { - icon = ((BitmapInfo.Extender) icon).getThemedDrawable(context); + if (ATLEAST_T && icon instanceof AdaptiveIconDrawable) { + AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon.mutate(); + Drawable mono = aid.getMonochrome(); + if (mono != null && Themes.isThemedIconEnabled(context)) { + int[] colors = ThemedIconDrawable.getColors(context); + mono = mono.mutate(); + mono.setTint(colors[1]); + return new AdaptiveIconDrawable(new ColorDrawable(colors[0]), mono); + } } return icon; } @@ -719,8 +728,7 @@ public final class Utilities { return icon; } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION && info instanceof SearchActionItemInfo) { - return new AdaptiveIconDrawable( - new FastBitmapDrawable(((SearchActionItemInfo) info).bitmap), null); + return ((SearchActionItemInfo) info).bitmap.newIcon(context); } else { return null; } diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index f4d64df7d9..88ce57e40f 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -133,23 +133,25 @@ public class LauncherPreviewRenderer extends ContextWrapper mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp); mObjectMap.put(LauncherAppState.INSTANCE, new LauncherAppState(this, null /* iconCacheFileName */)); - } - public LauncherIcons newLauncherIcons(Context context, boolean shapeDetection) { + /** + * Creates a new LauncherIcons for the preview, skipping the global pool + */ + public LauncherIcons newLauncherIcons(Context context) { LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll(); if (launcherIconsForPreview != null) { return launcherIconsForPreview; } return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize, - -1 /* poolId */, shapeDetection); + -1 /* poolId */); } private final class LauncherIconsForPreview extends LauncherIcons { private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize, - int poolId, boolean shapeDetection) { - super(context, fillResIconDpi, iconBitmapSize, poolId, shapeDetection); + int poolId) { + super(context, fillResIconDpi, iconBitmapSize, poolId); } @Override diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java new file mode 100644 index 0000000000..a7379bbd96 --- /dev/null +++ b/src/com/android/launcher3/icons/LauncherIconProvider.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 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; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.launcher3.R; +import com.android.launcher3.util.Themes; + +import org.xmlpull.v1.XmlPullParser; + +import java.util.Collections; +import java.util.Map; + +/** + * Extension of {@link IconProvider} with support for overriding theme icons + */ +public class LauncherIconProvider extends IconProvider { + + private static final String TAG_ICON = "icon"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_DRAWABLE = "drawable"; + + private static final String TAG = "LIconProvider"; + private static final Map DISABLED_MAP = Collections.emptyMap(); + + private Map mThemedIconMap; + private boolean mSupportsIconTheme; + + public LauncherIconProvider(Context context) { + super(context); + setIconThemeSupported(Themes.isThemedIconEnabled(context)); + } + + /** + * Enables or disables icon theme support + */ + public void setIconThemeSupported(boolean isSupported) { + mSupportsIconTheme = isSupported; + mThemedIconMap = isSupported ? null : DISABLED_MAP; + } + + @Override + protected ThemeData getThemeDataForPackage(String packageName) { + return getThemedIconMap().get(packageName); + } + + @Override + public String getSystemIconState() { + return super.getSystemIconState() + (mSupportsIconTheme ? ",with-theme" : ",no-theme"); + } + + private Map getThemedIconMap() { + if (mThemedIconMap != null) { + return mThemedIconMap; + } + ArrayMap map = new ArrayMap<>(); + Resources res = mContext.getResources(); + try (XmlResourceParser parser = res.getXml(R.xml.grayscale_icon_map)) { + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT); + + while (((type = parser.next()) != XmlPullParser.END_TAG + || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if (type != XmlPullParser.START_TAG) { + continue; + } + if (TAG_ICON.equals(parser.getName())) { + String pkg = parser.getAttributeValue(null, ATTR_PACKAGE); + int iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0); + if (iconId != 0 && !TextUtils.isEmpty(pkg)) { + map.put(pkg, new ThemeData(res, iconId)); + } + } + } + } catch (Exception e) { + Log.e(TAG, "Unable to parse icon map", e); + } + mThemedIconMap = map; + return mThemedIconMap; + } +} diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java index bf7897e8cc..5508c49410 100644 --- a/src/com/android/launcher3/icons/LauncherIcons.java +++ b/src/com/android/launcher3/icons/LauncherIcons.java @@ -21,6 +21,7 @@ import android.content.Context; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.graphics.IconShape; import com.android.launcher3.graphics.LauncherPreviewRenderer; +import com.android.launcher3.util.Themes; /** * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class @@ -32,18 +33,13 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { private static LauncherIcons sPool; private static int sPoolId = 0; - public static LauncherIcons obtain(Context context) { - return obtain(context, IconShape.getShape().enableShapeDetection()); - } - /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ - public static LauncherIcons obtain(Context context, boolean shapeDetection) { + public static LauncherIcons obtain(Context context) { if (context instanceof LauncherPreviewRenderer.PreviewContext) { - return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context, - shapeDetection); + return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context); } int poolId; @@ -58,8 +54,7 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { } InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context); - return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId, - shapeDetection); + return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId); } public static void clearPool() { @@ -73,9 +68,9 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { private LauncherIcons next; - protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId, - boolean shapeDetection) { - super(context, fillResIconDpi, iconBitmapSize, shapeDetection); + protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { + super(context, fillResIconDpi, iconBitmapSize, IconShape.getShape().enableShapeDetection()); + mMonoIconEnabled = Themes.isThemedIconEnabled(context); mPoolId = poolId; }