• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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