• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (C) 2022 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 android.view.View
20 import androidx.lifecycle.Lifecycle
21 import androidx.lifecycle.coroutineScope
22 import androidx.lifecycle.repeatOnLifecycle
23 import com.android.app.tracing.coroutines.launchTraced as launch
24 import com.android.systemui.dagger.SysUISingleton
25 import com.android.systemui.dagger.qualifiers.Application
26 import com.android.systemui.lifecycle.repeatWhenAttached
27 import java.util.function.Consumer
28 import javax.inject.Inject
29 import kotlin.coroutines.CoroutineContext
30 import kotlin.coroutines.EmptyCoroutineContext
31 import kotlin.coroutines.cancellation.CancellationException
32 import kotlinx.coroutines.CoroutineScope
33 import kotlinx.coroutines.DisposableHandle
34 import kotlinx.coroutines.Job
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.SharingStarted
37 import kotlinx.coroutines.flow.StateFlow
38 import kotlinx.coroutines.flow.combine
39 import kotlinx.coroutines.flow.stateIn
40 import kotlinx.coroutines.plus
41 
42 /** A class allowing Java classes to collect on Kotlin flows. */
43 @SysUISingleton
44 class JavaAdapter @Inject constructor(@Application private val scope: CoroutineScope) {
45     /**
46      * Collect information for the given [flow], calling [consumer] for each emitted event.
47      *
48      * Important: This will immediately start collection and *never* stop it. This should only be
49      * used by classes that *need* to always be collecting a value and processing it. Whenever
50      * possible, please use [collectFlow] instead; that method will stop the collection when a view
51      * has disappeared, which will ensure that we don't perform unnecessary work.
52      *
53      * Do *not* call this method in a class's constructor. Instead, call it in
54      * [com.android.systemui.CoreStartable.start] or similar method.
55      */
alwaysCollectFlownull56     fun <T> alwaysCollectFlow(flow: Flow<T>, consumer: Consumer<T>): Job {
57         return scope.launch { flow.collect { consumer.accept(it) } }
58     }
59 
60     @JvmOverloads
stateInAppnull61     fun <T> stateInApp(
62         flow: Flow<T>,
63         initialValue: T,
64         started: SharingStarted = SharingStarted.Eagerly,
65     ): StateFlow<T> {
66         return flow.stateIn(scope, started, initialValue)
67     }
68 
69     /** Call suspend functions from Java */
callSuspendnull70     fun <T, R> callSuspend(
71         suspendFunction: suspend (T) -> R,
72         arg: T,
73         onSuccess: (R) -> Unit,
74         onCancel: (CancellationException) -> Unit,
75         onFailure: (Throwable) -> Unit,
76     ): Job =
77         scope.launch {
78             val result =
79                 try {
80                     suspendFunction(arg)
81                 } catch (ex: CancellationException) {
82                     onCancel(ex)
83                     return@launch
84                 } catch (ex: Throwable) {
85                     onFailure(ex)
86                     return@launch
87                 }
88             onSuccess(result)
89         }
90 }
91 
92 /**
93  * Collect information for the given [flow], calling [consumer] for each emitted event. Defaults to
94  * [LifeCycle.State.CREATED] to better align with legacy ViewController usage of attaching listeners
95  * during onViewAttached() and removing during onViewRemoved().
96  *
97  * @return a disposable handle in order to cancel the flow in the future.
98  */
99 @JvmOverloads
collectFlownull100 fun <T> collectFlow(
101     view: View,
102     flow: Flow<T>,
103     consumer: Consumer<T>,
104     coroutineContext: CoroutineContext = EmptyCoroutineContext,
105     state: Lifecycle.State = Lifecycle.State.CREATED,
106 ): DisposableHandle {
107     return view.repeatWhenAttached(coroutineContext) {
108         repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } }
109     }
110 }
111 
112 /**
113  * Collect information for the given [flow], calling [consumer] for each emitted event. Defaults to
114  * [LifeCycle.State.CREATED] which is mapped over from the equivalent definition for collecting the
115  * flow on a view.
116  */
117 @JvmOverloads
collectFlownull118 fun <T> collectFlow(
119     lifecycle: Lifecycle,
120     flow: Flow<T>,
121     consumer: Consumer<T>,
122     state: Lifecycle.State = Lifecycle.State.CREATED,
123 ): Job {
124     return lifecycle.coroutineScope.launch {
125         lifecycle.repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } }
126     }
127 }
128 
129 /**
130  * Collect information for the given [flow], calling [consumer] for each emitted event on the
131  * specified [collectContext].
132  *
133  * Collection will continue until the given [scope] is cancelled.
134  */
135 @JvmOverloads
collectFlownull136 fun <T> collectFlow(
137     scope: CoroutineScope,
138     collectContext: CoroutineContext = scope.coroutineContext,
139     flow: Flow<T>,
140     consumer: Consumer<T>,
141 ): Job {
142     return scope.plus(collectContext).launch { flow.collect { consumer.accept(it) } }
143 }
144 
combineFlowsnull145 fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> {
146     return combine(flow1, flow2, bifunction)
147 }
148 
combineFlowsnull149 fun <A, B, C, R> combineFlows(
150     flow1: Flow<A>,
151     flow2: Flow<B>,
152     flow3: Flow<C>,
153     trifunction: (A, B, C) -> R,
154 ): Flow<R> {
155     return combine(flow1, flow2, flow3, trifunction)
156 }
157 
combineFlowsnull158 fun <T1, T2, T3, T4, R> combineFlows(
159     flow: Flow<T1>,
160     flow2: Flow<T2>,
161     flow3: Flow<T3>,
162     flow4: Flow<T4>,
163     transform: (T1, T2, T3, T4) -> R,
164 ): Flow<R> {
165     return combine(flow, flow2, flow3, flow4, transform)
166 }
167 
combineFlowsnull168 fun <T1, T2, T3, T4, T5, R> combineFlows(
169     flow: Flow<T1>,
170     flow2: Flow<T2>,
171     flow3: Flow<T3>,
172     flow4: Flow<T4>,
173     flow5: Flow<T5>,
174     transform: (T1, T2, T3, T4, T5) -> R,
175 ): Flow<R> {
176     return combine(flow, flow2, flow3, flow4, flow5, transform)
177 }
178 
combineFlowsnull179 fun <T1, T2, T3, T4, T5, T6, R> combineFlows(
180     flow: Flow<T1>,
181     flow2: Flow<T2>,
182     flow3: Flow<T3>,
183     flow4: Flow<T4>,
184     flow5: Flow<T5>,
185     flow6: Flow<T6>,
186     transform: (T1, T2, T3, T4, T5, T6) -> R,
187 ): Flow<R> {
188     return combine(flow, flow2, flow3, flow4, flow5, flow6, transform)
189 }
190