Skip to content

Commit cfbf782

Browse files
authored
Merge pull request #542 from android/as/dynamic-color-option
Add dynamic color option in settings for API >=32
2 parents df2f312 + f28d7f0 commit cfbf782

File tree

27 files changed

+411
-71
lines changed

27 files changed

+411
-71
lines changed

app/src/main/java/com/google/samples/apps/nowinandroid/MainActivity.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ class MainActivity : ComponentActivity() {
106106

107107
NiaTheme(
108108
darkTheme = darkTheme,
109-
androidTheme = shouldUseAndroidTheme(uiState)
109+
androidTheme = shouldUseAndroidTheme(uiState),
110+
disableDynamicTheming = shouldDisableDynamicTheming(uiState)
110111
) {
111112
NiaApp(
112113
networkMonitor = networkMonitor,
@@ -141,6 +142,17 @@ private fun shouldUseAndroidTheme(
141142
}
142143
}
143144

145+
/**
146+
* Returns `true` if the dynamic color is disabled, as a function of the [uiState].
147+
*/
148+
@Composable
149+
private fun shouldDisableDynamicTheming(
150+
uiState: MainActivityUiState,
151+
): Boolean = when (uiState) {
152+
Loading -> false
153+
is Success -> !uiState.userData.useDynamicColor
154+
}
155+
144156
/**
145157
* Returns `true` if dark theme should be used, as a function of the [uiState] and the
146158
* current system context.

core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class OfflineFirstUserDataRepository @Inject constructor(
4545
override suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) =
4646
niaPreferencesDataSource.setDarkThemeConfig(darkThemeConfig)
4747

48+
override suspend fun setDynamicColorPreference(useDynamicColor: Boolean) =
49+
niaPreferencesDataSource.setDynamicColorPreference(useDynamicColor)
50+
4851
override suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) =
4952
niaPreferencesDataSource.setShouldHideOnboarding(shouldHideOnboarding)
5053
}

core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ interface UserDataRepository {
5353
*/
5454
suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig)
5555

56+
/**
57+
* Sets the preferred dynamic color config.
58+
*/
59+
suspend fun setDynamicColorPreference(useDynamicColor: Boolean)
60+
5661
/**
5762
* Sets whether the user has completed the onboarding process.
5863
*/

core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/fake/FakeUserDataRepository.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class FakeUserDataRepository @Inject constructor(
5555
niaPreferencesDataSource.setDarkThemeConfig(darkThemeConfig)
5656
}
5757

58+
override suspend fun setDynamicColorPreference(useDynamicColor: Boolean) {
59+
niaPreferencesDataSource.setDynamicColorPreference(useDynamicColor)
60+
}
61+
5862
override suspend fun setShouldHideOnboarding(shouldHideOnboarding: Boolean) {
5963
niaPreferencesDataSource.setShouldHideOnboarding(shouldHideOnboarding)
6064
}

core/data/src/test/java/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class OfflineFirstUserDataRepositoryTest {
6060
followedTopics = emptySet(),
6161
themeBrand = ThemeBrand.DEFAULT,
6262
darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM,
63+
useDynamicColor = false,
6364
shouldHideOnboarding = false
6465
),
6566
subject.userData.first()
@@ -170,6 +171,26 @@ class OfflineFirstUserDataRepositoryTest {
170171
)
171172
}
172173

