Skip to content

Commit 862a7e1

Browse files
Material Design Teamraajkumars
authored andcommitted
[M3][Color] Add dynamic contrast support
PiperOrigin-RevId: 544727089
1 parent ad6afbf commit 862a7e1

File tree

5 files changed

+197
-1
lines changed

5 files changed

+197
-1
lines changed

lib/java/com/google/android/material/color/DynamicColors.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
import android.app.Application.ActivityLifecycleCallbacks;
2424
import android.app.UiModeManager;
2525
import android.content.Context;
26+
import android.content.res.Resources.NotFoundException;
2627
import android.content.res.TypedArray;
2728
import android.os.Build;
2829
import android.os.Build.VERSION;
2930
import android.os.Build.VERSION_CODES;
3031
import android.os.Bundle;
32+
import android.util.Log;
3133
import android.view.ContextThemeWrapper;
3234
import androidx.annotation.ChecksSdkIntAtLeast;
3335
import androidx.annotation.NonNull;
@@ -44,6 +46,7 @@
4446
import java.util.HashMap;
4547
import java.util.Locale;
4648
import java.util.Map;
49+
import java.util.Objects;
4750

4851
/** Utility for applying dynamic colors to application/activities. */
4952
public class DynamicColors {
@@ -138,6 +141,12 @@ public boolean isSupported() {
138141

139142
private static final int USE_DEFAULT_THEME_OVERLAY = 0;
140143
private static final int UPDATED_NEUTRAL_PALETTE_CHROMA = 6;
144+
private static final String TAG = DynamicColors.class.getSimpleName();
145+
146+
private static final String SYSTEM_OUTLINE_VARIANT_DARK_RESOURCE_ENTRY_NAME =
147+
"system_outline_variant_dark";
148+
149+
private static final int SYSTEM_OUTLINE_VARIANT_DARK_RESOURCE_ID = 0x010600c1;
141150

142151
private DynamicColors() {}
143152

@@ -446,6 +455,11 @@ public static boolean isDynamicColorAvailable() {
446455
}
447456

448457
private static int getDefaultThemeOverlay(@NonNull Context context) {
458+
// TODO(b/289112889): Remove workaround and roll forward cl/528599594 as soon as U public
459+
// release.
460+
if (isDynamicContrastAvailable(context)) {
461+
return R.style.ThemeOverlay_Material3_DynamicColors_Contrast_DayNight;
462+
}
449463
TypedArray dynamicColorAttributes =
450464
context.obtainStyledAttributes(DYNAMIC_COLOR_THEME_OVERLAY_ATTRIBUTE);
451465
final int theme = dynamicColorAttributes.getResourceId(0, 0);
@@ -568,4 +582,26 @@ private static float getSystemContrast(Context context) {
568582
? 0
569583
: uiModeManager.getContrast();
570584
}
585+
586+
private static boolean isDynamicContrastAvailable(Context context) {
587+
if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE && areSystemColorRolesDefined(context)) {
588+
return true;
589+
}
590+
return false;
591+
}
592+
593+
// TODO(b/289112889): Remove workaround and roll forward cl/528599594 as soon as U public release.
594+
//
595+
// This is to check and make sure the last material resource defined in the android block of
596+
// resources matches the resource name from app's context.
597+
private static boolean areSystemColorRolesDefined(Context context) {
598+
try {
599+
return Objects.equals(
600+
context.getResources().getResourceEntryName(SYSTEM_OUTLINE_VARIANT_DARK_RESOURCE_ID),
601+
SYSTEM_OUTLINE_VARIANT_DARK_RESOURCE_ENTRY_NAME);
602+
} catch (NotFoundException e) {
603+
Log.i(TAG, SYSTEM_OUTLINE_VARIANT_DARK_RESOURCE_ENTRY_NAME + " resource not found.", e);
604+
}
605+
return false;
606+
}
571607
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2023 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<!-- Material3 alternative to textColorHighlight for dynamic dark theme -->
18+
<selector xmlns:android="http://schemas.android.com/apk/res/android"
19+
xmlns:tools="http://schemas.android.com/tools"
20+
tools:ignore="NewApi">
21+
<item android:alpha="@dimen/material_emphasis_medium" android:color="@android:color/system_primary_dark" />
22+
</selector>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2023 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<!-- Material3 alternative to textColorHighlight for dynamic light theme -->
18+
<selector xmlns:android="http://schemas.android.com/apk/res/android"
19+
xmlns:tools="http://schemas.android.com/tools"
20+
tools:ignore="NewApi">
21+
<item android:alpha="@dimen/material_emphasis_medium" android:color="@android:color/system_primary_light" />
22+
</selector>

lib/java/com/google/android/material/theme/res/values-night/themes_overlay.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@
1616
-->
1717
<resources>
1818
<style name="ThemeOverlay.Material3.DynamicColors.DayNight" parent="ThemeOverlay.Material3.DynamicColors.Dark" />
19+
20+
<style name="ThemeOverlay.Material3.DynamicColors.Contrast.DayNight" parent="ThemeOverlay.Material3.DynamicColors.Contrast.Dark" />
1921
</resources>

lib/java/com/google/android/material/theme/res/values/themes_overlay.xml

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
~ See the License for the specific language governing permissions and
1515
~ limitations under the License.
1616
-->
17-
<resources>
17+
<resources xmlns:tools="http://schemas.android.com/tools">
1818
<!--
1919
Material 3 theme overlay.
2020
-->
@@ -174,4 +174,118 @@
174174
-->
175175
<style name="ThemeOverlay.MaterialComponents.Dark.ActionBar" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
176176

177+
<style name="ThemeOverlay.Material3.DynamicColors.Contrast.Light" parent=""
178+
tools:ignore="NewApi">
179+
<!-- Color palettes -->
180+
<item name="colorPrimary">@android:color/system_primary_light</item>
181+
<item name="colorOnPrimary">@android:color/system_on_primary_light</item>
182+
<item name="colorPrimaryInverse">@android:color/system_primary_dark</item>
183+
<item name="colorPrimaryContainer">@android:color/system_primary_container_light</item>
184+
<item name="colorOnPrimaryContainer">@android:color/system_on_primary_container_light</item>
185+
<item name="colorPrimaryFixed">@android:color/system_primary_fixed</item>
186+
<item name="colorPrimaryFixedDim">@android:color/system_primary_fixed_dim</item>
187+
<item name="colorOnPrimaryFixed">@android:color/system_on_primary_fixed</item>
188+
<item name="colorOnPrimaryFixedVariant">@android:color/system_on_primary_fixed_variant</item>
189+
<item name="colorSecondary">@android:color/system_secondary_light</item>
190+
<item name="colorOnSecondary">@android:color/system_on_secondary_light</item>
191+
<item name="colorSecondaryContainer">@android:color/system_secondary_container_light</item>
192+
<item name="colorOnSecondaryContainer">@android:color/system_on_secondary_container_light</item>
193+
<item name="colorSecondaryFixed">@android:color/system_secondary_fixed</item>
194+
<item name="colorSecondaryFixedDim">@android:color/system_secondary_fixed_dim</item>
195+
<item name="colorOnSecondaryFixed">@android:color/system_on_secondary_fixed</item>
196+
<item name="colorOnSecondaryFixedVariant">@android:color/system_on_secondary_fixed_variant</item>
197+
<item name="colorTertiary">@android:color/system_tertiary_light</item>
198+
<item name="colorOnTertiary">@android:color/system_on_tertiary_light</item>
199+
<item name="colorTertiaryContainer">@android:color/system_tertiary_container_light</item>
200+
<item name="colorOnTertiaryContainer">@android:color/system_on_tertiary_container_light</item>
201+
<item name="colorTertiaryFixed">@android:color/system_tertiary_fixed</item>
202+
<item name="colorTertiaryFixedDim">@android:color/system_tertiary_fixed_dim</item>
203+
<item name="colorOnTertiaryFixed">@android:color/system_on_tertiary_fixed</item>
204+
<item name="colorOnTertiaryFixedVariant">@android:color/system_on_tertiary_fixed_variant</item>
205+
<item name="android:colorBackground">@android:color/system_background_light</item>
206+
<item name="colorOnBackground">@android:color/system_on_background_light</item>
207+
<item name="colorSurface">@android:color/system_surface_light</item>
208+
<item name="colorOnSurface">@android:color/system_on_surface_light</item>
209+
<item name="colorSurfaceVariant">@android:color/system_surface_variant_light</item>
210+
<item name="colorOnSurfaceVariant">@android:color/system_on_surface_variant_light</item>
211+
<item name="colorSurfaceInverse">@android:color/system_surface_dark</item>
212+
<item name="colorOnSurfaceInverse">@android:color/system_on_surface_dark</item>
213+
<item name="colorSurfaceBright">@android:color/system_surface_bright_light</item>
214+
<item name="colorSurfaceDim">@android:color/system_surface_dim_light</item>
215+
<item name="colorSurfaceContainer">@android:color/system_surface_container_light</item>
216+
<item name="colorSurfaceContainerLow">@android:color/system_surface_container_low_light</item>
217+
<item name="colorSurfaceContainerHigh">@android:color/system_surface_container_high_light</item>
218+
<item name="colorSurfaceContainerLowest">@android:color/system_surface_container_lowest_light</item>
219+
<item name="colorSurfaceContainerHighest">@android:color/system_surface_container_highest_light</item>
220+
<item name="colorOutline">@android:color/system_outline_light</item>
221+
<item name="colorOutlineVariant">@android:color/system_outline_variant_light</item>
222+
<item name="colorError">@android:color/system_error_light</item>
223+
<item name="colorOnError">@android:color/system_error_container_light</item>
224+
<item name="colorErrorContainer">@android:color/system_on_error_light</item>
225+
<item name="colorOnErrorContainer">@android:color/system_on_error_container_light</item>
226+
227+
<!-- Default Framework Text Colors. -->
228+
<!-- textColorHighlight and textColorHighlightInverse are using system color resource directly in the CSL for contrast support, as they use the primary color palette. -->
229+
<item name="android:textColorHighlight">@color/m3_dynamic_contrast_highlighted_text</item>
230+
<item name="android:textColorHighlightInverse">@color/m3_dynamic_contrast_dark_highlighted_text</item>
231+
</style>
232+
233+
<style name="ThemeOverlay.Material3.DynamicColors.Contrast.Dark" parent=""
234+
tools:ignore="NewApi">
235+
<!-- Color palettes -->
236+
<item name="colorPrimary">@android:color/system_primary_dark</item>
237+
<item name="colorOnPrimary">@android:color/system_on_primary_dark</item>
238+
<item name="colorPrimaryInverse">@android:color/system_primary_light</item>
239+
<item name="colorPrimaryContainer">@android:color/system_primary_container_dark</item>
240+
<item name="colorOnPrimaryContainer">@android:color/system_on_primary_container_dark</item>
241+
<item name="colorPrimaryFixed">@android:color/system_primary_fixed</item>
242+
<item name="colorPrimaryFixedDim">@android:color/system_primary_fixed_dim</item>
243+
<item name="colorOnPrimaryFixed">@android:color/system_on_primary_fixed</item>
244+
<item name="colorOnPrimaryFixedVariant">@android:color/system_on_primary_fixed_variant</item>
245+
<item name="colorSecondary">@android:color/system_secondary_dark</item>
246+
<item name="colorOnSecondary">@android:color/system_on_secondary_dark</item>
247+
<item name="colorSecondaryContainer">@android:color/system_secondary_container_dark</item>
248+
<item name="colorOnSecondaryContainer">@android:color/system_on_secondary_container_dark</item>
249+
<item name="colorSecondaryFixed">@android:color/system_secondary_fixed</item>
250+
<item name="colorSecondaryFixedDim">@android:color/system_secondary_fixed_dim</item>
251+
<item name="colorOnSecondaryFixed">@android:color/system_on_secondary_fixed</item>
252+
<item name="colorOnSecondaryFixedVariant">@android:color/system_on_secondary_fixed_variant</item>
253+
<item name="colorTertiary">@android:color/system_tertiary_dark</item>
254+
<item name="colorOnTertiary">@android:color/system_on_tertiary_dark</item>
255+
<item name="colorTertiaryContainer">@android:color/system_tertiary_container_dark</item>
256+
<item name="colorOnTertiaryContainer">@android:color/system_on_tertiary_container_dark</item>
257+
<item name="colorTertiaryFixed">@android:color/system_tertiary_fixed</item>
258+
<item name="colorTertiaryFixedDim">@android:color/system_tertiary_fixed_dim</item>
259+
<item name="colorOnTertiaryFixed">@android:color/system_on_tertiary_fixed</item>
260+
<item name="colorOnTertiaryFixedVariant">@android:color/system_on_tertiary_fixed_variant</item>
261+
<item name="android:colorBackground">@android:color/system_background_dark</item>
262+
<item name="colorOnBackground">@android:color/system_on_background_dark</item>
263+
<item name="colorSurface">@android:color/system_surface_dark</item>
264+
<item name="colorOnSurface">@android:color/system_on_surface_dark</item>
265+
<item name="colorSurfaceVariant">@android:color/system_surface_variant_dark</item>
266+
<item name="colorOnSurfaceVariant">@android:color/system_on_surface_variant_dark</item>
267+
<item name="colorSurfaceInverse">@android:color/system_surface_light</item>
268+
<item name="colorOnSurfaceInverse">@android:color/system_on_surface_light</item>
269+
<item name="colorSurfaceBright">@android:color/system_surface_bright_dark</item>
270+
<item name="colorSurfaceDim">@android:color/system_surface_dim_dark</item>
271+
<item name="colorSurfaceContainer">@android:color/system_surface_container_dark</item>
272+
<item name="colorSurfaceContainerLow">@android:color/system_surface_container_low_dark</item>
273+
<item name="colorSurfaceContainerHigh">@android:color/system_surface_container_high_dark</item>
274+
<item name="colorSurfaceContainerLowest">@android:color/system_surface_container_lowest_dark</item>
275+
<item name="colorSurfaceContainerHighest">@android:color/system_surface_container_highest_dark</item>
276+
<item name="colorOutline">@android:color/system_outline_dark</item>
277+
<item name="colorOutlineVariant">@android:color/system_outline_variant_dark</item>
278+
<item name="colorError">@android:color/system_error_dark</item>
279+
<item name="colorOnError">@android:color/system_on_error_dark</item>
280+
<item name="colorErrorContainer">@android:color/system_error_container_dark</item>
281+
<item name="colorOnErrorContainer">@android:color/system_on_error_container_dark</item>
282+
283+
<!-- Default Framework Text Colors. -->
284+
<!-- textColorHighlight and textColorHighlightInverse are using system color resource directly in the CSL for contrast support, as they use the primary color palette. -->
285+
<item name="android:textColorHighlight">@color/m3_dynamic_contrast_dark_highlighted_text</item>
286+
<item name="android:textColorHighlightInverse">@color/m3_dynamic_contrast_highlighted_text</item>
287+
</style>
288+
289+
<style name="ThemeOverlay.Material3.DynamicColors.Contrast.DayNight" parent="ThemeOverlay.Material3.DynamicColors.Contrast.Light" />
290+
177291
</resources>

0 commit comments

Comments
 (0)