Create LifecycleEffect to listen for lifecycle
Bug: 236346018
Test: Unit test
Change-Id: I264f7215914d92dc32120254b355ba75be225f42
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 73c1099..bf4ad75 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -88,6 +88,7 @@
implementation "com.airbnb.android:lottie-compose:5.2.0"
androidTestImplementation project(":testutils")
+ androidTestImplementation 'androidx.lifecycle:lifecycle-runtime-testing'
androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:2.28.1"
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt
new file mode 100644
index 0000000..e91fa65
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 com.android.settingslib.spa.framework.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+
+@Composable
+fun LifecycleEffect(
+ onStart: () -> Unit = {},
+ onStop: () -> Unit = {},
+) {
+ val lifecycleOwner = LocalLifecycleOwner.current
+ DisposableEffect(lifecycleOwner) {
+ val observer = LifecycleEventObserver { _, event ->
+ if (event == Lifecycle.Event.ON_START) {
+ onStart()
+ } else if (event == Lifecycle.Event.ON_STOP) {
+ onStop()
+ }
+ }
+
+ lifecycleOwner.lifecycle.addObserver(observer)
+
+ onDispose {
+ lifecycleOwner.lifecycle.removeObserver(observer)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
index 271443e..73eae07 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
@@ -18,55 +18,41 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.core.os.bundleOf
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
import com.android.settingslib.spa.framework.common.LOG_DATA_DISPLAY_NAME
import com.android.settingslib.spa.framework.common.LOG_DATA_SESSION_NAME
import com.android.settingslib.spa.framework.common.LogCategory
import com.android.settingslib.spa.framework.common.LogEvent
+import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.LifecycleEffect
import com.android.settingslib.spa.framework.compose.LocalNavController
+import com.android.settingslib.spa.framework.compose.NavControllerWrapper
@Composable
internal fun SettingsPageProvider.PageEvent(arguments: Bundle? = null) {
val page = remember(arguments) { createSettingsPage(arguments) }
- val lifecycleOwner = LocalLifecycleOwner.current
val navController = LocalNavController.current
- DisposableEffect(lifecycleOwner) {
- val observer = LifecycleEventObserver { _, event ->
- val logPageEvent: (event: LogEvent) -> Unit = {
- SpaEnvironmentFactory.instance.logger.event(
- id = page.id,
- event = it,
- category = LogCategory.FRAMEWORK,
- extraData = bundleOf(
- LOG_DATA_DISPLAY_NAME to page.displayName,
- LOG_DATA_SESSION_NAME to navController.sessionSourceName,
- ).apply {
- val normArguments = parameter.normalize(arguments)
- if (normArguments != null) putAll(normArguments)
- }
- )
- }
- if (event == Lifecycle.Event.ON_START) {
- logPageEvent(LogEvent.PAGE_ENTER)
- } else if (event == Lifecycle.Event.ON_STOP) {
- logPageEvent(LogEvent.PAGE_LEAVE)
- }
- }
-
- // Add the observer to the lifecycle
- lifecycleOwner.lifecycle.addObserver(observer)
-
- // When the effect leaves the Composition, remove the observer
- onDispose {
- lifecycleOwner.lifecycle.removeObserver(observer)
- }
- }
+ LifecycleEffect(
+ onStart = { page.logPageEvent(LogEvent.PAGE_ENTER, navController) },
+ onStop = { page.logPageEvent(LogEvent.PAGE_LEAVE, navController) },
+ )
}
+
+private fun SettingsPage.logPageEvent(event: LogEvent, navController: NavControllerWrapper) {
+ SpaEnvironmentFactory.instance.logger.event(
+ id = id,
+ event = event,
+ category = LogCategory.FRAMEWORK,
+ extraData = bundleOf(
+ LOG_DATA_DISPLAY_NAME to displayName,
+ LOG_DATA_SESSION_NAME to navController.sessionSourceName,
+ ).apply {
+ val normArguments = parameter.normalize(arguments)
+ if (normArguments != null) putAll(normArguments)
+ }
+ )
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp
index f9e64ae..b4c67cc 100644
--- a/packages/SettingsLib/Spa/tests/Android.bp
+++ b/packages/SettingsLib/Spa/tests/Android.bp
@@ -31,6 +31,7 @@
"SpaLib",
"SpaLibTestUtils",
"androidx.compose.runtime_runtime",
+ "androidx.lifecycle_lifecycle-runtime-testing",
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt
new file mode 100644
index 0000000..fe7baff
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 com.android.settingslib.spa.framework.compose
+
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LifecycleEffectTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun onStart_isCalled() {
+ var onStartIsCalled = false
+ composeTestRule.setContent {
+ LifecycleEffect(onStart = { onStartIsCalled = true })
+ }
+
+ assertThat(onStartIsCalled).isTrue()
+ }
+
+ @Test
+ fun onStop_isCalled() {
+ var onStopIsCalled = false
+ val testLifecycleOwner = TestLifecycleOwner()
+
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalLifecycleOwner provides testLifecycleOwner) {
+ LifecycleEffect(onStop = { onStopIsCalled = true })
+ }
+ LaunchedEffect(Unit) {
+ testLifecycleOwner.currentState = Lifecycle.State.CREATED
+ }
+ }
+
+ assertThat(onStopIsCalled).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
index b2ea4a0..a2fb101 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
@@ -22,11 +22,9 @@
import android.content.IntentFilter
import android.os.UserHandle
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLifecycleOwner
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
+import com.android.settingslib.spa.framework.compose.LifecycleEffect
/**
* A [BroadcastReceiver] which registered when on start and unregistered when on stop.
@@ -39,28 +37,22 @@
onReceive: (Intent) -> Unit,
) {
val context = LocalContext.current
- val lifecycleOwner = LocalLifecycleOwner.current
- DisposableEffect(lifecycleOwner) {
- val broadcastReceiver = object : BroadcastReceiver() {
+ val broadcastReceiver = remember {
+ object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
onReceive(intent)
}
}
- val observer = LifecycleEventObserver { _, event ->
- if (event == Lifecycle.Event.ON_START) {
- context.registerReceiverAsUser(
- broadcastReceiver, userHandle, intentFilter, null, null
- )
- onStart()
- } else if (event == Lifecycle.Event.ON_STOP) {
- context.unregisterReceiver(broadcastReceiver)
- }
- }
-
- lifecycleOwner.lifecycle.addObserver(observer)
-
- onDispose {
- lifecycleOwner.lifecycle.removeObserver(observer)
- }
}
+ LifecycleEffect(
+ onStart = {
+ context.registerReceiverAsUser(
+ broadcastReceiver, userHandle, intentFilter, null, null
+ )
+ onStart()
+ },
+ onStop = {
+ context.unregisterReceiver(broadcastReceiver)
+ },
+ )
}