Enhance dagger usage in UnfoldSharedModule

UnfoldTransitionProgressProvider is used in sysui and in other places that don't use dagger (e.g. Launcher).
However, we want to use dagger for all unfold transition related classes.
The solution in this cl is to create FoldSharedComponent. If an app that doesn't use dagger need UnfoldTransitionProgressProvider (or other shared classes), it can create the component with the factory, and get those object (now created by dagger, previously manually created).
If the app uses dagger, UnfoldSharedModule should be used instead.

The external interface is not changed in this cl: createUnfoldTransitionProgressProvider is left as it was before, therefore launcher doesn't require any change and continues to work as before.

Test: atest com.android.systemui.unfold
Test: Manually tested
Bug: 213908821
Change-Id: I22885ec15fed99254405e26ed2fe99a9567625b6
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index d172006..3cf5bc1 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -49,9 +49,12 @@
         "PluginCoreLib",
         "androidx.dynamicanimation_dynamicanimation",
         "androidx.concurrent_concurrent-futures",
+        "dagger2",
+        "jsr330",
     ],
     java_version: "1.8",
     min_sdk_version: "current",
+    plugins: ["dagger2-compiler"],
 }
 
 java_library {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java
new file mode 100644
index 0000000..7b09774
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 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.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface Main {
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
new file mode 100644
index 0000000..ac62cf9
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.content.ContentResolver
+import android.content.Context
+import android.hardware.SensorManager
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
+import dagger.BindsInstance
+import dagger.Component
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Singleton
+
+/**
+ * Provides [UnfoldTransitionProgressProvider]. The [Optional] is empty when the transition
+ * animation is disabled.
+ *
+ * This component is meant to be used for places that don't use dagger. By providing those
+ * parameters to the factory, all dagger objects are correctly instantiated. See
+ * [createUnfoldTransitionProgressProvider] for an example.
+ */
+@Singleton
+@Component(modules = [UnfoldSharedModule::class])
+internal interface UnfoldSharedComponent {
+
+    @Component.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance context: Context,
+            @BindsInstance config: UnfoldTransitionConfig,
+            @BindsInstance screenStatusProvider: ScreenStatusProvider,
+            @BindsInstance deviceStateManager: DeviceStateManager,
+            @BindsInstance sensorManager: SensorManager,
+            @BindsInstance @Main handler: Handler,
+            @BindsInstance @Main executor: Executor,
+            @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
+            @BindsInstance contentResolver: ContentResolver = context.contentResolver
+        ): UnfoldSharedComponent
+    }
+
+    val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt
new file mode 100644
index 0000000..23e4c97
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.hardware.SensorManager
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.updates.DeviceFoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider
+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 dagger.Module
+import dagger.Provides
+import java.util.Optional
+import javax.inject.Singleton
+
+@Module
+class UnfoldSharedModule {
+    @Provides
+    @Singleton
+    fun unfoldTransitionProgressProvider(
+        config: UnfoldTransitionConfig,
+        scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+        tracingListener: ATraceLoggerTransitionProgressListener,
+        foldStateProvider: FoldStateProvider
+    ): Optional<UnfoldTransitionProgressProvider> =
+        if (!config.isEnabled) {
+            Optional.empty()
+        } else {
+            val baseProgressProvider =
+                if (config.isHingeAngleEnabled) {
+                    PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
+                } else {
+                    FixedTimingTransitionProgressProvider(foldStateProvider)
+                }
+            Optional.of(
+                scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
+                    // Always present callback that logs animation beginning and end.
+                    addCallback(tracingListener)
+                })
+        }
+
+    @Provides
+    @Singleton
+    fun provideFoldStateProvider(
+        deviceFoldStateProvider: DeviceFoldStateProvider
+    ): FoldStateProvider = deviceFoldStateProvider
+
+    @Provides
+    fun hingeAngleProvider(
+        config: UnfoldTransitionConfig,
+        sensorManager: SensorManager
+    ): HingeAngleProvider =
+        if (config.isHingeAngleEnabled) {
+            HingeSensorAngleProvider(sensorManager)
+        } else {
+            EmptyHingeAngleProvider
+        }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 953b0e0..d5d6362 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -23,22 +23,16 @@
 import android.os.Handler
 import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
-import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
-import com.android.systemui.unfold.updates.DeviceFoldStateProvider
-import com.android.systemui.unfold.updates.FoldStateProvider
-import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
-import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
-import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
-import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
 import java.util.concurrent.Executor
 
 /**
  * Factory for [UnfoldTransitionProgressProvider].
  *
- * This is needed as Launcher has to create the object manually. Sysui create it using dagger (see
- * [UnfoldTransitionModule]).
+ * This is needed as Launcher has to create the object manually. If dagger is available, this object
+ * is provided in [UnfoldSharedModule].
+ *
+ * This should **never** be called from sysui, as the object is already provided in that process.
  */
 fun createUnfoldTransitionProgressProvider(
     context: Context,
@@ -49,62 +43,21 @@
     mainHandler: Handler,
     mainExecutor: Executor,
     tracingTagPrefix: String
-): UnfoldTransitionProgressProvider {
-
-    if (!config.isEnabled) {
-        throw IllegalStateException(
-            "Trying to create " +
-                "UnfoldTransitionProgressProvider when the transition is disabled")
-    }
-
-    val foldStateProvider =
-        createFoldStateProvider(
+): UnfoldTransitionProgressProvider =
+    DaggerUnfoldSharedComponent.factory()
+        .create(
             context,
             config,
             screenStatusProvider,
             deviceStateManager,
             sensorManager,
             mainHandler,
-            mainExecutor)
-
-    val unfoldTransitionProgressProvider =
-        if (config.isHingeAngleEnabled) {
-            PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
-        } else {
-            FixedTimingTransitionProgressProvider(foldStateProvider)
-        }
-
-    return ScaleAwareTransitionProgressProvider(
-            unfoldTransitionProgressProvider, context.contentResolver)
-        .apply {
-            // Always present callback that logs animation beginning and end.
-            addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
-        }
-}
-
-fun createFoldStateProvider(
-    context: Context,
-    config: UnfoldTransitionConfig,
-    screenStatusProvider: ScreenStatusProvider,
-    deviceStateManager: DeviceStateManager,
-    sensorManager: SensorManager,
-    mainHandler: Handler,
-    mainExecutor: Executor
-): FoldStateProvider {
-    val hingeAngleProvider =
-        if (config.isHingeAngleEnabled) {
-            HingeSensorAngleProvider(sensorManager)
-        } else {
-            EmptyHingeAngleProvider()
-        }
-
-    return DeviceFoldStateProvider(
-        context,
-        hingeAngleProvider,
-        screenStatusProvider,
-        deviceStateManager,
-        mainExecutor,
-        mainHandler)
-}
+            mainExecutor,
+            tracingTagPrefix)
+        .unfoldTransitionProvider
+        .orElse(null)
+        ?: throw IllegalStateException(
+            "Trying to create " +
+                "UnfoldTransitionProgressProvider when the transition is disabled")
 
 fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index cd1ea21..204ae09 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -22,6 +22,7 @@
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import androidx.core.util.Consumer
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
@@ -29,14 +30,17 @@
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import java.util.concurrent.Executor
+import javax.inject.Inject
 
