Collect ReduceMotion setting changes in lifecycle aware manner
Bug: 314722367
Test: Manual
Change-Id: I249c0c0a1c8c47493954e89bf12047a32102b95e
diff --git a/wear/compose/compose-foundation/build.gradle b/wear/compose/compose-foundation/build.gradle
index 71f1302..214a3f6 100644
--- a/wear/compose/compose-foundation/build.gradle
+++ b/wear/compose/compose-foundation/build.gradle
@@ -33,6 +33,7 @@
implementation(libs.kotlinStdlib)
implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":compose:ui:ui-util"))
+ implementation(project(":lifecycle:lifecycle-runtime-compose"))
implementation("androidx.core:core:1.11.0")
implementation("androidx.profileinstaller:profileinstaller:1.3.0")
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CompositionLocals.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CompositionLocals.kt
index 03c26c6..6c6eb62 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CompositionLocals.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CompositionLocals.kt
@@ -29,13 +29,13 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.core.os.HandlerCompat
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.MainScope
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn
/**
@@ -48,7 +48,8 @@
val LocalReduceMotion: ProvidableCompositionLocal = staticCompositionLocalOf {
ReduceMotion {
val context = LocalContext.current.applicationContext
- getReduceMotionFlowFor(context).value
+ val flow = getReduceMotionFlowFor(context)
+ flow.collectAsStateWithLifecycle().value
}
}
@@ -85,27 +86,34 @@
private fun getReduceMotionFlowFor(applicationContext: Context): StateFlow {
val resolver = applicationContext.contentResolver
val reduceMotionUri = Settings.Global.getUriFor(REDUCE_MOTION)
- val channel = Channel(CONFLATED)
- val contentObserver =
- object : ContentObserver(HandlerCompat.createAsync(Looper.getMainLooper())) {
- override fun onChange(selfChange: Boolean, uri: Uri?) {
- channel.trySend(Unit)
- }
- }
+
return reduceMotionCache.updateAndGet {
- it ?: flow {
- resolver.registerContentObserver(reduceMotionUri, false, contentObserver)
- try {
- for (value in channel) {
- val newValue = getReducedMotionSettingValue(resolver)
- emit(newValue)
+ it ?: callbackFlow {
+ val contentObserver =
+ object : ContentObserver(HandlerCompat.createAsync(Looper.getMainLooper())) {
+ override fun deliverSelfNotifications(): Boolean {
+ // Returning true to receive change notification so that
+ // the flow sends new value after it is initialized.
+ return true
+ }
+
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ super.onChange(selfChange, uri)
+ trySend(getReducedMotionSettingValue(resolver))
+ }
}
- } finally {
+
+ resolver.registerContentObserver(reduceMotionUri, false, contentObserver)
+
+ // Force send value when flow is initialized
+ resolver.notifyChange(reduceMotionUri, contentObserver)
+
+ awaitClose {
resolver.unregisterContentObserver(contentObserver)
}
}.stateIn(
MainScope(),
- SharingStarted.WhileSubscribed(),
+ SharingStarted.WhileSubscribed(5000),
getReducedMotionSettingValue(resolver)
)
}
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index 64ad508..24f6946 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -69,6 +69,9 @@
androidTestImplementation(project(":activity:activity-compose"))
androidTestImplementation(project(":activity:activity-ktx"))
androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+ androidTestImplementation(project(":lifecycle:lifecycle-runtime"))
+ androidTestImplementation(project(":lifecycle:lifecycle-common"))
+ androidTestImplementation(project(":lifecycle:lifecycle-runtime-ktx"))
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.testRunner)
diff --git a/wear/compose/integration-tests/navigation/build.gradle b/wear/compose/integration-tests/navigation/build.gradle
index 065c152..dc4e776 100644
--- a/wear/compose/integration-tests/navigation/build.gradle
+++ b/wear/compose/integration-tests/navigation/build.gradle
@@ -52,12 +52,8 @@
implementation(project(":wear:compose:compose-foundation-samples"))
implementation(project(":wear:compose:compose-material-samples"))
implementation(project(':wear:compose:compose-navigation'))
- // old version of common-java8 conflicts with newer version, because both have
- // DefaultLifecycleEventObserver.
- // Outside of androidx this is resolved via constraint added to lifecycle-common,
- // but it doesn't work in androidx.
- // See aosp/1804059
- androidTestImplementation "androidx.lifecycle:lifecycle-common-java8:2.4.0"
+
+ androidTestImplementation(project(":lifecycle:lifecycle-common"))
// Uses project dependency to match collections/compose-runtime
androidTestImplementation api("androidx.annotation:annotation:1.7.0")
}
\ No newline at end of file