是否有API可以检测操作系统使用的主题-黑暗还是明亮(或其他)?

时间:2019-04-21 21:44:12

标签: android themes android-9.0-pie android-8.1-oreo androidq

背景

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

问题

即使在用户角度上有很多关于暗模式的讨论,也几乎没有为开发人员编写的内容。

我发现的东西

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

此外,如果您开发了动态壁纸应用程序,则可以告诉操作系统它具有哪种颜色(三种颜色),这也影响了操作系统的颜色(至少在基于Vanilla的ROM和Google设备上)。这就是为什么我什至制作了一款可以让您拥有任何墙纸,同时仍然可以选择颜色(here)的应用程序的原因。可以通过调用notifyColorsChanged,然后使用onComputeColors

来提供。

从Android 9.0开始,现在可以选择要使用的主题:浅色,深色或自动(基于墙纸):

enter image description here

现在在即将到来的Android Q上,它似乎走得更远,但尚不清楚到什么程度。不知何故,一个名为“ Smart Launcher”的启动器已经骑上了,可以直接使用其主题(第here条)。因此,如果启用暗模式(通常以here表示),则会显示该应用的设置屏幕,如下所示:

enter image description here

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

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

问题

  1. 是否有一个API可以获取设置为OS使用的颜色?

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

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

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

5 个答案:

答案 0 :(得分:10)

一种简单的Kotlin方法来回答Charles Annic的问题:

const [wallets, setWallets] = useState({});
async function fetchData() {
const res = await fetch('https://api.streetcred.id/custodian/v1/api/wallets', {
method: 'GET',
headers: {
Authorization: 'Bearer ',
XStreetcredSubscriptionKey: '',
Accept: 'application/json',
'Content-Type': 'application/json',
}
});
res
.json()
.then(res => setWallets(res))
.catch(err => setErrors(err));
}

useEffect(() => {
fetchData();
}, []);  



答案 1 :(得分:3)

Google刚刚在I / O 2019结束时发布了有关黑暗主题的文档,here

为了管理深色主题,您必须首先使用材料组件库的最新版本:"com.google.android.material:material:1.1.0-alpha06"

根据系统主题更改应用程序主题

要使应用程序根据系统切换到深色主题,只需要一个主题。为此,主题必须具有Theme.MaterialComponents.DayNight作为父级。

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
    ...
</style>

确定当前系统主题

要了解系统当前是否处于深色主题,可以实施以下代码:

switch (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) {
    case Configuration.UI_MODE_NIGHT_YES:
        …
        break;
    case Configuration.UI_MODE_NIGHT_NO:
        …
        break; 
}

有关主题更改的通知

我认为不可能在主题更改时实现回调通知,但这不是问题。实际上,当系统更改主题时,将自动重新创建活动。这样,在活动开始时放置先前的代码就足够了。

它可以从哪个版本的Android SDK运行?

我无法在Android SDK版本28的Android Pie上使用它。因此,我认为这仅适用于SDK的下一版本,该版本将与Q版本29一起启动。

结果

result

答案 2 :(得分:1)

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

似乎当操作系统创建WallpaperColors时,它还会生成颜色提示。在功能WallpaperColors.fromBitmap中,有一个对int hints = calculateDarkHints(bitmap);的调用,这是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;
}

然后搜索getColorHints具有的WallpaperColors.java,我在updateTheme中发现了StatusBar.java的功能:

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

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

根据我在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
}

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

答案 3 :(得分:0)

我认为Google在Android Q中应用深色和浅色主题的基础是电池。

也许是DayNight theme

  

然后,您需要在应用中启用该功能。你这样做   调用AppCompatDelegate.setDefaultNightMode(),它采用以下一种   以下值:

     
      
  • MODE_NIGHT_NO。始终使用白天(浅色)主题。
  •   
  • MODE_NIGHT_YES。始终使用夜晚(黑暗)主题。
  •   
  • MODE_NIGHT_FOLLOW_SYSTEM(默认值)。此设置遵循系统的设置,在Android Pie及更高版本上为系统设置   (有关详情,请参见下文)。
  •   
  • MODE_NIGHT_AUTO_BATTERY。启用“省电模式”后,该指示灯将变暗,否则亮起。 in新进   v1.1.0-alpha03。
  •   
  • MODE_NIGHT_AUTO_TIME和MODE_NIGHT_AUTO。白天/晚上之间根据一天中的时间变化。
  •   

答案 4 :(得分:0)

我想补充 Vitor Hugo Schwaab 的回答,您可以进一步分解代码并使用 isNightModeActive

resources.configuration.isNightModeActive

resources
configuration
isNightModeActive