I. Introduction to System Topics
Android P has been out for a long time. Today, in the process of using Android P, there is a Device Theme under Dislpay.
As follows
It can be seen that one has three options. After using it, he runs the user to set the mobile phone theme. Unlike app theme, the theme settings here can be queried by the system and all applications. There are two dark and bright states for the system theme. The three options in the above settings represent the following meanings:
- Automatic (base on wallpaper): The system checks whether the currently set wallpaper is dark or bright, and then decides more about the setting theme.
- Light: Set the bright theme
- Dark: Setting Dark Themes
Set the highlight theme:
Set dark themes:
From the source code, it is found that launche and SystemUI on the system monitor the status of the system theme, then display the theme of the system. If other app s need to monitor the change of the theme in real time according to the current theme of the system, they also need to monitor the change of the theme in real time.
2. How to realize the system theme
Personal app s listen to system topics, directly using Wallpaper Manager, addOnColors ChangedListener method, through the listener callback
The steps are as follows:
Step 1: Get Wallpaper Manager
if (wallpaperManager == null) { wallpaperManager = WallpaperManager.getInstance(context.getApplicationContext()); }
The invocation of Wallpaper Manager is actually getSystem Service
public static WallpaperManager getInstance(Context context) { return (WallpaperManager)context.getSystemService( Context.WALLPAPER_SERVICE); }
Step 2: Monitoring Thematic Changes
This callback method will be triggered during the topic switch. Here is the method called after the trigger.
WallpaperManager.OnColorsChangedListener onColorsChangedListener = new WallpaperManager.OnColorsChangedListener(){ @Override public void onColorsChanged(WallpaperColors colors, int which) {//1 if (WallpaperManager.FLAG_SYSTEM == which) {//2 updateTheme(colors); } } }; wallpaperManager.addOnColorsChangedListener(onColorsChangedListene, null);//3 WallpaperColors colorsSystem = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM); updateTheme(colorsSystem);//4
- 3. The second parameter is handler. If null is selected, the hander corresponding to the looper of the main thread is represented.
- Returns two parameters Wallpaper Colors, which; Wallpaper Colors can determine whether it belongs to a bright or dark topic, and which indicates whether the lock screen (Wallpaper Manager. FLAG_LOCK) or the system (Wallpaper Manager. FLAG_SYSTEM), because the theme setting has an Automatic (base on wallpaper) option, and the lock screen wallpaper and desktop wallpaper can be different, so Wallpaper Manager. FLAG_SYSTEM is a configuration theme, such as 2.
- Four, because theme callbacks occur only when the theme is switched, the app needs to go back to the theme when it starts running.
Because the system theme is only available on android O, if you want the lowest version of app to be below android O, please make a judgement.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) { }
Step 3: Judging whether the topic is bright or dark
Here's a code snippet for SystemUI to determine a topic
public void updateTheme(WallpaperColors colors) { final boolean useDarkTheme = colors != null && (colors.getColorHints() & HINT_SUPPORTS_DARK_THEME) != 0; }
Printing log s shows that colors.getColorHints() is 4 at light and 6 at dark, while HINT_SUPPORTS_DARK_THEME=== 2
Many methods of this class, WallpaperColors, are modified by hide, including the getColorHints method, which makes non-system calls impossible, as shown in the figure.
Android P then restricts the invocation of the system API through reflection. It's no use calling this method directly, but the toString method of this class prints this value out again.
We can intercept this value and then make a judgment, so we can modify the judgment above.
public void updateTheme(WallpaperColors colors) { final boolean useDarkTheme = colors != null && (getColorHints(colors) & HINT_SUPPORTS_DARK_THEME) != 0; } private int getColorHints(WallpaperColors colors) { String str = colors.toString(); int index = str.lastIndexOf("h: "); String val = str.substring(index + 3, str.length() - 1); if (TextUtils.isDigitsOnly(val)) { return Integer.valueOf(val); } Log.e(TAG, "can not get getColorHints!!"); return 0; }
The reform is not elegant and can only be used.
Step 4: Exit listening
wallpaperManager.removeOnColorsChangedListener(onColorsChangedListener);
3. Introduce the WallpaperColors class
Wallpaper Colors Source Interpretation: Provides information about the colors of a wallpaper. This record wallpaper color
WallpaperColors has several interesting approaches
Method | Return type | Significance |
---|---|---|
getPrimaryColor() | Color | Get the most visually representative color of wallpaper. |
getSecondaryColor() | Color | The second most outstanding color of wallpaper was obtained. |
getTertiaryColor() | Color | The third most outstanding color of wallpaper was obtained. |
getMainColors() | List of Color | A list of the most outstanding colors sorted by importance. |
getColorHints() | int | Combination of wallpaper color tips. (dark or bright) |
calculateDarkHints(Bitmap source) | int | Check if the image is bright enough based on the incoming Bitmap |
fromBitmap(@NonNull Bitmap bitmap) | WallpaperColors | Based on the incoming Bitmap, the check image is returned to WallpaperColors |
fromDrawable(Drawable drawable) | WallpaperColors | Based on the incoming Drawable, check that the image returns to WallpaperColors |
4. Share an encapsulated class
import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.Context; import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; import java.util.List; public final class ColorExtractor { public interface ColorsChange { public void themeChange(boolean useDarkTheme); } private static ColorExtractor colorExtractor; private static final String TAG = "ColorExtractor"; private WallpaperManager wallpaperManager; private static final int HINT_SUPPORTS_DARK_THEME = 1 << 1; private WallpaperManager.OnColorsChangedListener onColorsChangedListene; private List<ColorsChange> listens; private ColorExtractor() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) { onColorsChangedListene = new WallpaperManager.OnColorsChangedListener() { @Override public void onColorsChanged(WallpaperColors colors, int which) { if (which == WallpaperManager.FLAG_SYSTEM) { final boolean useDarkTheme = colors != null && (getColorHints(colors) & HINT_SUPPORTS_DARK_THEME) != 0; if (listens != null && listens.size() > 0) { for (ColorsChange colorsChange : listens) { colorsChange.themeChange(useDarkTheme); } } } } }; } listens = new ArrayList<>(); } public static ColorExtractor getInstance() { if (colorExtractor == null) { colorExtractor = new ColorExtractor(); } return colorExtractor; } public void startListen(Context context) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) { if (wallpaperManager == null) { wallpaperManager = WallpaperManager.getInstance(context.getApplicationContext()); } WallpaperColors colorsSystem = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM); final boolean useDarkTheme = colorsSystem != null && (getColorHints(colorsSystem) & HINT_SUPPORTS_DARK_THEME) != 0; if (listens != null && listens.size() > 0) { for (ColorsChange colorsChange : listens) { colorsChange.themeChange(useDarkTheme); } } wallpaperManager.addOnColorsChangedListener(onColorsChangedListene, null); } } public void addThemeChange(ColorsChange colorsChange) { if (!listens.contains(colorsChange)) { listens.add(colorsChange); } } public void removeThemeChange(ColorsChange colorsChange) { if (listens.contains(colorsChange)) { listens.remove(colorsChange); } } public void stopListen() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) { wallpaperManager.removeOnColorsChangedListener(onColorsChangedListene); } if (listens != null) { listens.clear(); } } } private int getColorHints(WallpaperColors colors) { String str = colors.toString(); int index = str.lastIndexOf("h: "); String val = str.substring(index + 3, str.length() - 1); if (TextUtils.isDigitsOnly(val)) { return Integer.valueOf(val); } Log.e(TAG, "can not get getColorHints!!"); return 0; } }
Usage method:
//Singleton instantiation ColorExtractor colorExtractor = ColorExtractor.getInstance(); //Register listener events, because Wallpaper Manager calls the initial theme when registering, so in order to get the initial value, the listener events are registered before addThemeChange begins. colorExtractor.addThemeChange(new ColorExtractor.ColorsChange() { @Override public void themeChange(boolean useDarkTheme) { Log.i(TAG, "useDarkTheme[" + useDarkTheme + "]"); } }); //Start listening; generally in onStart (or onCreate) methods colorExtractor.startListen(this); //End listening, generally in onStop (or onDestory) method colorExtractor.stopListen();