Loading packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +14 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.annotation.SuppressLint; import android.app.INotificationManager; import android.content.Context; import android.content.SharedPreferences; import android.content.om.OverlayManager; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.HandlerThread; Loading @@ -43,6 +44,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.accessibility.ModeSwitchesController; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; Loading Loading @@ -78,6 +80,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.theme.ThemeOverlayApplier; import com.android.systemui.util.leak.LeakDetector; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; Loading Loading @@ -193,6 +196,17 @@ public class DependencyProvider { return new PluginManagerImpl(context, new PluginInitializerImpl()); } /** */ @SysUISingleton @Provides static ThemeOverlayApplier provideThemeOverlayManager(Context context, @Background Executor bgExecutor, OverlayManager overlayManager, DumpManager dumpManager) { return new ThemeOverlayApplier(overlayManager, bgExecutor, context.getString(R.string.launcher_overlayable_package), context.getString(R.string.themepicker_overlayable_package), dumpManager); } /** */ @Provides @SysUISingleton Loading packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +8 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.app.role.RoleManager; import android.app.trust.TrustManager; import android.content.ContentResolver; import android.content.Context; import android.content.om.OverlayManager; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; Loading Loading @@ -351,7 +352,7 @@ public class FrameworkServicesModule { @Provides static WallpaperManager provideWallpaperManager(Context context) { return (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); return context.getSystemService(WallpaperManager.class); } @Provides Loading @@ -361,6 +362,12 @@ public class FrameworkServicesModule { return context.getSystemService(WifiManager.class); } @Provides @Singleton static OverlayManager provideOverlayManager(Context context) { return context.getSystemService(OverlayManager.class); } @Provides @Singleton static WindowManager provideWindowManager(Context context) { Loading packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java→packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +101 −11 Original line number Diff line number Diff line Loading @@ -21,11 +21,18 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.google.android.collect.Lists; import com.google.android.collect.Sets; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; Loading @@ -34,9 +41,19 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.stream.Collectors; class ThemeOverlayManager { /** * Responsible for orchestrating overlays, based on user preferences and other inputs from * {@link ThemeOverlayController}. */ @SysUISingleton public class ThemeOverlayApplier implements Dumpable { private static final String TAG = "ThemeOverlayManager"; private static final boolean DEBUG = false; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting static final String MONET_ACCENT_COLOR_PACKAGE = "com.android.theme.accentcolor.color"; @VisibleForTesting static final String MONET_SYSTEM_PALETTE_PACKAGE = "com.android.theme.systemcolors.color"; @VisibleForTesting static final String ANDROID_PACKAGE = "android"; Loading @@ -46,7 +63,11 @@ class ThemeOverlayManager { static final String SYSUI_PACKAGE = "com.android.systemui"; @VisibleForTesting static final String OVERLAY_CATEGORY_COLOR = "android.theme.customization.accent_color"; static final String OVERLAY_CATEGORY_ACCENT_COLOR = "android.theme.customization.accent_color"; @VisibleForTesting static final String OVERLAY_CATEGORY_SYSTEM_PALETTE = "android.theme.customization.system_palette"; @VisibleForTesting static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font"; @VisibleForTesting Loading @@ -73,24 +94,36 @@ class ThemeOverlayManager { * starts with launcher and grouped by target package. */ static final List<String> THEME_CATEGORIES = Lists.newArrayList( OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ICON_LAUNCHER, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI, OVERLAY_CATEGORY_ICON_SETTINGS, OVERLAY_CATEGORY_ICON_THEME_PICKER); /* Categories that need to applied to the current user as well as the system user. */ /* Categories that need to be applied to the current user as well as the system user. */ @VisibleForTesting static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet( OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI); /** * List of main colors of Monet themes. These are extracted from overlays installed * on the system. */ private final ArrayList<Integer> mMainSystemColors = new ArrayList<>(); /** * Same as above, but providing accent colors instead of a system palette. */ private final ArrayList<Integer> mAccentColors = new ArrayList<>(); /* Allowed overlay categories for each target package. */ private final Map<String, Set<String>> mTargetPackageToCategories = new ArrayMap<>(); /* Target package for each overlay category. */ Loading @@ -100,15 +133,15 @@ class ThemeOverlayManager { private final String mLauncherPackage; private final String mThemePickerPackage; ThemeOverlayManager(OverlayManager overlayManager, Executor executor, String launcherPackage, String themePickerPackage) { public ThemeOverlayApplier(OverlayManager overlayManager, Executor executor, String launcherPackage, String themePickerPackage, DumpManager dumpManager) { mOverlayManager = overlayManager; mExecutor = executor; mLauncherPackage = launcherPackage; mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); mTargetPackageToCategories.put(SYSUI_PACKAGE, Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI)); mTargetPackageToCategories.put(SETTINGS_PACKAGE, Loading @@ -117,7 +150,7 @@ class ThemeOverlayManager { Sets.newHashSet(OVERLAY_CATEGORY_ICON_LAUNCHER)); mTargetPackageToCategories.put(mThemePickerPackage, Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER)); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_ANDROID, ANDROID_PACKAGE); Loading @@ -125,6 +158,54 @@ class ThemeOverlayManager { mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage); collectMonetSystemOverlays(); dumpManager.registerDumpable(TAG, this); } /** * List of accent colors available as Monet overlays. */ List<Integer> getAvailableAccentColors() { return mAccentColors; } /** * List of main system colors available as Monet overlays. */ List<Integer> getAvailableSystemColors() { return mMainSystemColors; } private void collectMonetSystemOverlays() { List<OverlayInfo> androidOverlays = mOverlayManager .getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM); for (OverlayInfo overlayInfo : androidOverlays) { String packageName = overlayInfo.packageName; if (DEBUG) { Log.d(TAG, "Processing overlay " + packageName); } if (OVERLAY_CATEGORY_SYSTEM_PALETTE.equals(overlayInfo.category) && packageName.startsWith(MONET_SYSTEM_PALETTE_PACKAGE)) { try { String color = packageName.replace(MONET_SYSTEM_PALETTE_PACKAGE, ""); mMainSystemColors.add(Integer.parseInt(color, 16)); } catch (NumberFormatException e) { Log.w(TAG, "Invalid package name for overlay " + packageName, e); } } else if (OVERLAY_CATEGORY_ACCENT_COLOR.equals(overlayInfo.category) && packageName.startsWith(MONET_ACCENT_COLOR_PACKAGE)) { try { String color = packageName.replace(MONET_ACCENT_COLOR_PACKAGE, ""); mAccentColors.add(Integer.parseInt(color, 16)); } catch (NumberFormatException e) { Log.w(TAG, "Invalid package name for overlay " + packageName, e); } } else if (DEBUG) { Log.d(TAG, "Unknown overlay: " + packageName + " category: " + overlayInfo.category); } } } /** Loading Loading @@ -184,4 +265,13 @@ class ThemeOverlayManager { } }); } /** * @inherit */ @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mMainSystemColors=" + mMainSystemColors.size()); pw.println("mAccentColors=" + mAccentColors.size()); } } packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +217 −23 Original line number Diff line number Diff line Loading @@ -15,16 +15,20 @@ */ package com.android.systemui.theme; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; import android.app.ActivityManager; import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.om.OverlayManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; Loading @@ -33,20 +37,32 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.systemui.R; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.systemui.Dumpable; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; import com.google.android.collect.Sets; import org.json.JSONException; import org.json.JSONObject; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; Loading @@ -60,49 +76,76 @@ import javax.inject.Inject; * associated work profiles */ @SysUISingleton public class ThemeOverlayController extends SystemUI { public class ThemeOverlayController extends SystemUI implements Dumpable { private static final String TAG = "ThemeOverlayController"; private static final boolean DEBUG = false; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private ThemeOverlayManager mThemeManager; private UserManager mUserManager; private BroadcastDispatcher mBroadcastDispatcher; // If lock screen wallpaper colors should also be considered when selecting the theme. // Doing this has performance impact, given that overlays would need to be swapped when // the device unlocks. @VisibleForTesting static final boolean USE_LOCK_SCREEN_WALLPAPER = false; private final ThemeOverlayApplier mThemeManager; private final UserManager mUserManager; private final BroadcastDispatcher mBroadcastDispatcher; private final Executor mBgExecutor; private final SecureSettings mSecureSettings; private final Executor mMainExecutor; private final Handler mBgHandler; private final WallpaperManager mWallpaperManager; private final KeyguardStateController mKeyguardStateController; private WallpaperColors mLockColors; private WallpaperColors mSystemColors; // Color extracted from wallpaper, NOT the color used on the overlay private int mMainWallpaperColor = Color.TRANSPARENT; // Color extracted from wallpaper, NOT the color used on the overlay private int mWallpaperAccentColor = Color.TRANSPARENT; // Main system color that maps to an overlay color private int mSystemOverlayColor = Color.TRANSPARENT; // Accent color that maps to an overlay color private int mAccentOverlayColor = Color.TRANSPARENT; @Inject public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDispatcher, @Background Handler bgHandler) { @Background Handler bgHandler, @Main Executor mainExecutor, @Background Executor bgExecutor, ThemeOverlayApplier themeOverlayApplier, SecureSettings secureSettings, WallpaperManager wallpaperManager, UserManager userManager, KeyguardStateController keyguardStateController, DumpManager dumpManager) { super(context); mBroadcastDispatcher = broadcastDispatcher; mUserManager = userManager; mBgExecutor = bgExecutor; mMainExecutor = mainExecutor; mBgHandler = bgHandler; mThemeManager = themeOverlayApplier; mSecureSettings = secureSettings; mWallpaperManager = wallpaperManager; mKeyguardStateController = keyguardStateController; dumpManager.registerDumpable(TAG, this); } @Override public void start() { if (DEBUG) Log.d(TAG, "Start"); mUserManager = mContext.getSystemService(UserManager.class); mThemeManager = new ThemeOverlayManager( mContext.getSystemService(OverlayManager.class), AsyncTask.THREAD_POOL_EXECUTOR, mContext.getString(R.string.launcher_overlayable_package), mContext.getString(R.string.themepicker_overlayable_package)); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); mBroadcastDispatcher.registerReceiverWithHandler(new BroadcastReceiver() { mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); updateThemeOverlays(); } }, filter, mBgHandler, UserHandle.ALL); mContext.getContentResolver().registerContentObserver( }, filter, mBgExecutor, UserHandle.ALL); mSecureSettings.registerContentObserverForUser( Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), false, new ContentObserver(mBgHandler) { @Override public void onChange(boolean selfChange, Collection<Uri> uris, int flags, public void onChange(boolean selfChange, Collection<Uri> collection, int flags, int userId) { if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); if (ActivityManager.getCurrentUser() == userId) { Loading @@ -111,20 +154,147 @@ public class ThemeOverlayController extends SystemUI { } }, UserHandle.USER_ALL); // Upon boot, make sure we have the most up to date colors mBgExecutor.execute(() -> { WallpaperColors lockColors = mWallpaperManager.getWallpaperColors( WallpaperManager.FLAG_LOCK); WallpaperColors systemColor = mWallpaperManager.getWallpaperColors( WallpaperManager.FLAG_SYSTEM); mMainExecutor.execute(() -> { if (USE_LOCK_SCREEN_WALLPAPER) { mLockColors = lockColors; } mSystemColors = systemColor; reevaluateSystemTheme(); }); }); if (USE_LOCK_SCREEN_WALLPAPER) { mKeyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override public void onKeyguardShowingChanged() { if (mLockColors == null) { return; } // It's possible that the user has a lock screen wallpaper. On this case we'll // end up with different colors after unlocking. reevaluateSystemTheme(); } }); } mWallpaperManager.addOnColorsChangedListener((wallpaperColors, which) -> { if (USE_LOCK_SCREEN_WALLPAPER && (which & WallpaperManager.FLAG_LOCK) != 0) { mLockColors = wallpaperColors; if (DEBUG) { Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which); } } if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { mSystemColors = wallpaperColors; if (DEBUG) { Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which); } } reevaluateSystemTheme(); }, null, UserHandle.USER_ALL); } private void reevaluateSystemTheme() { if (mLockColors == null && mSystemColors == null) { Log.w(TAG, "Cannot update theme, colors are null"); return; } WallpaperColors currentColor = mKeyguardStateController.isShowing() && mLockColors != null ? mLockColors : mSystemColors; int mainColor = currentColor.getPrimaryColor().toArgb(); //TODO(b/172860591) implement more complex logic for picking accent color. //For now, picking the secondary should be enough. Color accentCandidate = currentColor.getSecondaryColor(); if (accentCandidate == null) { accentCandidate = currentColor.getTertiaryColor(); } if (accentCandidate == null) { accentCandidate = currentColor.getPrimaryColor(); } if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate.toArgb()) { return; } mMainWallpaperColor = mainColor; mWallpaperAccentColor = accentCandidate.toArgb(); // Let's compare these colors to our finite set of overlays, and then pick an overlay. List<Integer> systemColors = mThemeManager.getAvailableSystemColors(); List<Integer> accentColors = mThemeManager.getAvailableAccentColors(); if (systemColors.size() == 0 || accentColors.size() == 0) { if (DEBUG) { Log.d(TAG, "Cannot apply system theme, palettes are unavailable"); } return; } mSystemOverlayColor = getClosest(systemColors, mMainWallpaperColor); mAccentOverlayColor = getClosest(accentColors, mWallpaperAccentColor); updateThemeOverlays(); } /** * Given a color and a list of candidates, return the candidate that's the most similar to the * given color. */ private static int getClosest(List<Integer> candidates, int color) { float[] hslMain = new float[3]; float[] hslCandidate = new float[3]; ColorUtils.RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hslMain); hslMain[0] /= 360f; float minDistance = Float.MAX_VALUE; int closestColor = Color.TRANSPARENT; for (int candidate: candidates) { ColorUtils.RGBToHSL(Color.red(candidate), Color.green(candidate), Color.blue(candidate), hslCandidate); hslCandidate[0] /= 360f; float sqDistance = squared(hslCandidate[0] - hslMain[0]) + squared(hslCandidate[1] - hslMain[1]) + squared(hslCandidate[2] - hslMain[2]); if (sqDistance < minDistance) { minDistance = sqDistance; closestColor = candidate; } } return closestColor; } private static float squared(float f) { return f * f; } private void updateThemeOverlays() { final int currentUser = ActivityManager.getCurrentUser(); final String overlayPackageJson = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, final String overlayPackageJson = mSecureSettings.getStringForUser( Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, currentUser); if (DEBUG) Log.d(TAG, "updateThemeOverlays: " + overlayPackageJson); boolean hasSystemPalette = false; boolean hasAccentColor = false; final Map<String, String> categoryToPackage = new ArrayMap<>(); if (!TextUtils.isEmpty(overlayPackageJson)) { try { JSONObject object = new JSONObject(overlayPackageJson); for (String category : ThemeOverlayManager.THEME_CATEGORIES) { for (String category : ThemeOverlayApplier.THEME_CATEGORIES) { if (object.has(category)) { if (category.equals(OVERLAY_CATEGORY_ACCENT_COLOR)) { hasAccentColor = true; } else if (category.equals(OVERLAY_CATEGORY_SYSTEM_PALETTE)) { hasSystemPalette = true; } categoryToPackage.put(category, object.getString(category)); } } Loading @@ -132,6 +302,20 @@ public class ThemeOverlayController extends SystemUI { Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e); } } // Let's apply the system palette, but only if it was not overridden by the style picker. if (!hasSystemPalette && mSystemOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, ThemeOverlayApplier.MONET_SYSTEM_PALETTE_PACKAGE + Integer.toHexString(mSystemOverlayColor).toUpperCase()); } // Same for the accent color if (!hasAccentColor && mAccentOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ThemeOverlayApplier.MONET_ACCENT_COLOR_PACKAGE + Integer.toHexString(mAccentOverlayColor).toUpperCase()); } Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser)); for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { if (userInfo.isManagedProfile()) { Loading @@ -140,4 +324,14 @@ public class ThemeOverlayController extends SystemUI { } mThemeManager.applyCurrentUserOverlays(categoryToPackage, userHandles); } @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mLockColors=" + mLockColors); pw.println("mSystemColors=" + mSystemColors); pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor)); pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor)); pw.println("mSystemOverlayColor=" + Integer.toHexString(mSystemOverlayColor)); pw.println("mAccentOverlayColor=" + Integer.toHexString(mAccentOverlayColor)); } } packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java→packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +32 −21 File changed and moved.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +14 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.annotation.SuppressLint; import android.app.INotificationManager; import android.content.Context; import android.content.SharedPreferences; import android.content.om.OverlayManager; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.HandlerThread; Loading @@ -43,6 +44,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.accessibility.ModeSwitchesController; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; Loading Loading @@ -78,6 +80,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.theme.ThemeOverlayApplier; import com.android.systemui.util.leak.LeakDetector; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; Loading Loading @@ -193,6 +196,17 @@ public class DependencyProvider { return new PluginManagerImpl(context, new PluginInitializerImpl()); } /** */ @SysUISingleton @Provides static ThemeOverlayApplier provideThemeOverlayManager(Context context, @Background Executor bgExecutor, OverlayManager overlayManager, DumpManager dumpManager) { return new ThemeOverlayApplier(overlayManager, bgExecutor, context.getString(R.string.launcher_overlayable_package), context.getString(R.string.themepicker_overlayable_package), dumpManager); } /** */ @Provides @SysUISingleton Loading
packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +8 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.app.role.RoleManager; import android.app.trust.TrustManager; import android.content.ContentResolver; import android.content.Context; import android.content.om.OverlayManager; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; Loading Loading @@ -351,7 +352,7 @@ public class FrameworkServicesModule { @Provides static WallpaperManager provideWallpaperManager(Context context) { return (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); return context.getSystemService(WallpaperManager.class); } @Provides Loading @@ -361,6 +362,12 @@ public class FrameworkServicesModule { return context.getSystemService(WifiManager.class); } @Provides @Singleton static OverlayManager provideOverlayManager(Context context) { return context.getSystemService(OverlayManager.class); } @Provides @Singleton static WindowManager provideWindowManager(Context context) { Loading
packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java→packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +101 −11 Original line number Diff line number Diff line Loading @@ -21,11 +21,18 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.google.android.collect.Lists; import com.google.android.collect.Sets; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; Loading @@ -34,9 +41,19 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.stream.Collectors; class ThemeOverlayManager { /** * Responsible for orchestrating overlays, based on user preferences and other inputs from * {@link ThemeOverlayController}. */ @SysUISingleton public class ThemeOverlayApplier implements Dumpable { private static final String TAG = "ThemeOverlayManager"; private static final boolean DEBUG = false; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting static final String MONET_ACCENT_COLOR_PACKAGE = "com.android.theme.accentcolor.color"; @VisibleForTesting static final String MONET_SYSTEM_PALETTE_PACKAGE = "com.android.theme.systemcolors.color"; @VisibleForTesting static final String ANDROID_PACKAGE = "android"; Loading @@ -46,7 +63,11 @@ class ThemeOverlayManager { static final String SYSUI_PACKAGE = "com.android.systemui"; @VisibleForTesting static final String OVERLAY_CATEGORY_COLOR = "android.theme.customization.accent_color"; static final String OVERLAY_CATEGORY_ACCENT_COLOR = "android.theme.customization.accent_color"; @VisibleForTesting static final String OVERLAY_CATEGORY_SYSTEM_PALETTE = "android.theme.customization.system_palette"; @VisibleForTesting static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font"; @VisibleForTesting Loading @@ -73,24 +94,36 @@ class ThemeOverlayManager { * starts with launcher and grouped by target package. */ static final List<String> THEME_CATEGORIES = Lists.newArrayList( OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ICON_LAUNCHER, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI, OVERLAY_CATEGORY_ICON_SETTINGS, OVERLAY_CATEGORY_ICON_THEME_PICKER); /* Categories that need to applied to the current user as well as the system user. */ /* Categories that need to be applied to the current user as well as the system user. */ @VisibleForTesting static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet( OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI); /** * List of main colors of Monet themes. These are extracted from overlays installed * on the system. */ private final ArrayList<Integer> mMainSystemColors = new ArrayList<>(); /** * Same as above, but providing accent colors instead of a system palette. */ private final ArrayList<Integer> mAccentColors = new ArrayList<>(); /* Allowed overlay categories for each target package. */ private final Map<String, Set<String>> mTargetPackageToCategories = new ArrayMap<>(); /* Target package for each overlay category. */ Loading @@ -100,15 +133,15 @@ class ThemeOverlayManager { private final String mLauncherPackage; private final String mThemePickerPackage; ThemeOverlayManager(OverlayManager overlayManager, Executor executor, String launcherPackage, String themePickerPackage) { public ThemeOverlayApplier(OverlayManager overlayManager, Executor executor, String launcherPackage, String themePickerPackage, DumpManager dumpManager) { mOverlayManager = overlayManager; mExecutor = executor; mLauncherPackage = launcherPackage; mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); mTargetPackageToCategories.put(SYSUI_PACKAGE, Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI)); mTargetPackageToCategories.put(SETTINGS_PACKAGE, Loading @@ -117,7 +150,7 @@ class ThemeOverlayManager { Sets.newHashSet(OVERLAY_CATEGORY_ICON_LAUNCHER)); mTargetPackageToCategories.put(mThemePickerPackage, Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER)); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_ANDROID, ANDROID_PACKAGE); Loading @@ -125,6 +158,54 @@ class ThemeOverlayManager { mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage); collectMonetSystemOverlays(); dumpManager.registerDumpable(TAG, this); } /** * List of accent colors available as Monet overlays. */ List<Integer> getAvailableAccentColors() { return mAccentColors; } /** * List of main system colors available as Monet overlays. */ List<Integer> getAvailableSystemColors() { return mMainSystemColors; } private void collectMonetSystemOverlays() { List<OverlayInfo> androidOverlays = mOverlayManager .getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM); for (OverlayInfo overlayInfo : androidOverlays) { String packageName = overlayInfo.packageName; if (DEBUG) { Log.d(TAG, "Processing overlay " + packageName); } if (OVERLAY_CATEGORY_SYSTEM_PALETTE.equals(overlayInfo.category) && packageName.startsWith(MONET_SYSTEM_PALETTE_PACKAGE)) { try { String color = packageName.replace(MONET_SYSTEM_PALETTE_PACKAGE, ""); mMainSystemColors.add(Integer.parseInt(color, 16)); } catch (NumberFormatException e) { Log.w(TAG, "Invalid package name for overlay " + packageName, e); } } else if (OVERLAY_CATEGORY_ACCENT_COLOR.equals(overlayInfo.category) && packageName.startsWith(MONET_ACCENT_COLOR_PACKAGE)) { try { String color = packageName.replace(MONET_ACCENT_COLOR_PACKAGE, ""); mAccentColors.add(Integer.parseInt(color, 16)); } catch (NumberFormatException e) { Log.w(TAG, "Invalid package name for overlay " + packageName, e); } } else if (DEBUG) { Log.d(TAG, "Unknown overlay: " + packageName + " category: " + overlayInfo.category); } } } /** Loading Loading @@ -184,4 +265,13 @@ class ThemeOverlayManager { } }); } /** * @inherit */ @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mMainSystemColors=" + mMainSystemColors.size()); pw.println("mAccentColors=" + mAccentColors.size()); } }
packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +217 −23 Original line number Diff line number Diff line Loading @@ -15,16 +15,20 @@ */ package com.android.systemui.theme; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; import android.app.ActivityManager; import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.om.OverlayManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; Loading @@ -33,20 +37,32 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.systemui.R; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.systemui.Dumpable; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; import com.google.android.collect.Sets; import org.json.JSONException; import org.json.JSONObject; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; Loading @@ -60,49 +76,76 @@ import javax.inject.Inject; * associated work profiles */ @SysUISingleton public class ThemeOverlayController extends SystemUI { public class ThemeOverlayController extends SystemUI implements Dumpable { private static final String TAG = "ThemeOverlayController"; private static final boolean DEBUG = false; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private ThemeOverlayManager mThemeManager; private UserManager mUserManager; private BroadcastDispatcher mBroadcastDispatcher; // If lock screen wallpaper colors should also be considered when selecting the theme. // Doing this has performance impact, given that overlays would need to be swapped when // the device unlocks. @VisibleForTesting static final boolean USE_LOCK_SCREEN_WALLPAPER = false; private final ThemeOverlayApplier mThemeManager; private final UserManager mUserManager; private final BroadcastDispatcher mBroadcastDispatcher; private final Executor mBgExecutor; private final SecureSettings mSecureSettings; private final Executor mMainExecutor; private final Handler mBgHandler; private final WallpaperManager mWallpaperManager; private final KeyguardStateController mKeyguardStateController; private WallpaperColors mLockColors; private WallpaperColors mSystemColors; // Color extracted from wallpaper, NOT the color used on the overlay private int mMainWallpaperColor = Color.TRANSPARENT; // Color extracted from wallpaper, NOT the color used on the overlay private int mWallpaperAccentColor = Color.TRANSPARENT; // Main system color that maps to an overlay color private int mSystemOverlayColor = Color.TRANSPARENT; // Accent color that maps to an overlay color private int mAccentOverlayColor = Color.TRANSPARENT; @Inject public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDispatcher, @Background Handler bgHandler) { @Background Handler bgHandler, @Main Executor mainExecutor, @Background Executor bgExecutor, ThemeOverlayApplier themeOverlayApplier, SecureSettings secureSettings, WallpaperManager wallpaperManager, UserManager userManager, KeyguardStateController keyguardStateController, DumpManager dumpManager) { super(context); mBroadcastDispatcher = broadcastDispatcher; mUserManager = userManager; mBgExecutor = bgExecutor; mMainExecutor = mainExecutor; mBgHandler = bgHandler; mThemeManager = themeOverlayApplier; mSecureSettings = secureSettings; mWallpaperManager = wallpaperManager; mKeyguardStateController = keyguardStateController; dumpManager.registerDumpable(TAG, this); } @Override public void start() { if (DEBUG) Log.d(TAG, "Start"); mUserManager = mContext.getSystemService(UserManager.class); mThemeManager = new ThemeOverlayManager( mContext.getSystemService(OverlayManager.class), AsyncTask.THREAD_POOL_EXECUTOR, mContext.getString(R.string.launcher_overlayable_package), mContext.getString(R.string.themepicker_overlayable_package)); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); mBroadcastDispatcher.registerReceiverWithHandler(new BroadcastReceiver() { mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); updateThemeOverlays(); } }, filter, mBgHandler, UserHandle.ALL); mContext.getContentResolver().registerContentObserver( }, filter, mBgExecutor, UserHandle.ALL); mSecureSettings.registerContentObserverForUser( Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), false, new ContentObserver(mBgHandler) { @Override public void onChange(boolean selfChange, Collection<Uri> uris, int flags, public void onChange(boolean selfChange, Collection<Uri> collection, int flags, int userId) { if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); if (ActivityManager.getCurrentUser() == userId) { Loading @@ -111,20 +154,147 @@ public class ThemeOverlayController extends SystemUI { } }, UserHandle.USER_ALL); // Upon boot, make sure we have the most up to date colors mBgExecutor.execute(() -> { WallpaperColors lockColors = mWallpaperManager.getWallpaperColors( WallpaperManager.FLAG_LOCK); WallpaperColors systemColor = mWallpaperManager.getWallpaperColors( WallpaperManager.FLAG_SYSTEM); mMainExecutor.execute(() -> { if (USE_LOCK_SCREEN_WALLPAPER) { mLockColors = lockColors; } mSystemColors = systemColor; reevaluateSystemTheme(); }); }); if (USE_LOCK_SCREEN_WALLPAPER) { mKeyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override public void onKeyguardShowingChanged() { if (mLockColors == null) { return; } // It's possible that the user has a lock screen wallpaper. On this case we'll // end up with different colors after unlocking. reevaluateSystemTheme(); } }); } mWallpaperManager.addOnColorsChangedListener((wallpaperColors, which) -> { if (USE_LOCK_SCREEN_WALLPAPER && (which & WallpaperManager.FLAG_LOCK) != 0) { mLockColors = wallpaperColors; if (DEBUG) { Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which); } } if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { mSystemColors = wallpaperColors; if (DEBUG) { Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which); } } reevaluateSystemTheme(); }, null, UserHandle.USER_ALL); } private void reevaluateSystemTheme() { if (mLockColors == null && mSystemColors == null) { Log.w(TAG, "Cannot update theme, colors are null"); return; } WallpaperColors currentColor = mKeyguardStateController.isShowing() && mLockColors != null ? mLockColors : mSystemColors; int mainColor = currentColor.getPrimaryColor().toArgb(); //TODO(b/172860591) implement more complex logic for picking accent color. //For now, picking the secondary should be enough. Color accentCandidate = currentColor.getSecondaryColor(); if (accentCandidate == null) { accentCandidate = currentColor.getTertiaryColor(); } if (accentCandidate == null) { accentCandidate = currentColor.getPrimaryColor(); } if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate.toArgb()) { return; } mMainWallpaperColor = mainColor; mWallpaperAccentColor = accentCandidate.toArgb(); // Let's compare these colors to our finite set of overlays, and then pick an overlay. List<Integer> systemColors = mThemeManager.getAvailableSystemColors(); List<Integer> accentColors = mThemeManager.getAvailableAccentColors(); if (systemColors.size() == 0 || accentColors.size() == 0) { if (DEBUG) { Log.d(TAG, "Cannot apply system theme, palettes are unavailable"); } return; } mSystemOverlayColor = getClosest(systemColors, mMainWallpaperColor); mAccentOverlayColor = getClosest(accentColors, mWallpaperAccentColor); updateThemeOverlays(); } /** * Given a color and a list of candidates, return the candidate that's the most similar to the * given color. */ private static int getClosest(List<Integer> candidates, int color) { float[] hslMain = new float[3]; float[] hslCandidate = new float[3]; ColorUtils.RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hslMain); hslMain[0] /= 360f; float minDistance = Float.MAX_VALUE; int closestColor = Color.TRANSPARENT; for (int candidate: candidates) { ColorUtils.RGBToHSL(Color.red(candidate), Color.green(candidate), Color.blue(candidate), hslCandidate); hslCandidate[0] /= 360f; float sqDistance = squared(hslCandidate[0] - hslMain[0]) + squared(hslCandidate[1] - hslMain[1]) + squared(hslCandidate[2] - hslMain[2]); if (sqDistance < minDistance) { minDistance = sqDistance; closestColor = candidate; } } return closestColor; } private static float squared(float f) { return f * f; } private void updateThemeOverlays() { final int currentUser = ActivityManager.getCurrentUser(); final String overlayPackageJson = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, final String overlayPackageJson = mSecureSettings.getStringForUser( Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, currentUser); if (DEBUG) Log.d(TAG, "updateThemeOverlays: " + overlayPackageJson); boolean hasSystemPalette = false; boolean hasAccentColor = false; final Map<String, String> categoryToPackage = new ArrayMap<>(); if (!TextUtils.isEmpty(overlayPackageJson)) { try { JSONObject object = new JSONObject(overlayPackageJson); for (String category : ThemeOverlayManager.THEME_CATEGORIES) { for (String category : ThemeOverlayApplier.THEME_CATEGORIES) { if (object.has(category)) { if (category.equals(OVERLAY_CATEGORY_ACCENT_COLOR)) { hasAccentColor = true; } else if (category.equals(OVERLAY_CATEGORY_SYSTEM_PALETTE)) { hasSystemPalette = true; } categoryToPackage.put(category, object.getString(category)); } } Loading @@ -132,6 +302,20 @@ public class ThemeOverlayController extends SystemUI { Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e); } } // Let's apply the system palette, but only if it was not overridden by the style picker. if (!hasSystemPalette && mSystemOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, ThemeOverlayApplier.MONET_SYSTEM_PALETTE_PACKAGE + Integer.toHexString(mSystemOverlayColor).toUpperCase()); } // Same for the accent color if (!hasAccentColor && mAccentOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ThemeOverlayApplier.MONET_ACCENT_COLOR_PACKAGE + Integer.toHexString(mAccentOverlayColor).toUpperCase()); } Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser)); for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { if (userInfo.isManagedProfile()) { Loading @@ -140,4 +324,14 @@ public class ThemeOverlayController extends SystemUI { } mThemeManager.applyCurrentUserOverlays(categoryToPackage, userHandles); } @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mLockColors=" + mLockColors); pw.println("mSystemColors=" + mSystemColors); pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor)); pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor)); pw.println("mSystemOverlayColor=" + Integer.toHexString(mSystemOverlayColor)); pw.println("mAccentOverlayColor=" + Integer.toHexString(mAccentOverlayColor)); } }
packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java→packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +32 −21 File changed and moved.Preview size limit exceeded, changes collapsed. Show changes