Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4751d357 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Create LifecycleEffect to listen for lifecycle"

parents c3f130b6 527fbf9a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ dependencies {
    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"
}

+46 −0
Original line number Diff line number Diff line
/*
 * 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
+21 −35
Original line number Diff line number Diff line
@@ -18,35 +18,37 @@ package com.android.settingslib.spa.framework.util

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 = {
    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 = page.id,
                    event = it,
        id = id,
        event = event,
        category = LogCategory.FRAMEWORK,
        extraData = bundleOf(
                        LOG_DATA_DISPLAY_NAME to page.displayName,
            LOG_DATA_DISPLAY_NAME to displayName,
            LOG_DATA_SESSION_NAME to navController.sessionSourceName,
        ).apply {
            val normArguments = parameter.normalize(arguments)
@@ -54,19 +56,3 @@ internal fun SettingsPageProvider.PageEvent(arguments: Bundle? = null) {
        }
    )
}
 No newline at end of file
            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)
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ android_test {
        "SpaLib",
        "SpaLibTestUtils",
        "androidx.compose.runtime_runtime",
        "androidx.lifecycle_lifecycle-runtime-testing",
        "androidx.test.ext.junit",
        "androidx.test.runner",
        "mockito-target-minus-junit4",
+62 −0
Original line number Diff line number Diff line
/*
 * 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
Loading