/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.unfold import android.os.Handler import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.dagger.UnfoldBg import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider import com.android.systemui.unfold.progress.MainThreadUnfoldTransitionProgressProvider import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder import com.android.systemui.unfold.updates.DeviceFoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.RotationChangeProvider import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManagerImpl import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider import dagger.BindsOptionalOf import dagger.Module import dagger.Provides import java.util.Optional import javax.inject.Provider import javax.inject.Singleton import kotlin.jvm.optionals.getOrDefault @Module( includes = [ UnfoldFlagsModule::class, UnfoldSharedInternalModule::class, UnfoldRotationProviderInternalModule::class, HingeAngleProviderInternalModule::class, FoldStateProviderModule::class, ] ) class UnfoldSharedModule { @Provides @Singleton fun unfoldKeyguardVisibilityProvider( impl: UnfoldKeyguardVisibilityManagerImpl ): UnfoldKeyguardVisibilityProvider = impl @Provides @Singleton fun unfoldKeyguardVisibilityManager( impl: UnfoldKeyguardVisibilityManagerImpl ): UnfoldKeyguardVisibilityManager = impl } @Module abstract class UnfoldFlagsModule { /** * Users of the library can bind this boolean to notify whether the progress should be * calculated only in the background (and the main thread provider is generated by posting the * background events in the main handler). */ @BindsOptionalOf @UnfoldBgProgressFlag abstract fun unfoldBgProgressFlag(): Boolean } /** * Needed as methods inside must be public, but their parameters can be internal (and, a public * method can't have internal parameters). Making the module internal and included in a public one * fixes the issue. */ @Module internal class UnfoldSharedInternalModule { @Provides @Singleton fun unfoldTransitionProgressProvider( config: UnfoldTransitionConfig, scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory, tracingListener: ATraceLoggerTransitionProgressListener.Factory, physicsBasedUnfoldTransitionProgressProvider: PhysicsBasedUnfoldTransitionProgressProvider.Factory, fixedTimingTransitionProgressProvider: Provider, foldStateProvider: FoldStateProvider, @UnfoldMain mainHandler: Handler, mainThreadUnfoldTransitionProgressProviderFactory: MainThreadUnfoldTransitionProgressProvider.Factory, @UnfoldBg bgProvider: Provider>, @UnfoldBgProgressFlag unfoldBgProgressFlag: Optional, ): Optional { if (unfoldBgProgressFlag.getOrDefault(false)) { // In this case, we wrap the background progress provider val mainThreadProvider: Optional = bgProvider.get().map { mainThreadUnfoldTransitionProgressProviderFactory.create(it) } mainThreadProvider.ifPresent { it.addCallback(tracingListener.create("MainThreadFromBgProgress")) } return mainThreadProvider } else { // TODO(b/277879146): Remove this once unfold_animation_background_progress is launched. return createOptionalUnfoldTransitionProgressProvider( config = config, scaleAwareProviderFactory = scaleAwareProviderFactory, tracingListener = tracingListener.create("MainThread"), physicsBasedUnfoldTransitionProgressProvider = physicsBasedUnfoldTransitionProgressProvider, fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider, foldStateProvider = foldStateProvider, progressHandler = mainHandler, ) } } @Provides @Singleton @UnfoldBg fun unfoldBgTransitionProgressProvider( config: UnfoldTransitionConfig, scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory, tracingListener: ATraceLoggerTransitionProgressListener.Factory, physicsBasedUnfoldTransitionProgressProvider: PhysicsBasedUnfoldTransitionProgressProvider.Factory, fixedTimingTransitionProgressProvider: Provider, @UnfoldBg bgFoldStateProvider: FoldStateProvider, @UnfoldBg bgHandler: Handler, ): Optional { return createOptionalUnfoldTransitionProgressProvider( config = config, scaleAwareProviderFactory = scaleAwareProviderFactory, tracingListener = tracingListener.create("BgThread"), physicsBasedUnfoldTransitionProgressProvider = physicsBasedUnfoldTransitionProgressProvider, fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider, foldStateProvider = bgFoldStateProvider, progressHandler = bgHandler, ) } private fun createOptionalUnfoldTransitionProgressProvider( config: UnfoldTransitionConfig, scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory, tracingListener: ATraceLoggerTransitionProgressListener, physicsBasedUnfoldTransitionProgressProvider: PhysicsBasedUnfoldTransitionProgressProvider.Factory, fixedTimingTransitionProgressProvider: Provider, foldStateProvider: FoldStateProvider, progressHandler: Handler, ): Optional { if (!config.isEnabled) { return Optional.empty() } val baseProgressProvider = if (config.isHingeAngleEnabled) { physicsBasedUnfoldTransitionProgressProvider.create( foldStateProvider, progressHandler ) } else { fixedTimingTransitionProgressProvider.get() } return Optional.of( scaleAwareProviderFactory.wrap(baseProgressProvider).apply { // Always present callback that logs animation beginning and end. addCallback(tracingListener) } ) } @Provides @Singleton fun provideProgressForwarder( config: UnfoldTransitionConfig, progressForwarder: Provider ): Optional { if (!config.isEnabled) { return Optional.empty() } return Optional.of(progressForwarder.get()) } } /** * Provides [FoldStateProvider]. The [UnfoldBg] annotated binding sends progress in the [UnfoldBg] * handler. */ @Module internal class FoldStateProviderModule { @Provides @Singleton fun provideFoldStateProvider( factory: DeviceFoldStateProvider.Factory, @UnfoldMain hingeAngleProvider: HingeAngleProvider, @UnfoldMain rotationChangeProvider: RotationChangeProvider, @UnfoldMain mainHandler: Handler, ): FoldStateProvider = factory.create( hingeAngleProvider, rotationChangeProvider, progressHandler = mainHandler ) @Provides @Singleton @UnfoldBg fun provideBgFoldStateProvider( factory: DeviceFoldStateProvider.Factory, @UnfoldBg hingeAngleProvider: HingeAngleProvider, @UnfoldBg rotationChangeProvider: RotationChangeProvider, @UnfoldBg bgHandler: Handler, ): FoldStateProvider = factory.create( hingeAngleProvider, rotationChangeProvider, progressHandler = bgHandler ) } /** Provides bindings for both [UnfoldMain] and [UnfoldBg] [HingeAngleProvider]. */ @Module internal class HingeAngleProviderInternalModule { @Provides @UnfoldMain fun hingeAngleProvider( config: UnfoldTransitionConfig, @UnfoldMain handler: Handler, hingeAngleSensorProvider: HingeSensorAngleProvider.Factory ): HingeAngleProvider { return if (config.isHingeAngleEnabled) { hingeAngleSensorProvider.create(handler) } else { EmptyHingeAngleProvider } } @Provides @UnfoldBg fun hingeAngleProviderBg( config: UnfoldTransitionConfig, @UnfoldBg handler: Handler, hingeAngleSensorProvider: HingeSensorAngleProvider.Factory ): HingeAngleProvider { return if (config.isHingeAngleEnabled) { hingeAngleSensorProvider.create(handler) } else { EmptyHingeAngleProvider } } } @Module internal class UnfoldRotationProviderInternalModule { @Provides @Singleton @UnfoldMain fun provideRotationChangeProvider( rotationChangeProviderFactory: RotationChangeProvider.Factory, @UnfoldMain callbackHandler: Handler, ): RotationChangeProvider { return rotationChangeProviderFactory.create(callbackHandler) } @Provides @Singleton @UnfoldBg fun provideBgRotationChangeProvider( rotationChangeProviderFactory: RotationChangeProvider.Factory, @UnfoldBg callbackHandler: Handler, ): RotationChangeProvider { // For UnfoldBg RotationChangeProvider we use bgHandler as callbackHandler return rotationChangeProviderFactory.create(callbackHandler) } }