-class DeviceFoldStateProvider(
+class DeviceFoldStateProvider
+@Inject
+constructor(
     context: Context,
     private val hingeAngleProvider: HingeAngleProvider,
     private val screenStatusProvider: ScreenStatusProvider,
     private val deviceStateManager: DeviceStateManager,
-    private val mainExecutor: Executor,
-    private val handler: Handler
+    @Main private val mainExecutor: Executor,
+    @Main private val handler: Handler
 ) : FoldStateProvider {
 
     private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
index 9b58b1f..4ca1a53 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -2,7 +2,7 @@
 
 import androidx.core.util.Consumer
 
-internal class EmptyHingeAngleProvider : HingeAngleProvider {
+internal object EmptyHingeAngleProvider : HingeAngleProvider {
     override fun start() {
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
similarity index 76%
rename from packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
index f3eeb32..1574c8d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
@@ -2,6 +2,8 @@
 
 import android.os.Trace
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import javax.inject.Inject
+import javax.inject.Qualifier
 
 /**
  * Listener that logs start and end of the fold-unfold transition.
@@ -9,7 +11,10 @@
  * [tracePrefix] arg helps in differentiating those. Currently, this is expected to be logged twice
  * for each fold/unfold: in (1) systemui and (2) launcher process.
  */
-class ATraceLoggerTransitionProgressListener(tracePrefix: String) : TransitionProgressListener {
+class ATraceLoggerTransitionProgressListener
+@Inject
+internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String) :
+    TransitionProgressListener {
 
     private val traceName = "$tracePrefix#$UNFOLD_TRANSITION_TRACE_NAME"
 
@@ -27,3 +32,5 @@
 }
 
 private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
+
+@Qualifier annotation class UnfoldTransitionATracePrefix
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index df9078a..ee79b87 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -6,15 +6,20 @@
 import android.provider.Settings
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
 /** Wraps [UnfoldTransitionProgressProvider] to disable transitions when animations are disabled. */
-class ScaleAwareTransitionProgressProvider(
-    unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+class ScaleAwareTransitionProgressProvider
+@AssistedInject
+constructor(
+    @Assisted progressProviderToWrap: UnfoldTransitionProgressProvider,
     private val contentResolver: ContentResolver
 ) : UnfoldTransitionProgressProvider {
 
     private val scopedUnfoldTransitionProgressProvider =
-            ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+        ScopedUnfoldTransitionProgressProvider(progressProviderToWrap)
 
     private val animatorDurationScaleObserver = object : ContentObserver(null) {
         override fun onChange(selfChange: Boolean) {
@@ -47,4 +52,11 @@
         contentResolver.unregisterContentObserver(animatorDurationScaleObserver)
         scopedUnfoldTransitionProgressProvider.destroy()
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun wrap(
+            progressProvider: UnfoldTransitionProgressProvider
+        ): ScaleAwareTransitionProgressProvider
+    }
 }