是否有API可以检测操作系统使用的主题-黑暗或明亮(或其他)? [英] Is there an API to detect which theme the OS is using - dark or light (or other)?

查看:167
本文介绍了是否有API可以检测操作系统使用的主题-黑暗或明亮(或其他)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景



在最新的Android版本中,自Android 8.1以来,该操作系统对主题的支持越来越多。更具体地说,是黑暗主题。



问题



即使在从用户的角度来看,几乎没有为开发人员编写任何内容。



我发现的内容



从Android 8.1开始,Google提供了某种深色主题。如果用户选择使用深色墙纸,则操作系统的某些UI组件将变黑(文章



现在就在附近Android Q,它似乎走得更远,但仍不清楚到什么程度。不知何故,一个名为 Smart Launcher的启动器已经骑上了,可以直接使用其主题(文章



到目前为止,我唯一发现的就是以上文章,并且我正在关注这种主题。



我也知道如何请求操作系统使用动态壁纸更改颜色,但是这似乎在Android Q上正在发生变化,至少根据我尝试时看到的情况(我认为这更多是基于一天中的时间) ,但不确定)。



问题




  1. 是否存在一个API,以获取OS设置使用的颜色?


  2. 是否有某种API可以获取操作系统的主题?从哪个版本开始?


  3. 新的API是否也与夜间模式相关?它们如何一起工作?


  4. 是否有一个不错的API供应用处理所选主题?就是说,如果操作系统具有特定主题,那么当前的应用程序也会这样吗?



解决方案

好的,所以我知道在最新版本的Android(Q)和以前的版本中,它通常是如何工作的。



似乎当操作系统创建WallpaperColors时,它还会生成颜色提示。在函数 WallpaperColors.fromBitmap 中,有一个对 int hints = calculateDarkHints(bitmap); 的调用。 calculateDarkHints 的代码:

  / ** 
*检查图像是否足够明亮,干净以支持浅色文本。
*
* @param source阅读内容。
* @return图像是否支持深色文本。
* /
private static int computeDarkHints(Bitmap source){
if(source == null){
返回0;
}

int []像素=新int [source.getWidth()* source.getHeight()];
double totalLuminance = 0;
final int maxDarkPixels =(int)(pixels.length * MAX_DARK_AREA);
int darkPixels = 0;
source.getPixels(pixels,0 / *偏移* /,source.getWidth(),0 / * x * /,0 / * y * /,
source.getWidth(),source.getHeight ());

//此位图已经调整大小以适合最大允许区域。
//让我们遍历像素,不费吹灰之力!
float [] tmpHsl =新的float [3];
for(int i = 0; i< pixels.length; i ++){
ColorUtils.colorToHSL(pixels [i],tmpHsl);
最终浮动亮度= tmpHsl [2];
final int alpha = Color.alpha(pixels [i]);
//确保我们没有暗像素质量,否则
//将使文本难以辨认。
if(luminance< DARK_PIXEL_LUMINANCE&& alpha!= 0){
darkPixels ++;
}
totalLuminance + =亮度;
}

int提示= 0;
double meanLuminance = totalLuminance / pixel.length;
if(meanLuminance> BRIGHT_IMAGE_MEAN_LUMINANCE&&darkPixels< maxDarkPixels){
提示| = HINT_SUPPORTS_DARK_TEXT;
}
if(meanLuminance< DARK_THEME_MEAN_LUMINANCE){
提示| = HINT_SUPPORTS_DARK_THEME;
}

返回提示;
}

然后搜索 getColorHints WallpaperColors.java 具有的功能,我已经在 StatusBar.java中找到 updateTheme 函数

  WallpaperColors systemColors = mColorExtractor 
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
最终布尔值useDarkTheme = systemColors!= null
&& (systemColors.getColorHints()&WallpaperColors.HINT_SUPPORTS_DARK_THEME)!= 0;

这仅适用于Android 8.1,因为那时主题仅基于墙纸的颜色。在Android 9.0上,用户可以设置它而无需与墙纸建立任何连接。



根据我在Android上看到的内容,这就是我所做的:

 枚举类DarkThemeCheckResult {
DEFAULT_BEFORE_THEMES,LIGHT,DARK,PROBABLY_DARK,PROBABLY_LIGHT,USER_CHOSEN
}

@JvmStatic
fun getIsOsDarkTheme(context:Context):DarkThemeCheckResult {
when {
Build.VERSION.SDK_INT< = Build.VERSION_CODES.O->返回DarkThemeCheckResult.DEFAULT_BEFORE_THEMES
Build.VERSION.SDK_INT< = Build.VERSION_CODES.P-> {
val wallpaperManager = WallpaperManager.getInstance(context)
val wallpaperColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
?:返回DarkThemeCheckResult.UNKNOWN
val primaryColor = wallpaperColors.primaryColor。 toArgb()
val secondaryColor = wallpaperColors.secondaryColor?.toArgb()?:primaryColor
val tertiaryColor = wallpaperColors.tertiaryColor?.toArgb()?:secondaryColor
val bitmap = generateBitmapFromColors(primaryColor, secondaryColor,tertiaryColor)
val darkHints = computeDarkHints(bitmap)
//取自StatusBar.java,在updateTheme中:
val HINT_SUPPORTS_DARK_THEME = 1 shl 1
val useDarkTheme = darkHintME和HINT_SUPPORTS != 0
if(Build.VERSION.SDK_INT == VERSION_CODES.O_MR1)
return if(useDarkTheme)
DarkThemeCheckResult.UNKNOWN_MAYBE_DARK
else DarkThemeCheckResult.UNKNOWN_MAYBE_LIGHT
return if(useDarkTheme)
DarkThemeCheckResult.MOST_PROBABLY_DARK
else DarkThemeCheckResult.MOST_PROBABLY_LIGHT
{
在(context.resources.configuration.uiMode和Configuration.UI_MODE_NIGHT_MASK)时返回{
Configuration.UI_MODE_NIGHT_YES-> DarkThemeCheckResult.DARK
Configuration.UI_MODE_NIGHT_NO-> DarkThemeCheckResult.LIGHT
else-> DarkThemeCheckResult.MOST_PROBABLY_LIGHT
}
}
}
}

fungenerateBitmapFromColors(@ColorInt primaryColor:Int,@ColorInt secondaryColor:Int,@ColorInt tertiaryColor: Int):位图{
val colors = intArrayOf(primaryColor,secondaryColor,tertiaryColor)
val imageSize = 6
val位图= Bitmap.createBitmap(imageSize,1,Bitmap.Config.ARGB_8888)
表示(i在0之前直到imageSize / 2)
bitmap.setPixel(i,0,colors [0])
表示(i在imageSize / 2上直到imageSize / 2 + imageSize / 3)
bitmap.setPixel(i,0,colors [1])$ ​​b $ b for(i in imageSize / 2 + imageSize / 3直到imageSize)
bitmap.setPixel(i,0,colors [2] ])
返回位图
}

我已经设置了各种可能的值,因为在大多数情况下都无法保证。


Background

On recent Android versions, ever since Android 8.1, the OS got more and more support for themes. More specifically dark theme.

The problem

Even though there is a lot of talk about dark mode in the point-of-view for users, there is almost nothing that's written for developers.

What I've found

Starting from Android 8.1, Google provided some sort of dark theme . If the user chooses to have a dark wallpaper, some UI components of the OS would turn black (article here).

In addition, if you developed a live wallpaper app, you could tell the OS which colors it has (3 types of colors), which affected the OS colors too (at least on Vanilla based ROMs and on Google devices). That's why I even made an app that lets you have any wallpaper while still be able to choose the colors (here). This is done by calling notifyColorsChanged and then provide them using onComputeColors

Starting from Android 9.0, it is now possible to choose which theme to have: light, dark or automatic (based on wallpaper) :

And now on the near Android Q , it seems it went further, yet it's still unclear to what extent. Somehow a launcher called "Smart Launcher" has ridden on it, offering to use the theme right on itself (article here). So, if you enable dark mode (manually, as written here) , you get the app's settings screen as such:

The only thing I've found so far is the above articles, and that I'm following this kind of topic.

I also know how to request the OS to change color using the live wallpaper, but this seems to be changing on Android Q, at least according to what I've seen when trying it (I think it's more based on time-of-day, but not sure).

