1 /*
2  * Copyright (C) 2023 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.util.kotlin
18 
19 import com.android.systemui.lifecycle.repeatWhenAttached
20 import kotlin.coroutines.CoroutineContext
21 import kotlin.coroutines.EmptyCoroutineContext
22 import kotlinx.coroutines.CoroutineScope
23 import kotlinx.coroutines.CoroutineStart
24 import kotlinx.coroutines.DisposableHandle
25 import kotlinx.coroutines.Job
26 import kotlinx.coroutines.awaitCancellation
27 import kotlinx.coroutines.launch
28 
29 /**
30  * Suspends to keep getting updates until cancellation. Once cancelled, mark this as eligible for
31  * garbage collection.
32  *
33  * This utility is useful if you want to bind a [repeatWhenAttached] invocation to the lifetime of a
34  * coroutine, such that cancelling the coroutine cleans up the handle. For example:
35  * ```
36  * myFlow.collectLatest { value ->
37  *     val disposableHandle = myView.repeatWhenAttached { doStuff() }
38  *     doSomethingWith(value)
39  *     // un-bind when done
40  *     disposableHandle.awaitCancellationThenDispose()
41  * }
42  * ```
43  */
awaitCancellationThenDisposenull44 suspend fun DisposableHandle.awaitCancellationThenDispose() {
45     try {
46         awaitCancellation()
47     } finally {
48         dispose()
49     }
50 }
51 
52 /**
53  * This will [launch], run [onLaunch] to get a [DisposableHandle], and finally
54  * [awaitCancellationThenDispose][DisposableHandle.awaitCancellationThenDispose]. This can be used
55  * to structure self-disposing code which attaches listeners, for example in ViewBinders:
56  * ```
57  * suspend fun bind(view: MyView, viewModel: MyViewModel) = coroutineScope {
58  *     launchAndDispose {
59  *         view.setOnClickListener { viewModel.handleClick() }
60  *         DisposableHandle { view.setOnClickListener(null) }
61  *     }
62  * }
63  * ```
64  */
launchAndDisposenull65 inline fun CoroutineScope.launchAndDispose(
66     context: CoroutineContext = EmptyCoroutineContext,
67     start: CoroutineStart = CoroutineStart.DEFAULT,
68     crossinline onLaunch: () -> DisposableHandle
69 ): Job = launch(context, start) { onLaunch().awaitCancellationThenDispose() }
70