1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.wm.shell.windowdecor.common 17 18 import android.annotation.ColorInt 19 import android.annotation.IntRange 20 import android.app.ActivityManager.RunningTaskInfo 21 import android.content.Context 22 import android.content.res.Configuration 23 import android.content.res.Configuration.UI_MODE_NIGHT_MASK 24 import android.graphics.Color 25 import androidx.compose.material3.ColorScheme 26 import androidx.compose.material3.dynamicDarkColorScheme 27 import androidx.compose.material3.dynamicLightColorScheme 28 29 /** The theme of a window decoration. */ 30 internal enum class Theme { LIGHT, DARK } 31 32 /** Whether a [Theme] is light. */ isLightnull33internal fun Theme.isLight(): Boolean = this == Theme.LIGHT 34 35 /** Whether a [Theme] is dark. */ 36 internal fun Theme.isDark(): Boolean = this == Theme.DARK 37 38 /** Returns a copy of the color with its [alpha] component replaced with the given value. */ 39 @ColorInt 40 internal fun @receiver:ColorInt Int.withAlpha(@IntRange(from = 0, to = 255) alpha: Int): Int = 41 Color.argb( 42 alpha, 43 Color.red(this), 44 Color.green(this), 45 Color.blue(this) 46 ) 47 48 /** Common opacity values used in window decoration views. */ 49 const val OPACITY_100 = 255 50 const val OPACITY_11 = 28 51 const val OPACITY_12 = 31 52 const val OPACITY_15 = 38 53 const val OPACITY_40 = 102 54 const val OPACITY_55 = 140 55 const val OPACITY_60 = 153 56 const val OPACITY_65 = 166 57 58 /** 59 * Utility class for determining themes based on system settings and app's [RunningTaskInfo]. 60 */ 61 internal class DecorThemeUtil(private val context: Context) { 62 private val lightColors = dynamicLightColorScheme(context) 63 private val darkColors = dynamicDarkColorScheme(context) 64 65 private val systemTheme: Theme 66 get() = if ((context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK) == 67 Configuration.UI_MODE_NIGHT_YES) { 68 Theme.DARK 69 } else { 70 Theme.LIGHT 71 } 72 73 /** 74 * Returns the [Theme] used by the app with the given [RunningTaskInfo]. 75 */ 76 fun getAppTheme(task: RunningTaskInfo): Theme { 77 // TODO: use app's uiMode to find its actual light/dark value. It needs to be added to the 78 // TaskInfo/TaskDescription. 79 val backgroundColor = task.taskDescription?.backgroundColor ?: return systemTheme 80 return if (Color.valueOf(backgroundColor).luminance() < 0.5) { 81 Theme.DARK 82 } else { 83 Theme.LIGHT 84 } 85 } 86 87 /** 88 * Returns the [ColorScheme] to use to style window decorations based on the given 89 * [RunningTaskInfo]. 90 */ 91 fun getColorScheme(task: RunningTaskInfo): ColorScheme = when (getAppTheme(task)) { 92 Theme.LIGHT -> lightColors 93 Theme.DARK -> darkColors 94 } 95 96 fun getColorScheme(isDarkMode: Boolean): ColorScheme = 97 if (isDarkMode) darkColors else lightColors 98 } 99