• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. */
isLightnull33 internal 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