1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.icons; 17 18 import android.content.Context; 19 import android.content.pm.ApplicationInfo; 20 import android.content.res.Resources; 21 import android.content.res.XmlResourceParser; 22 import android.graphics.drawable.AdaptiveIconDrawable; 23 import android.graphics.drawable.Drawable; 24 import android.text.TextUtils; 25 import android.util.ArrayMap; 26 import android.util.Log; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.launcher3.R; 32 import com.android.launcher3.config.FeatureFlags; 33 import com.android.launcher3.dagger.ApplicationContext; 34 import com.android.launcher3.dagger.LauncherAppSingleton; 35 import com.android.launcher3.graphics.ShapeDelegate; 36 import com.android.launcher3.graphics.ThemeManager; 37 import com.android.launcher3.util.ApiWrapper; 38 39 import org.xmlpull.v1.XmlPullParser; 40 41 import java.util.Collections; 42 import java.util.Map; 43 44 import javax.inject.Inject; 45 46 /** 47 * Extension of {@link IconProvider} with support for overriding theme icons 48 */ 49 @LauncherAppSingleton 50 public class LauncherIconProvider extends IconProvider { 51 52 private static final String TAG_ICON = "icon"; 53 private static final String ATTR_PACKAGE = "package"; 54 private static final String ATTR_DRAWABLE = "drawable"; 55 56 private static final String TAG = "LIconProvider"; 57 private static final Map<String, ThemeData> DISABLED_MAP = Collections.emptyMap(); 58 59 private Map<String, ThemeData> mThemedIconMap; 60 61 private final ApiWrapper mApiWrapper; 62 private final ThemeManager mThemeManager; 63 64 @Inject LauncherIconProvider( @pplicationContext Context context, ThemeManager themeManager, ApiWrapper apiWrapper)65 public LauncherIconProvider( 66 @ApplicationContext Context context, 67 ThemeManager themeManager, 68 ApiWrapper apiWrapper) { 69 super(context); 70 mThemeManager = themeManager; 71 mApiWrapper = apiWrapper; 72 setIconThemeSupported(mThemeManager.isMonoThemeEnabled()); 73 } 74 75 /** 76 * Enables or disables icon theme support 77 */ setIconThemeSupported(boolean isSupported)78 public void setIconThemeSupported(boolean isSupported) { 79 mThemedIconMap = isSupported && FeatureFlags.USE_LOCAL_ICON_OVERRIDES.get() 80 ? null : DISABLED_MAP; 81 } 82 83 @Override getThemeDataForPackage(String packageName)84 protected ThemeData getThemeDataForPackage(String packageName) { 85 return getThemedIconMap().get(packageName); 86 } 87 88 @Override updateSystemState()89 public void updateSystemState() { 90 super.updateSystemState(); 91 mSystemState += "," + mThemeManager.getIconState().toUniqueId(); 92 } 93 94 @Override getApplicationInfoHash(@onNull ApplicationInfo appInfo)95 protected String getApplicationInfoHash(@NonNull ApplicationInfo appInfo) { 96 return mApiWrapper.getApplicationInfoHash(appInfo); 97 } 98 99 @Nullable 100 @Override loadAppInfoIcon(ApplicationInfo info, Resources resources, int density)101 protected Drawable loadAppInfoIcon(ApplicationInfo info, Resources resources, int density) { 102 // Tries to load the round icon res, if the app defines it as an adaptive icon 103 if (mThemeManager.getIconShape() instanceof ShapeDelegate.Circle) { 104 int roundIconRes = mApiWrapper.getRoundIconRes(info); 105 if (roundIconRes != 0 && roundIconRes != info.icon) { 106 try { 107 Drawable d = resources.getDrawableForDensity(roundIconRes, density); 108 if (d instanceof AdaptiveIconDrawable) { 109 return d; 110 } 111 } catch (Resources.NotFoundException exc) { } 112 } 113 } 114 return super.loadAppInfoIcon(info, resources, density); 115 } 116 getThemedIconMap()117 private Map<String, ThemeData> getThemedIconMap() { 118 if (mThemedIconMap != null) { 119 return mThemedIconMap; 120 } 121 ArrayMap<String, ThemeData> map = new ArrayMap<>(); 122 Resources res = mContext.getResources(); 123 try (XmlResourceParser parser = res.getXml(R.xml.grayscale_icon_map)) { 124 final int depth = parser.getDepth(); 125 int type; 126 while ((type = parser.next()) != XmlPullParser.START_TAG 127 && type != XmlPullParser.END_DOCUMENT); 128 129 while (((type = parser.next()) != XmlPullParser.END_TAG 130 || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 131 if (type != XmlPullParser.START_TAG) { 132 continue; 133 } 134 if (TAG_ICON.equals(parser.getName())) { 135 String pkg = parser.getAttributeValue(null, ATTR_PACKAGE); 136 int iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0); 137 if (iconId != 0 && !TextUtils.isEmpty(pkg)) { 138 map.put(pkg, new ThemeData(res, iconId)); 139 } 140 } 141 } 142 } catch (Exception e) { 143 Log.e(TAG, "Unable to parse icon map", e); 144 } 145 mThemedIconMap = map; 146 return mThemedIconMap; 147 } 148 } 149