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
This commit is contained in:
Nicolo' Mazzucato
2022-01-10 19:56:02 +01:00
parent 8fe59ba86d
commit 6fb25a6665
10 changed files with 200 additions and 128 deletions

View File

@@ -49,9 +49,12 @@ android_library {
"PluginCoreLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
"dagger2",
"jsr330",
],
java_version: "1.8",
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
}
java_library {

View File

@@ -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>
}

View File

@@ -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
}
}

View File

@@ -23,22 +23,16 @@ import android.hardware.devicestate.DeviceStateManager
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 @@ fun createUnfoldTransitionProgressProvider(
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)

View File

@@ -22,6 +22,7 @@ import android.os.Handler
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.FULLY_OPEN_DEGREES
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()

View File

@@ -2,7 +2,7 @@ package com.android.systemui.unfold.updates.hinge
import androidx.core.util.Consumer
internal class EmptyHingeAngleProvider : HingeAngleProvider {
internal object EmptyHingeAngleProvider : HingeAngleProvider {
override fun start() {
}

View File

@@ -2,6 +2,8 @@ package com.android.systemui.unfold.util
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 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr
* [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 @@ class ATraceLoggerTransitionProgressListener(tracePrefix: String) : TransitionPr
}
private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
@Qualifier annotation class UnfoldTransitionATracePrefix

View File

@@ -6,15 +6,20 @@ import android.database.ContentObserver
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 @@ class ScaleAwareTransitionProgressProvider(
contentResolver.unregisterContentObserver(animatorDurationScaleObserver)
scopedUnfoldTransitionProgressProvider.destroy()
}
@AssistedFactory
interface Factory {
fun wrap(
progressProvider: UnfoldTransitionProgressProvider
): ScaleAwareTransitionProgressProvider
}
}

View File

@@ -17,87 +17,38 @@
package com.android.systemui.unfold
import android.content.Context
import android.hardware.SensorManager
import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import android.view.IWindowManager
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
import com.android.systemui.util.time.SystemClockImpl
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
import dagger.Lazy
import dagger.Module
import dagger.Provides
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Named
import javax.inject.Singleton
@Module
@Module(includes = [UnfoldSharedModule::class])
class UnfoldTransitionModule {
@Provides
@Singleton
fun provideUnfoldTransitionProgressProvider(
context: Context,
config: UnfoldTransitionConfig,
screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
deviceStateManager: DeviceStateManager,
sensorManager: SensorManager,
@Main executor: Executor,
@Main handler: Handler
): Optional<UnfoldTransitionProgressProvider> =
if (config.isEnabled) {
Optional.of(
createUnfoldTransitionProgressProvider(
context,
config,
screenStatusProvider.get(),
deviceStateManager,
sensorManager,
handler,
executor,
tracingTagPrefix = "systemui"))
} else {
Optional.empty()
}
@Provides
@Singleton
fun provideFoldStateProvider(
context: Context,
config: UnfoldTransitionConfig,
screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
deviceStateManager: DeviceStateManager,
sensorManager: SensorManager,
@Main executor: Executor,
@Main handler: Handler
): Optional<FoldStateProvider> =
if (!config.isHingeAngleEnabled) {
Optional.empty()
} else {
Optional.of(
createFoldStateProvider(
context,
config,
screenStatusProvider.get(),
deviceStateManager,
sensorManager,
handler,
executor))
}
@Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
@Provides
@Singleton
fun providesFoldStateLoggingProvider(
optionalFoldStateProvider: Optional<FoldStateProvider>
config: UnfoldTransitionConfig,
foldStateProvider: Lazy<FoldStateProvider>
): Optional<FoldStateLoggingProvider> =
optionalFoldStateProvider.map { foldStateProvider ->
FoldStateLoggingProviderImpl(foldStateProvider, SystemClockImpl())
if (config.isHingeAngleEnabled) {
Optional.of(FoldStateLoggingProviderImpl(foldStateProvider.get(), SystemClockImpl()))
} else {
Optional.empty()
}
@Provides
@@ -135,6 +86,9 @@ class UnfoldTransitionModule {
} else {
ShellUnfoldProgressProvider.NO_PROVIDER
}
@Provides
fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
}
const val UNFOLD_STATUS_BAR = "unfold_status_bar"