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.unfold
18 
19 import android.os.Handler
20 import com.android.systemui.unfold.config.UnfoldTransitionConfig
21 import com.android.systemui.unfold.dagger.UnfoldBg
22 import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
23 import com.android.systemui.unfold.dagger.UnfoldMain
24 import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
25 import com.android.systemui.unfold.progress.MainThreadUnfoldTransitionProgressProvider
26 import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
27 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
28 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
29 import com.android.systemui.unfold.updates.FoldStateProvider
30 import com.android.systemui.unfold.updates.RotationChangeProvider
31 import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
32 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
33 import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
34 import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
35 import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
36 import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
37 import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManagerImpl
38 import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
39 import dagger.BindsOptionalOf
40 import dagger.Module
41 import dagger.Provides
42 import java.util.Optional
43 import javax.inject.Provider
44 import javax.inject.Singleton
45 import kotlin.jvm.optionals.getOrDefault
46 
47 @Module(
48     includes =
49         [
50             UnfoldFlagsModule::class,
51             UnfoldSharedInternalModule::class,
52             UnfoldRotationProviderInternalModule::class,
53             HingeAngleProviderInternalModule::class,
54             FoldStateProviderModule::class,
55         ]
56 )
57 class UnfoldSharedModule {
58     @Provides
59     @Singleton
unfoldKeyguardVisibilityProvidernull60     fun unfoldKeyguardVisibilityProvider(
61         impl: UnfoldKeyguardVisibilityManagerImpl
62     ): UnfoldKeyguardVisibilityProvider = impl
63 
64     @Provides
65     @Singleton
66     fun unfoldKeyguardVisibilityManager(
67         impl: UnfoldKeyguardVisibilityManagerImpl
68     ): UnfoldKeyguardVisibilityManager = impl
69 }
70 
71 @Module
72 abstract class UnfoldFlagsModule {
73     /**
74      * Users of the library can bind this boolean to notify whether the progress should be
75      * calculated only in the background (and the main thread provider is generated by posting the
76      * background events in the main handler).
77      */
78     @BindsOptionalOf @UnfoldBgProgressFlag abstract fun unfoldBgProgressFlag(): Boolean
79 }
80 
81 /**
82  * Needed as methods inside must be public, but their parameters can be internal (and, a public
83  * method can't have internal parameters). Making the module internal and included in a public one
84  * fixes the issue.
85  */
86 @Module
87 internal class UnfoldSharedInternalModule {
88     @Provides
89     @Singleton
unfoldTransitionProgressProvidernull90     fun unfoldTransitionProgressProvider(
91         config: UnfoldTransitionConfig,
92         scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
93         tracingListener: ATraceLoggerTransitionProgressListener.Factory,
94         physicsBasedUnfoldTransitionProgressProvider:
95             PhysicsBasedUnfoldTransitionProgressProvider.Factory,
96         fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
97         foldStateProvider: FoldStateProvider,
98         @UnfoldMain mainHandler: Handler,
99         mainThreadUnfoldTransitionProgressProviderFactory:
100             MainThreadUnfoldTransitionProgressProvider.Factory,
101         @UnfoldBg bgProvider: Provider<Optional<UnfoldTransitionProgressProvider>>,
102         @UnfoldBgProgressFlag unfoldBgProgressFlag: Optional<Boolean>,
103     ): Optional<UnfoldTransitionProgressProvider> {
104         if (unfoldBgProgressFlag.getOrDefault(false)) {
105             // In this case, we wrap the background progress provider
106             val mainThreadProvider: Optional<UnfoldTransitionProgressProvider> =
107                 bgProvider.get().map {
108                     mainThreadUnfoldTransitionProgressProviderFactory.create(it)
109                 }
110             mainThreadProvider.ifPresent {
111                 it.addCallback(tracingListener.create("MainThreadFromBgProgress"))
112             }
113             return mainThreadProvider
114         } else {
115             // TODO(b/277879146): Remove this once unfold_animation_background_progress is launched.
116             return createOptionalUnfoldTransitionProgressProvider(
117                 config = config,
118                 scaleAwareProviderFactory = scaleAwareProviderFactory,
119                 tracingListener = tracingListener.create("MainThread"),
120                 physicsBasedUnfoldTransitionProgressProvider =
121                     physicsBasedUnfoldTransitionProgressProvider,
122                 fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
123                 foldStateProvider = foldStateProvider,
124                 progressHandler = mainHandler,
125             )
126         }
127     }
128 
129     @Provides
130     @Singleton
131     @UnfoldBg
unfoldBgTransitionProgressProvidernull132     fun unfoldBgTransitionProgressProvider(
133         config: UnfoldTransitionConfig,
134         scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
135         tracingListener: ATraceLoggerTransitionProgressListener.Factory,
136         physicsBasedUnfoldTransitionProgressProvider:
137             PhysicsBasedUnfoldTransitionProgressProvider.Factory,
138         fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
139         @UnfoldBg bgFoldStateProvider: FoldStateProvider,
140         @UnfoldBg bgHandler: Handler,
141     ): Optional<UnfoldTransitionProgressProvider> {
142         return createOptionalUnfoldTransitionProgressProvider(
143             config = config,
144             scaleAwareProviderFactory = scaleAwareProviderFactory,
145             tracingListener = tracingListener.create("BgThread"),
146             physicsBasedUnfoldTransitionProgressProvider =
147                 physicsBasedUnfoldTransitionProgressProvider,
148             fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
149             foldStateProvider = bgFoldStateProvider,
150             progressHandler = bgHandler,
151         )
152     }
153 
createOptionalUnfoldTransitionProgressProvidernull154     private fun createOptionalUnfoldTransitionProgressProvider(
155         config: UnfoldTransitionConfig,
156         scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
157         tracingListener: ATraceLoggerTransitionProgressListener,
158         physicsBasedUnfoldTransitionProgressProvider:
159             PhysicsBasedUnfoldTransitionProgressProvider.Factory,
160         fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
161         foldStateProvider: FoldStateProvider,
162         progressHandler: Handler,
163     ): Optional<UnfoldTransitionProgressProvider> {
164         if (!config.isEnabled) {
165             return Optional.empty()
166         }
167         val baseProgressProvider =
168             if (config.isHingeAngleEnabled) {
169                 physicsBasedUnfoldTransitionProgressProvider.create(
170                     foldStateProvider,
171                     progressHandler
172                 )
173             } else {
174                 fixedTimingTransitionProgressProvider.get()
175             }
176 
177         return Optional.of(
178             scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
179                 // Always present callback that logs animation beginning and end.
180                 addCallback(tracingListener)
181             }
182         )
183     }
184 
185     @Provides
186     @Singleton
provideProgressForwardernull187     fun provideProgressForwarder(
188         config: UnfoldTransitionConfig,
189         progressForwarder: Provider<UnfoldTransitionProgressForwarder>
190     ): Optional<UnfoldTransitionProgressForwarder> {
191         if (!config.isEnabled) {
192             return Optional.empty()
193         }
194         return Optional.of(progressForwarder.get())
195     }
196 }
197 
198 /**
199  * Provides [FoldStateProvider]. The [UnfoldBg] annotated binding sends progress in the [UnfoldBg]
200  * handler.
201  */
202 @Module
203 internal class FoldStateProviderModule {
204     @Provides
205     @Singleton
provideFoldStateProvidernull206     fun provideFoldStateProvider(
207         factory: DeviceFoldStateProvider.Factory,
208         @UnfoldMain hingeAngleProvider: HingeAngleProvider,
209         @UnfoldMain rotationChangeProvider: RotationChangeProvider,
210         @UnfoldMain mainHandler: Handler,
211     ): FoldStateProvider =
212         factory.create(
213             hingeAngleProvider,
214             rotationChangeProvider,
215             progressHandler = mainHandler
216         )
217 
218     @Provides
219     @Singleton
220     @UnfoldBg
221     fun provideBgFoldStateProvider(
222         factory: DeviceFoldStateProvider.Factory,
223         @UnfoldBg hingeAngleProvider: HingeAngleProvider,
224         @UnfoldBg rotationChangeProvider: RotationChangeProvider,
225         @UnfoldBg bgHandler: Handler,
226     ): FoldStateProvider =
227         factory.create(
228             hingeAngleProvider,
229             rotationChangeProvider,
230             progressHandler = bgHandler
231         )
232 }
233 
234 /** Provides bindings for both [UnfoldMain] and [UnfoldBg] [HingeAngleProvider]. */
235 @Module
236 internal class HingeAngleProviderInternalModule {
237     @Provides
238     @UnfoldMain
239     fun hingeAngleProvider(
240         config: UnfoldTransitionConfig,
241         @UnfoldMain handler: Handler,
242         hingeAngleSensorProvider: HingeSensorAngleProvider.Factory
243     ): HingeAngleProvider {
244         return if (config.isHingeAngleEnabled) {
245             hingeAngleSensorProvider.create(handler)
246         } else {
247             EmptyHingeAngleProvider
248         }
249     }
250 
251     @Provides
252     @UnfoldBg
253     fun hingeAngleProviderBg(
254         config: UnfoldTransitionConfig,
255         @UnfoldBg handler: Handler,
256         hingeAngleSensorProvider: HingeSensorAngleProvider.Factory
257     ): HingeAngleProvider {
258         return if (config.isHingeAngleEnabled) {
259             hingeAngleSensorProvider.create(handler)
260         } else {
261             EmptyHingeAngleProvider
262         }
263     }
264 }
265 
266 @Module
267 internal class UnfoldRotationProviderInternalModule {
268     @Provides
269     @Singleton
270     @UnfoldMain
provideRotationChangeProvidernull271     fun provideRotationChangeProvider(
272         rotationChangeProviderFactory: RotationChangeProvider.Factory,
273         @UnfoldMain callbackHandler: Handler,
274     ): RotationChangeProvider {
275         return rotationChangeProviderFactory.create(callbackHandler)
276     }
277 
278     @Provides
279     @Singleton
280     @UnfoldBg
provideBgRotationChangeProvidernull281     fun provideBgRotationChangeProvider(
282         rotationChangeProviderFactory: RotationChangeProvider.Factory,
283         @UnfoldBg callbackHandler: Handler,
284     ): RotationChangeProvider {
285         // For UnfoldBg RotationChangeProvider we use bgHandler as callbackHandler
286         return rotationChangeProviderFactory.create(callbackHandler)
287     }
288 }
289