174+
@Test
175+
fun offlineFirstUserDataRepository_set_dynamic_color_delegates_to_nia_preferences() =
176+
runTest {
177+
subject.setDynamicColorPreference(true)
178+
179+
assertEquals(
180+
true,
181+
subject.userData
182+
.map { it.useDynamicColor }
183+
.first()
184+
)
185+
assertEquals(
186+
true,
187+
niaPreferencesDataSource
188+
.userData
189+
.map { it.useDynamicColor }
190+
.first()
191+
)
192+
}
193+
173194
@Test
174195
fun offlineFirstUserDataRepository_set_dark_theme_config_delegates_to_nia_preferences() =
175196
runTest {

core/datastore/src/main/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.map
2929
class NiaPreferencesDataSource @Inject constructor(
3030
private val userPreferences: DataStore<UserPreferences>
3131
) {
32-
3332
val userData = userPreferences.data
3433
.map {
3534
UserData(
@@ -52,6 +51,7 @@ class NiaPreferencesDataSource @Inject constructor(
5251
DarkThemeConfig.LIGHT
5352
DarkThemeConfigProto.DARK_THEME_CONFIG_DARK -> DarkThemeConfig.DARK
5453
},
54+
useDynamicColor = it.useDynamicColor,
5555
shouldHideOnboarding = it.shouldHideOnboarding
5656
)
5757
}
@@ -98,6 +98,14 @@ class NiaPreferencesDataSource @Inject constructor(
9898
}
9999
}
100100

101+
suspend fun setDynamicColorPreference(useDynamicColor: Boolean) {
102+
userPreferences.updateData {
103+
it.copy {
104+
this.useDynamicColor = useDynamicColor
105+
}
106+
}
107+
}
108+
101109
suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) {
102110
userPreferences.updateData {
103111
it.copy {

core/datastore/src/main/proto/com/google/samples/apps/nowinandroid/data/user_preferences.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,6 @@ message UserPreferences {
4545
DarkThemeConfigProto dark_theme_config = 17;
4646

4747
bool should_hide_onboarding = 18;
48+
49+
bool use_dynamic_color = 19;
4850
}

core/datastore/src/test/java/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,15 @@ class NiaPreferencesDataSourceTest {
7777
// Then: onboarding should be shown again
7878
assertFalse(subject.userData.first().shouldHideOnboarding)
7979
}
80+
81+
@Test
82+
fun shouldUseDynamicColorFalseByDefault() = runTest {
83+
assertFalse(subject.userData.first().useDynamicColor)
84+
}
85+
86+
@Test
87+
fun userShouldUseDynamicColorIsTrueWhenSet() = runTest {
88+
subject.setDynamicColorPreference(true)
89+
assertTrue(subject.userData.first().useDynamicColor)
90+
}
8091
}

core/designsystem/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ android {
3131

3232
dependencies {
3333
implementation(libs.androidx.core.ktx)
34+
implementation(libs.coil.kt.compose)
3435
api(libs.androidx.compose.foundation)
3536
api(libs.androidx.compose.foundation.layout)
3637
api(libs.androidx.compose.material.iconsExtended)
@@ -41,4 +42,4 @@ dependencies {
4142
api(libs.androidx.compose.runtime)
4243
lintPublish(project(":lint"))
4344
androidTestImplementation(project(":core:testing"))
44-
}
45+
}

core/designsystem/src/androidTest/java/com/google/samples/apps/nowinandroid/core/designsystem/ThemeTest.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightAndroid
3838
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightDefaultColorScheme
3939
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalBackgroundTheme
4040
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
41+
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme
4142
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
43+
import com.google.samples.apps.nowinandroid.core.designsystem.theme.TintTheme
4244
import kotlin.test.assertEquals
4345
import org.junit.Rule
4446
import org.junit.Test
@@ -70,6 +72,8 @@ class ThemeTest {
7072
assertEquals(gradientColors, LocalGradientColors.current)
7173
val backgroundTheme = defaultBackgroundTheme(colorScheme)
7274
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
75+
val tintTheme = defaultTintTheme()
76+
assertEquals(tintTheme, LocalTintTheme.current)
7377
}
7478
}
7579
}
@@ -88,6 +92,8 @@ class ThemeTest {
8892
assertEquals(gradientColors, LocalGradientColors.current)
8993
val backgroundTheme = defaultBackgroundTheme(colorScheme)
9094
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
95+
val tintTheme = defaultTintTheme()
96+
assertEquals(tintTheme, LocalTintTheme.current)
9197
}
9298
}
9399
}
@@ -97,6 +103,7 @@ class ThemeTest {
97103
composeTestRule.setContent {
98104
NiaTheme(
99105
darkTheme = false,
106+
disableDynamicTheming = false,
100107
androidTheme = false
101108
) {
102109
val colorScheme = dynamicLightColorSchemeWithFallback()
@@ -105,6 +112,8 @@ class ThemeTest {
105112
assertEquals(gradientColors, LocalGradientColors.current)
106113
val backgroundTheme = defaultBackgroundTheme(colorScheme)
107114
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
115+
val tintTheme = dynamicTintThemeWithFallback(colorScheme)
116+
assertEquals(tintTheme, LocalTintTheme.current)
108117
}
109118
}
110119
}
@@ -114,6 +123,7 @@ class ThemeTest {
114123
composeTestRule.setContent {
115124
NiaTheme(
116125
darkTheme = true,
126+
disableDynamicTheming = false,
117127
androidTheme = false
118128
) {
119129
val colorScheme = dynamicDarkColorSchemeWithFallback()
@@ -122,6 +132,8 @@ class ThemeTest {
122132
assertEquals(gradientColors, LocalGradientColors.current)
123133
val backgroundTheme = defaultBackgroundTheme(colorScheme)
124134
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
135+
val tintTheme = dynamicTintThemeWithFallback(colorScheme)
136+
assertEquals(tintTheme, LocalTintTheme.current)
125137
}
126138
}
127139
}
@@ -140,6 +152,8 @@ class ThemeTest {
140152
assertEquals(gradientColors, LocalGradientColors.current)
141153
val backgroundTheme = LightAndroidBackgroundTheme
142154
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
155+
val tintTheme = defaultTintTheme()
156+
assertEquals(tintTheme, LocalTintTheme.current)
143157
}
144158
}
145159
}
@@ -158,6 +172,8 @@ class ThemeTest {
158172
assertEquals(gradientColors, LocalGradientColors.current)
159173
val backgroundTheme = DarkAndroidBackgroundTheme
160174
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
175+
val tintTheme = defaultTintTheme()
176+
assertEquals(tintTheme, LocalTintTheme.current)
161177
}
162178
}
163179
}
@@ -167,6 +183,7 @@ class ThemeTest {
167183
composeTestRule.setContent {
168184
NiaTheme(
169185
darkTheme = false,
186+
disableDynamicTheming = false,
170187
androidTheme = true
171188
) {
172189
val colorScheme = LightAndroidColorScheme
@@ -175,6 +192,8 @@ class ThemeTest {
175192
assertEquals(gradientColors, LocalGradientColors.current)
176193
val backgroundTheme = LightAndroidBackgroundTheme
177194
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
195+
val tintTheme = defaultTintTheme()
196+
assertEquals(tintTheme, LocalTintTheme.current)
178197
}
179198
}
180199
}
@@ -184,6 +203,7 @@ class ThemeTest {
184203
composeTestRule.setContent {
185204
NiaTheme(
186205
darkTheme = true,
206+
disableDynamicTheming = false,
187207
androidTheme = true
188208
) {
189209
val colorScheme = DarkAndroidColorScheme
@@ -192,6 +212,8 @@ class ThemeTest {
192212
assertEquals(gradientColors, LocalGradientColors.current)
193213
val backgroundTheme = DarkAndroidBackgroundTheme
194214
assertEquals(backgroundTheme, LocalBackgroundTheme.current)
215+
val tintTheme = defaultTintTheme()
216+
assertEquals(tintTheme, LocalTintTheme.current)
195217
}
196218
}
197219
}
@@ -241,6 +263,18 @@ class ThemeTest {
241263
)
242264
}
243265

266+
private fun defaultTintTheme(): TintTheme {
267+
return TintTheme()
268+
}
269+
270+
private fun dynamicTintThemeWithFallback(colorScheme: ColorScheme): TintTheme {
271+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
272+
TintTheme(colorScheme.primary)
273+
} else {
274+
TintTheme()
275+
}
276+
}
277+
244278
/**
245279
* Workaround for the fact that the NiA design system specify all color scheme values.
246280
*/

0 commit comments

Comments
 (0)