Есть ли API для определения, какую тему использует ОС - темная или светлая (или другая)? - PullRequest
5 голосов
/ 22 апреля 2019

Фон

В последних версиях Android, начиная с Android 8.1, ОС все больше и больше поддерживает темы. Точнее темная тема.

Проблема

Даже несмотря на то, что много разговоров о темном режиме с точки зрения пользователей, для разработчиков почти ничего не написано.

Что я нашел

Начиная с Android 8.1, Google предоставил какую-то темную тему. Если пользователь выберет темные обои, некоторые компоненты пользовательского интерфейса ОС станут черными (статья здесь ).

Кроме того, если вы разработали приложение для живых обоев, вы можете указать ОС, какие цвета у него есть (3 типа цветов), что также влияет на цвета ОС (по крайней мере, на ПЗУ на основе Vanilla и на устройствах Google). Вот почему я даже создал приложение, которое позволяет вам иметь любые обои, но при этом можно выбирать цвета ( здесь ). Это можно сделать, вызвав notifyColorsChanged и затем предоставив их, используя onComputeColors

Начиная с Android 9.0, теперь можно выбирать, какую тему иметь: светлую, темную или автоматическую (на основе обоев):

enter image description here

А теперь на ближайшем Android Q, кажется, он пошел дальше, но пока неясно, в какой степени. Каким-то образом на нем появился лаунчер под названием «Smart Launcher», предлагающий использовать тему прямо над собой (статья здесь ). Таким образом, если вы включите темный режим (вручную, как написано здесь ), вы получите экран настроек приложения следующим образом:

enter image description here

Единственное, что я до сих пор нашел, - это вышеприведенные статьи, и я следую за этим видом темы.

Я также знаю, как запросить у ОС изменение цвета с помощью живых обоев, но, похоже, это меняется на Android Q, по крайней мере, в соответствии с тем, что я видел при попытке (я думаю, что это больше зависит от времени - сегодня, но не уверен).

Вопросы

  1. Существует ли API для получения цветов, которые ОС будет использовать?

  2. Есть ли какой-нибудь API для получения темы ОС? С какой версии?

  3. Новый API тоже как-то связан с ночным режимом? Как они работают вместе?

  4. Есть ли хороший API для приложений для обработки выбранной темы? Это означает, что, если ОС находится в определенной теме, будет ли текущее приложение?

Ответы [ 3 ]

3 голосов
/ 08 мая 2019

Google только что опубликовал документацию по темной теме в конце ввода-вывода 2019, здесь .

Чтобы управлять темной темой, вы должны сначала использовать самую последнююверсия библиотеки компонентов материалов: "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 Pie с версией 28 Android SDK.Поэтому я предполагаю, что это работает только со следующей версии SDK, которая будет запущена с Q, версия 29.

Результат

result

1 голос
/ 09 июня 2019

ОК, так что я узнал, как это обычно работает, как на самой новой версии Android (Q), так и раньше.

Кажется, что когда ОС создает обои ColorColors, она также генерирует цветовые подсказки.В функции 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, I 'мы нашли 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
}

I 'Мы устанавливаем различные возможные значения, потому что в большинстве случаев ничего не гарантируется.

0 голосов
/ 22 апреля 2019

Я думаю, что Google использует уровень заряда батареи для применения темных и светлых тем в Android Q.

Может быть Тема DayNight ?

Вам нужночтобы включить функцию в вашем приложении.Это можно сделать, вызвав AppCompatDelegate.setDefaultNightMode (), который принимает одно из следующих значений:

  • MODE_NIGHT_NO.Всегда используйте дневную (светлую) тему.
  • MODE_NIGHT_YES.Всегда используйте ночные (темные) темы.
  • MODE_NIGHT_FOLLOW_SYSTEM (по умолчанию).Этот параметр следует настройке системы, которая на Android Pie и выше является системной настройкой (подробнее об этом ниже).
  • MODE_NIGHT_AUTO_BATTERY.Меняется на темный, когда на устройстве включена функция «Экономия заряда», в противном случае светится.✨Новое в v1.1.0-alpha03.
  • MODE_NIGHT_AUTO_TIME & MODE_NIGHT_AUTO.Изменения между днем ​​и ночью в зависимости от времени суток.
...