1 /*
2 * Copyright (C) 2022 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
17 package com.android.settingslib.spa.framework.theme
18
19 import android.content.Context
20 import android.os.Build
21 import androidx.annotation.VisibleForTesting
22 import androidx.compose.runtime.Composable
23 import androidx.compose.runtime.remember
24 import androidx.compose.runtime.staticCompositionLocalOf
25 import androidx.compose.ui.graphics.Color
26 import androidx.compose.ui.platform.LocalContext
27
28 data class SettingsColorScheme(
29 val background: Color = Color.Unspecified,
30 val categoryTitle: Color = Color.Unspecified,
31 val surface: Color = Color.Unspecified,
32 val surfaceHeader: Color = Color.Unspecified,
33 val secondaryText: Color = Color.Unspecified,
34 val primaryContainer: Color = Color.Unspecified,
35 val onPrimaryContainer: Color = Color.Unspecified,
36 val spinnerHeaderContainer: Color = Color.Unspecified,
37 val onSpinnerHeaderContainer: Color = Color.Unspecified,
38 val spinnerItemContainer: Color = Color.Unspecified,
39 val onSpinnerItemContainer: Color = Color.Unspecified,
40 )
41
<lambda>null42 internal val LocalColorScheme = staticCompositionLocalOf { SettingsColorScheme() }
43
44 @Composable
settingsColorSchemenull45 internal fun settingsColorScheme(isDarkTheme: Boolean): SettingsColorScheme {
46 val context = LocalContext.current
47 return remember(isDarkTheme) {
48 when {
49 Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
50 if (isDarkTheme) dynamicDarkColorScheme(context)
51 else dynamicLightColorScheme(context)
52 }
53 isDarkTheme -> darkColorScheme()
54 else -> lightColorScheme()
55 }
56 }
57 }
58
59 /**
60 * Creates a light dynamic color scheme.
61 *
62 * Use this function to create a color scheme based off the system wallpaper. If the developer
63 * changes the wallpaper this color scheme will change accordingly. This dynamic scheme is a
64 * light theme variant.
65 *
66 * @param context The context required to get system resource data.
67 */
68 @VisibleForTesting
dynamicLightColorSchemenull69 internal fun dynamicLightColorScheme(context: Context): SettingsColorScheme {
70 val tonalPalette = dynamicTonalPalette(context)
71 return SettingsColorScheme(
72 background = tonalPalette.neutral95,
73 categoryTitle = tonalPalette.primary40,
74 surface = tonalPalette.neutral99,
75 surfaceHeader = tonalPalette.neutral90,
76 secondaryText = tonalPalette.neutralVariant30,
77 primaryContainer = tonalPalette.primary90,
78 onPrimaryContainer = tonalPalette.neutral10,
79 spinnerHeaderContainer = tonalPalette.primary90,
80 onSpinnerHeaderContainer = tonalPalette.neutral10,
81 spinnerItemContainer = tonalPalette.secondary90,
82 onSpinnerItemContainer = tonalPalette.neutralVariant30,
83 )
84 }
85
86 /**
87 * Creates a dark dynamic color scheme.
88 *
89 * Use this function to create a color scheme based off the system wallpaper. If the developer
90 * changes the wallpaper this color scheme will change accordingly. This dynamic scheme is a dark
91 * theme variant.
92 *
93 * @param context The context required to get system resource data.
94 */
95 @VisibleForTesting
dynamicDarkColorSchemenull96 internal fun dynamicDarkColorScheme(context: Context): SettingsColorScheme {
97 val tonalPalette = dynamicTonalPalette(context)
98 return SettingsColorScheme(
99 background = tonalPalette.neutral10,
100 categoryTitle = tonalPalette.primary90,
101 surface = tonalPalette.neutral20,
102 surfaceHeader = tonalPalette.neutral30,
103 secondaryText = tonalPalette.neutralVariant80,
104 primaryContainer = tonalPalette.secondary90,
105 onPrimaryContainer = tonalPalette.neutral10,
106 spinnerHeaderContainer = tonalPalette.primary90,
107 onSpinnerHeaderContainer = tonalPalette.neutral10,
108 spinnerItemContainer = tonalPalette.secondary90,
109 onSpinnerItemContainer = tonalPalette.neutralVariant30,
110 )
111 }
112
113 @VisibleForTesting
darkColorSchemenull114 internal fun darkColorScheme(): SettingsColorScheme {
115 val tonalPalette = tonalPalette()
116 return SettingsColorScheme(
117 background = tonalPalette.neutral10,
118 categoryTitle = tonalPalette.primary90,
119 surface = tonalPalette.neutral20,
120 surfaceHeader = tonalPalette.neutral30,
121 secondaryText = tonalPalette.neutralVariant80,
122 primaryContainer = tonalPalette.secondary90,
123 onPrimaryContainer = tonalPalette.neutral10,
124 spinnerHeaderContainer = tonalPalette.primary90,
125 onSpinnerHeaderContainer = tonalPalette.neutral10,
126 spinnerItemContainer = tonalPalette.secondary90,
127 onSpinnerItemContainer = tonalPalette.neutralVariant30,
128 )
129 }
130
131 @VisibleForTesting
lightColorSchemenull132 internal fun lightColorScheme(): SettingsColorScheme {
133 val tonalPalette = tonalPalette()
134 return SettingsColorScheme(
135 background = tonalPalette.neutral95,
136 categoryTitle = tonalPalette.primary40,
137 surface = tonalPalette.neutral99,
138 surfaceHeader = tonalPalette.neutral90,
139 secondaryText = tonalPalette.neutralVariant30,
140 primaryContainer = tonalPalette.primary90,
141 onPrimaryContainer = tonalPalette.neutral10,
142 spinnerHeaderContainer = tonalPalette.primary90,
143 onSpinnerHeaderContainer = tonalPalette.neutral10,
144 spinnerItemContainer = tonalPalette.secondary90,
145 onSpinnerItemContainer = tonalPalette.neutralVariant30,
146 )
147 }
148