The questions

  1. Is there an API to get which colors the OS is set to use ?

  2. Is there any kind of API to get the theme of the OS ? From which version?

  3. Is the new API somehow related to night mode too? How do those work together?

  4. Is there a nice API for apps to handle the chosen theme? Meaning that if the OS is in certain theme, so will the current app?

解决方案

OK so I got to know how this usually works, on both newest version of Android (Q) and before.

It seems that when the OS creates the WallpaperColors , it also generates color-hints. In the function WallpaperColors.fromBitmap , there is a call to int hints = calculateDarkHints(bitmap); , and this is the code of calculateDarkHints :

/**
 * Checks if image is bright and clean enough to support light text.
 *
 * @param source What to read.
 * @return Whether image supports dark text or not.
 */
private static int calculateDarkHints(Bitmap source) {
    if (source == null) {
        return 0;
    }

    int[] pixels = new int[source.getWidth() * source.getHeight()];
    double totalLuminance = 0;
    final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
    int darkPixels = 0;
    source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
            source.getWidth(), source.getHeight());

    // This bitmap was already resized to fit the maximum allowed area.
    // Let's just loop through the pixels, no sweat!
    float[] tmpHsl = new float[3];
    for (int i = 0; i < pixels.length; i++) {
        ColorUtils.colorToHSL(pixels[i], tmpHsl);
        final float luminance = tmpHsl[2];
        final int alpha = Color.alpha(pixels[i]);
        // Make sure we don't have a dark pixel mass that will
        // make text illegible.
        if (luminance < DARK_PIXEL_LUMINANCE && alpha != 0) {
            darkPixels++;
        }
        totalLuminance += luminance;
    }

    int hints = 0;
    double meanLuminance = totalLuminance / pixels.length;
    if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) {
        hints |= HINT_SUPPORTS_DARK_TEXT;
    }
    if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) {
        hints |= HINT_SUPPORTS_DARK_THEME;
    }

    return hints;
}

