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 java.util.concurrent.atomic.AtomicBoolean 20 21 /** 22 * A base [Activatable] that can only be activated by a single owner (hence "exclusive"). A previous 23 * call to [activate] must be canceled before a new call to [activate] can be made. Trying to call 24 * [activate] while already active will result in a runtime error. 25 */ 26 abstract class ExclusiveActivatable : Activatable { 27 28 private val _isActive = AtomicBoolean(false) 29 30 protected var isActive: Boolean 31 get() = _isActive.get() 32 private set(value) { 33 _isActive.set(value) 34 } 35 activatenull36 final override suspend fun activate(): Nothing { 37 val allowed = _isActive.compareAndSet(false, true) 38 check(allowed) { "Cannot activate an already active ExclusiveActivatable!" } 39 40 try { 41 onActivated() 42 } finally { 43 isActive = false 44 } 45 } 46 47 /** 48 * Notifies that the [Activatable] has been activated. 49 * 50 * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep 51 * its state fresh and/or perform side-effects. 52 * 53 * The method suspends and doesn't return until all work required by the object is finished. In 54 * most cases, it's expected for the work to remain ongoing forever so this method will forever 55 * suspend its caller until the coroutine that called it is canceled. 56 * 57 * Implementations could follow this pattern: 58 * ```kotlin 59 * override suspend fun onActivated(): Nothing { 60 * coroutineScope { 61 * launch { ... } 62 * launch { ... } 63 * launch { ... } 64 * awaitCancellation() 65 * } 66 * } 67 * ``` 68 * 69 * @see activate 70 */ onActivatednull71 protected abstract suspend fun onActivated(): Nothing 72 } 73