1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.lifecycle 18 19 import androidx.compose.runtime.Composable 20 import androidx.compose.runtime.LaunchedEffect 21 import androidx.compose.runtime.remember 22 import com.android.app.tracing.coroutines.traceCoroutine 23 24 /** Defines interface for classes that can be activated to do coroutine work. */ 25 interface Activatable { 26 27 /** 28 * Activates this object. 29 * 30 * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep 31 * its state fresh and/or perform side-effects. 32 * 33 * The method suspends and doesn't return until all work required by the object is finished. In 34 * most cases, it's expected for the work to remain ongoing forever so this method will forever 35 * suspend its caller until the coroutine that called it is canceled. 36 * 37 * Implementations could follow this pattern: 38 * ```kotlin 39 * override suspend fun activate() { 40 * coroutineScope { 41 * launch { ... } 42 * launch { ... } 43 * launch { ... } 44 * } 45 * } 46 * ``` 47 * 48 * **Must be invoked** by the owner of the object when the object is to become active. 49 * Similarly, the work must be canceled by the owner when the objects is to be deactivated. 50 * 51 * One way to have a parent call this would be by using a `LaunchedEffect` in Compose: 52 * ```kotlin 53 * @Composable 54 * fun MyUi(activatable: Activatable) { 55 * LaunchedEffect(activatable) { 56 * activatable.activate() 57 * } 58 * } 59 * ``` 60 */ activatenull61 suspend fun activate(): Nothing 62 } 63 64 /** 65 * Returns a remembered [Activatable] of the type [T] that's automatically kept active until this 66 * composable leaves the composition. 67 * 68 * If the [key] changes, the old [Activatable] is deactivated and a new one will be instantiated, 69 * activated, and returned. 70 * 71 * The [traceName] is used for coroutine performance tracing purposes. Please try to use a label 72 * that's unique enough and easy enough to find in code search; this should help correlate 73 * performance findings with actual code. One recommendation: prefer whole string literals instead 74 * of some complex concatenation or templating scheme. 75 */ 76 @Composable 77 fun <T : Activatable> rememberActivated( 78 traceName: String, 79 key: Any = Unit, 80 factory: () -> T, 81 ): T { 82 val instance = remember(key) { factory() } 83 LaunchedEffect(instance) { traceCoroutine(traceName) { instance.activate() } } 84 return instance 85 } 86