Then searching for getColorHints that the WallpaperColors.java has, I've found updateTheme function in StatusBar.java :

    WallpaperColors systemColors = mColorExtractor
            .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
    final boolean useDarkTheme = systemColors != null
            && (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;

This would work only on Android 8.1 , because then the theme was based on the colors of the wallpaper alone. On Android 9.0 , the user can set it without any connection to the wallpaper.

Here's what I've made, according to what I've seen on Android :

enum class DarkThemeCheckResult {
    DEFAULT_BEFORE_THEMES, LIGHT, DARK, PROBABLY_DARK, PROBABLY_LIGHT, USER_CHOSEN
}

@JvmStatic
fun getIsOsDarkTheme(context: Context): DarkThemeCheckResult {
    when {
        Build.VERSION.SDK_INT <= Build.VERSION_CODES.O -> return DarkThemeCheckResult.DEFAULT_BEFORE_THEMES
        Build.VERSION.SDK_INT <= Build.VERSION_CODES.P -> {
            val wallpaperManager = WallpaperManager.getInstance(context)
            val wallpaperColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
                    ?: return DarkThemeCheckResult.UNKNOWN
            val primaryColor = wallpaperColors.primaryColor.toArgb()
            val secondaryColor = wallpaperColors.secondaryColor?.toArgb() ?: primaryColor
            val tertiaryColor = wallpaperColors.tertiaryColor?.toArgb() ?: secondaryColor
            val bitmap = generateBitmapFromColors(primaryColor, secondaryColor, tertiaryColor)
            val darkHints = calculateDarkHints(bitmap)
            //taken from StatusBar.java , in updateTheme :
            val HINT_SUPPORTS_DARK_THEME = 1 shl 1
            val useDarkTheme = darkHints and HINT_SUPPORTS_DARK_THEME != 0
            if (Build.VERSION.SDK_INT == VERSION_CODES.O_MR1)
                return if (useDarkTheme)
                    DarkThemeCheckResult.UNKNOWN_MAYBE_DARK
                else DarkThemeCheckResult.UNKNOWN_MAYBE_LIGHT
            return if (useDarkTheme)
                DarkThemeCheckResult.MOST_PROBABLY_DARK
            else DarkThemeCheckResult.MOST_PROBABLY_LIGHT
        }
        else -> {
            return when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
                Configuration.UI_MODE_NIGHT_YES -> DarkThemeCheckResult.DARK
                Configuration.UI_MODE_NIGHT_NO -> DarkThemeCheckResult.LIGHT
                else -> DarkThemeCheckResult.MOST_PROBABLY_LIGHT
            }
        }
    }
}

fun generateBitmapFromColors(@ColorInt primaryColor: Int, @ColorInt secondaryColor: Int, @ColorInt tertiaryColor: Int): Bitmap {
    val colors = intArrayOf(primaryColor, secondaryColor, tertiaryColor)
    val imageSize = 6
    val bitmap = Bitmap.createBitmap(imageSize, 1, Bitmap.Config.ARGB_8888)
    for (i in 0 until imageSize / 2)
        bitmap.setPixel(i, 0, colors[0])
    for (i in imageSize / 2 until imageSize / 2 + imageSize / 3)
        bitmap.setPixel(i, 0, colors[1])
    for (i in imageSize / 2 + imageSize / 3 until imageSize)
        bitmap.setPixel(i, 0, colors[2])
    return bitmap
}

I've set the various possible values, because in most of those cases nothing is guaranteed.

这篇关于是否有API可以检测操作系统使用的主题-黑暗或明亮(或其他)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