Merge "Update ColorScheme to be Immutable" into androidx-main
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index d6d64a0..9e11015 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -130,7 +130,7 @@
field public static final androidx.wear.compose.material3.CheckboxDefaults INSTANCE;
}
- @androidx.compose.runtime.Stable public final class ColorScheme {
+ @androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class ColorScheme {
ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
method public long getBackground();
@@ -159,7 +159,6 @@
method public long getTertiary();
method public long getTertiaryContainer();
method public long getTertiaryDim();
- method public void setSecondaryDim(long);
property public final long background;
property public final long error;
property public final long onBackground;
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index d6d64a0..9e11015 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -130,7 +130,7 @@
field public static final androidx.wear.compose.material3.CheckboxDefaults INSTANCE;
}
- @androidx.compose.runtime.Stable public final class ColorScheme {
+ @androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class ColorScheme {
ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
method public long getBackground();
@@ -159,7 +159,6 @@
method public long getTertiary();
method public long getTertiaryContainer();
method public long getTertiaryDim();
- method public void setSecondaryDim(long);
property public final long background;
property public final long error;
property public final long onBackground;
diff --git a/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ColorSchemeBenchmark.kt b/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ColorSchemeBenchmark.kt
new file mode 100644
index 0000000..cdbce14
--- /dev/null
+++ b/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ColorSchemeBenchmark.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.benchmark
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.contentColorFor
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class ColorSchemeBenchmark {
+ @get:Rule
+ val benchmarkRule = ComposeBenchmarkRule()
+
+ private val colorSchemeTestCaseFactory = { ColorSchemeTestCase() }
+
+ @Test
+ fun firstPixel() {
+ benchmarkRule.benchmarkToFirstPixel(colorSchemeTestCaseFactory)
+ }
+}
+
+class ColorSchemeTestCase : LayeredComposeTestCase() {
+
+ @Composable
+ override fun MeasuredContent() {
+ MaterialTheme {
+ Column {
+ // Primary
+ Box(modifier = Modifier.size(1.dp).background(MaterialTheme.colorScheme.primary))
+ Box(
+ modifier = Modifier.size(1.dp).background(
+ MaterialTheme.colorScheme.contentColorFor(MaterialTheme.colorScheme.primary)
+ )
+ )
+
+ // Primary Container
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(MaterialTheme.colorScheme.primaryContainer)
+ )
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(
+ MaterialTheme.colorScheme.contentColorFor(
+ MaterialTheme.colorScheme.primaryContainer
+ )
+ )
+ )
+
+ // Secondary
+ Box(modifier = Modifier.size(1.dp).background(MaterialTheme.colorScheme.secondary))
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(
+ MaterialTheme.colorScheme.contentColorFor(
+ MaterialTheme.colorScheme.secondary
+ )
+ )
+ )
+
+ // Secondary Container
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(MaterialTheme.colorScheme.secondaryContainer)
+ )
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(
+ MaterialTheme.colorScheme.contentColorFor(
+ MaterialTheme.colorScheme.secondaryContainer
+ )
+ )
+ )
+
+ // Tertiary
+ Box(modifier = Modifier.size(1.dp).background(MaterialTheme.colorScheme.tertiary))
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(
+ MaterialTheme.colorScheme.contentColorFor(
+ MaterialTheme.colorScheme.tertiary
+ )
+ )
+ )
+
+ // Tertiary Container
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(MaterialTheme.colorScheme.tertiaryContainer)
+ )
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(
+ MaterialTheme.colorScheme.contentColorFor(
+ MaterialTheme.colorScheme.tertiaryContainer
+ )
+ )
+ )
+
+ // Surface
+ Box(modifier = Modifier.size(1.dp).background(MaterialTheme.colorScheme.surface))
+ Box(
+ modifier = Modifier.size(1.dp).background(
+ MaterialTheme.colorScheme.contentColorFor(MaterialTheme.colorScheme.surface)
+ )
+ )
+
+ // Background
+ Box(modifier = Modifier.size(1.dp).background(MaterialTheme.colorScheme.background))
+ Box(
+ modifier = Modifier
+ .size(1.dp)
+ .background(
+ MaterialTheme.colorScheme.contentColorFor(
+ MaterialTheme.colorScheme.background
+ )
+ )
+ )
+
+ // Error
+ Box(modifier = Modifier.size(1.dp).background(MaterialTheme.colorScheme.error))
+ Box(
+ modifier = Modifier.size(1.dp).background(
+ MaterialTheme.colorScheme.contentColorFor(MaterialTheme.colorScheme.error)
+ )
+ )
+ }
+ }
+ }
+
+ @Composable
+ override fun ContentWrappers(content: @Composable () -> Unit) {
+ content()
+ }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
index 6e4256c..d05b634 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
@@ -16,11 +16,9 @@
package androidx.wear.compose.material3
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
@@ -77,196 +75,38 @@
* @property onBackground Color used for text and icons displayed on top of the background color.
* @property error The error color is used to indicate errors.
* @property onError Color used for text and icons displayed on top of the error color.
- */@Stable
-class ColorScheme(
- primary: Color = ColorTokens.Primary,
- primaryDim: Color = ColorTokens.PrimaryDim,
- primaryContainer: Color = ColorTokens.PrimaryContainer,
- onPrimary: Color = ColorTokens.OnPrimary,
- onPrimaryContainer: Color = ColorTokens.OnPrimaryContainer,
- secondary: Color = ColorTokens.Secondary,
- secondaryDim: Color = ColorTokens.SecondaryDim,
- secondaryContainer: Color = ColorTokens.SecondaryContainer,
- onSecondary: Color = ColorTokens.OnSecondary,
- onSecondaryContainer: Color = ColorTokens.OnSecondaryContainer,
- tertiary: Color = ColorTokens.Tertiary,
- tertiaryDim: Color = ColorTokens.TertiaryDim,
- tertiaryContainer: Color = ColorTokens.TertiaryContainer,
- onTertiary: Color = ColorTokens.OnTertiary,
- onTertiaryContainer: Color = ColorTokens.OnTertiaryContainer,
- surfaceDim: Color = ColorTokens.SurfaceDim,
- surface: Color = ColorTokens.Surface,
- surfaceBright: Color = ColorTokens.SurfaceBright,
- onSurface: Color = ColorTokens.OnSurface,
- onSurfaceVariant: Color = ColorTokens.OnSurfaceVariant,
- outline: Color = ColorTokens.Outline,
- outlineVariant: Color = ColorTokens.OutlineVariant,
- background: Color = ColorTokens.Background,
- onBackground: Color = ColorTokens.OnBackground,
- error: Color = ColorTokens.Error,
- onError: Color = ColorTokens.OnError,
+ */
+@Immutable
+@Stable
+public class ColorScheme(
+ val primary: Color = ColorTokens.Primary,
+ val primaryDim: Color = ColorTokens.PrimaryDim,
+ val primaryContainer: Color = ColorTokens.PrimaryContainer,
+ val onPrimary: Color = ColorTokens.OnPrimary,
+ val onPrimaryContainer: Color = ColorTokens.OnPrimaryContainer,
+ val secondary: Color = ColorTokens.Secondary,
+ val secondaryDim: Color = ColorTokens.SecondaryDim,
+ val secondaryContainer: Color = ColorTokens.SecondaryContainer,
+ val onSecondary: Color = ColorTokens.OnSecondary,
+ val onSecondaryContainer: Color = ColorTokens.OnSecondaryContainer,
+ val tertiary: Color = ColorTokens.Tertiary,
+ val tertiaryDim: Color = ColorTokens.TertiaryDim,
+ val tertiaryContainer: Color = ColorTokens.TertiaryContainer,
+ val onTertiary: Color = ColorTokens.OnTertiary,
+ val onTertiaryContainer: Color = ColorTokens.OnTertiaryContainer,
+ val surfaceDim: Color = ColorTokens.SurfaceDim,
+ val surface: Color = ColorTokens.Surface,
+ val surfaceBright: Color = ColorTokens.SurfaceBright,
+ val onSurface: Color = ColorTokens.OnSurface,
+ val onSurfaceVariant: Color = ColorTokens.OnSurfaceVariant,
+ val outline: Color = ColorTokens.Outline,
+ val outlineVariant: Color = ColorTokens.OutlineVariant,
+ val background: Color = ColorTokens.Background,
+ val onBackground: Color = ColorTokens.OnBackground,
+ val error: Color = ColorTokens.Error,
+ val onError: Color = ColorTokens.OnError,
) {
/**
- * [primary] is the main color used across screens and components
- */
- var primary: Color by mutableStateOf(primary)
- internal set
-
- /**
- * [primaryDim] is less prominent than [primary] for component backgrounds
- */
- var primaryDim: Color by mutableStateOf(primaryDim)
- internal set
-
- /**
- * [primaryContainer] is a standout container color for key components
- */
- var primaryContainer: Color by mutableStateOf(primaryContainer)
- internal set
-
- /**
- * [onPrimary] is for text and icons shown against the Primary and primaryDim colors
- */
- var onPrimary: Color by mutableStateOf(onPrimary)
- internal set
-
- /**
- * [onPrimaryContainer] is a contrast-passing color shown against the primaryContainer
- */
- var onPrimaryContainer: Color by mutableStateOf(onPrimaryContainer)
- internal set
-
- /**
- * [secondary] is an accent color used across screens and components
- */
- var secondary: Color by mutableStateOf(secondary)
- internal set
-
- /**
- * [secondaryDim] is less prominent than [secondary] for component backgrounds
- */
- var secondaryDim: Color by mutableStateOf(secondaryDim)
-
- /**
- * [secondaryContainer] is a less prominent container color than [primaryContainer],
- * for components like tonal buttons
- */
- var secondaryContainer: Color by mutableStateOf(secondaryContainer)
- internal set
-
- /**
- * [onSecondary] is for text and icons shown against the Secondary and SecondaryDim colors
- */
- var onSecondary: Color by mutableStateOf(onSecondary)
- internal set
-
- /**
- * [onSecondaryContainer] is a contrast-passing color shown against the secondaryContainer
- */
- var onSecondaryContainer: Color by mutableStateOf(onSecondaryContainer)
- internal set
-
- /**
- * [tertiary] is a complementary color to create contrast and draw attention to elements
- */
- var tertiary: Color by mutableStateOf(tertiary)
- internal set
-
- /**
- * [tertiaryDim] is less prominent than [tertiary] for component backgrounds
- */
- var tertiaryDim: Color by mutableStateOf(tertiaryDim)
- internal set
-
- /**
- * [tertiaryContainer] is a contrasting container color for components
- */
- var tertiaryContainer: Color by mutableStateOf(tertiaryContainer)
- internal set
-
- /**
- * [onTertiary] is for text and icons shown against the Tertiary and tertiaryDim colors
- */
- var onTertiary: Color by mutableStateOf(onTertiary)
- internal set
-
- /**
- * [onTertiaryContainer] is a contrast-passing color shown against the tertiaryContainer
- */
- var onTertiaryContainer: Color by mutableStateOf(onTertiaryContainer)
- internal set
-
- /**
- * [surfaceDim] is a surface color used for large containment components, with the
- * lowest prominence behind Surface and surfaceBright
- */
- var surfaceDim: Color by mutableStateOf(surfaceDim)
- internal set
-
- /**
- * [surface] is the main color for large containment components like card and button backgrounds
- */
- var surface: Color by mutableStateOf(surface)
- internal set
-
- /**
- * [surfaceBright] is a surface color used for large containment components, with
- * the highest prominence, ahead of Surface and surfaceDim
- */
- var surfaceBright: Color by mutableStateOf(surfaceBright)
- internal set
-
- /**
- * [onSurface] for primary text and icons shown against the
- * [surface], [surfaceDim] and [surfaceBright]
- */
- var onSurface: Color by mutableStateOf(onSurface)
- internal set
-
- /**
- * [onSurfaceVariant] for secondary text and icons on
- * [surface], [surfaceDim] and [surfaceBright]
- */
- var onSurfaceVariant: Color by mutableStateOf(onSurfaceVariant)
- internal set
-
- /**
- * [outline] is the main color for primary outline components
- */
- var outline: Color by mutableStateOf(outline)
- internal set
-
- /**
- * [outlineVariant] is the secondary color for secondary outline components
- */
- var outlineVariant: Color by mutableStateOf(outlineVariant)
- internal set
-
- /**
- * [background] is the static color used behind all texts and components
- */
- var background: Color by mutableStateOf(background)
- internal set
-
- /**
- * [onBackground] is used for text and icons shown against the background color
- */
- var onBackground: Color by mutableStateOf(onBackground)
- internal set
-
- /**
- * [error] indicates errors and emergency states
- */
- var error: Color by mutableStateOf(error)
- internal set
-
- /**
- * [onError] is used for text and icons on the error color
- */
- var onError: Color by mutableStateOf(onError)
- internal set
-
- /**
* Returns a copy of this Colors, optionally overriding some of the values.
*/
fun copy(
@@ -414,48 +254,6 @@
.takeOrElse { LocalContentColor.current }
/**
- * Updates the internal values of the given [ColorScheme] with values from the [other] [ColorScheme]. This
- * allows efficiently updating a subset of [ColorScheme], without recomposing every composable that
- * consumes values from [LocalColorScheme].
- *
- * Because [ColorScheme] is very wide-reaching, and used by many expensive composables in the
- * hierarchy, providing a new value to [LocalColorScheme] causes every composable consuming
- * [LocalColorScheme] to recompose, which is prohibitively expensive in cases such as animating one
- * color in the theme. Instead, [ColorScheme] is internally backed by [mutableStateOf], and this
- * function mutates the internal state of [this] to match values in [other]. This means that any
- * changes will mutate the internal state of [this], and only cause composables that are reading
- * the specific changed value to recompose.
- */
-internal fun ColorScheme.updateColorSchemeFrom(other: ColorScheme) {
- primary = other.primary
- primaryDim = other.primaryDim
- primaryContainer = other.primaryContainer
- onPrimary = other.onPrimary
- onPrimaryContainer = other.onPrimaryContainer
- secondary = other.secondary
- secondaryDim = other.secondaryDim
- secondaryContainer = other.secondaryContainer
- onSecondary = other.onSecondary
- onSecondaryContainer = other.onSecondaryContainer
- tertiary = other.tertiary
- tertiaryDim = other.tertiaryDim
- tertiaryContainer = other.tertiaryContainer
- onTertiary = other.onTertiary
- onTertiaryContainer = other.onTertiaryContainer
- surfaceDim = other.surfaceDim
- surface = other.surface
- surfaceBright = other.surfaceBright
- onSurface = other.onSurface
- onSurfaceVariant = other.onSurfaceVariant
- outline = other.outline
- outlineVariant = other.outlineVariant
- background = other.background
- onBackground = other.onBackground
- error = other.error
- onError = other.onError
-}
-
-/**
* Helper function for component color tokens. Here is an example on how to use component color
* tokens:
* ``MaterialTheme.colorScheme.fromToken(FilledButtonTokens.ContainerColor)``
@@ -491,6 +289,13 @@
}
}
+/**
+ * CompositionLocal used to pass [ColorScheme] down the tree.
+ *
+ * Setting the value here is typically done as part of [MaterialTheme].
+ * To retrieve the current value of this CompositionLocal, use
+ * [MaterialTheme.colorScheme].
+ */
internal val LocalColorScheme = staticCompositionLocalOf<ColorScheme> { ColorScheme() }
/**
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt
index f86da68..5ed024f 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt
@@ -58,24 +58,19 @@
shapes: Shapes = MaterialTheme.shapes,
content: @Composable () -> Unit
) {
- val rememberedColors = remember {
- // Explicitly creating a new object here so we don't mutate the initial [colors]
- // provided, and overwrite the values set in it.
- colorScheme.copy()
- }.apply { updateColorSchemeFrom(colorScheme) }
val rippleIndication = rippleOrFallbackImplementation()
- val selectionColors = rememberTextSelectionColors(rememberedColors)
+ val selectionColors = rememberTextSelectionColors(colorScheme)
@Suppress("DEPRECATION_ERROR")
CompositionLocalProvider(
- LocalColorScheme provides rememberedColors,
+ LocalColorScheme provides colorScheme,
LocalShapes provides shapes,
LocalTypography provides typography,
LocalIndication provides rippleIndication,
// TODO: b/304985887 - remove after one stable release
androidx.compose.material.ripple.LocalRippleTheme provides CompatRippleTheme,
LocalTextSelectionColors provides selectionColors,
- LocalSwipeToDismissBackgroundScrimColor provides rememberedColors.background,
- LocalSwipeToDismissContentScrimColor provides rememberedColors.background
+ LocalSwipeToDismissBackgroundScrimColor provides colorScheme.background,
+ LocalSwipeToDismissContentScrimColor provides colorScheme.background
) {
ProvideTextStyle(value = typography.bodyLarge, content = content)
}