Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit edd5a074 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Create FoldStateLoggingProvider"

parents d65d1745 2e4d663e
Loading
Loading
Loading
Loading
+45 −25
Original line number Diff line number Diff line
@@ -21,24 +21,24 @@ import android.content.Context
import android.hardware.SensorManager
import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
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.util.ScaleAwareTransitionProgressProvider
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 java.lang.IllegalStateException
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. Sysui create it using dagger (see
 * [UnfoldTransitionModule]).
 */
fun createUnfoldTransitionProgressProvider(
    context: Context,
@@ -52,39 +52,59 @@ fun createUnfoldTransitionProgressProvider(
): UnfoldTransitionProgressProvider {

    if (!config.isEnabled) {
        throw IllegalStateException("Trying to create " +
        throw IllegalStateException(
            "Trying to create " +
                "UnfoldTransitionProgressProvider when the transition is disabled")
    }

    val hingeAngleProvider =
        if (config.isHingeAngleEnabled) {
            HingeSensorAngleProvider(sensorManager)
        } else {
            EmptyHingeAngleProvider()
        }

    val foldStateProvider = DeviceFoldStateProvider(
    val foldStateProvider =
        createFoldStateProvider(
            context,
        hingeAngleProvider,
            config,
            screenStatusProvider,
            deviceStateManager,
        mainExecutor,
        mainHandler
    )
            sensorManager,
            mainHandler,
            mainExecutor)

    val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
    val unfoldTransitionProgressProvider =
        if (config.isHingeAngleEnabled) {
            PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
        } else {
            FixedTimingTransitionProgressProvider(foldStateProvider)
        }

    return ScaleAwareTransitionProgressProvider(
        unfoldTransitionProgressProvider,
        context.contentResolver
    ).apply {
            unfoldTransitionProgressProvider, context.contentResolver)
        .apply {
            // Always present callback that logs animation beginning and end.
            addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
        }
}

fun createConfig(context: Context): UnfoldTransitionConfig =
    ResourceUnfoldTransitionConfig(context)
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)
}

fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
+48 −0
Original line number Diff line number Diff line
/*
 * 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.annotation.IntDef
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates

/** Reports device fold states for logging purposes. */
// TODO(b/198305865): Log state changes.
interface FoldStateLoggingProvider : CallbackController<FoldStateLoggingListener> {

    fun init()
    fun uninit()

    interface FoldStateLoggingListener {
        fun onFoldUpdate(foldStateUpdate: FoldStateChange)
    }

    @IntDef(prefix = ["LOGGED_FOLD_STATE_"], value = [FULLY_OPENED, FULLY_CLOSED, HALF_OPENED])
    @Retention(AnnotationRetention.SOURCE)
    annotation class LoggedFoldedStates
}

data class FoldStateChange(
    @LoggedFoldedStates val previous: Int,
    @LoggedFoldedStates val current: Int,
    val dtMillis: Long
)

const val FULLY_OPENED = 1
const val FULLY_CLOSED = 2
const val HALF_OPENED = 3
+108 −0
Original line number Diff line number Diff line
/*
 * 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.util.Log
import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.util.time.SystemClock

/**
 * Reports device fold states for logging purposes.
 *
 * Wraps the state provided by [FoldStateProvider] to output only [FULLY_OPENED], [FULLY_CLOSED] and
 * [HALF_OPENED] for logging purposes.
 *
 * Note that [HALF_OPENED] state is only emitted after the device angle is stable for some timeout.
 * Check [FoldStateProvider] impl for it.
 *
 * This doesn't log the following transitions:
 * - [HALF_OPENED] -> [FULLY_OPENED]: not interesting, as there is no transition going on
 * - [HALF_OPENED] -> [HALF_OPENED]: not meaningful.
 */
class FoldStateLoggingProviderImpl(
    private val foldStateProvider: FoldStateProvider,
    private val clock: SystemClock
) : FoldStateLoggingProvider, FoldStateProvider.FoldUpdatesListener {

    private val outputListeners: MutableList<FoldStateLoggingListener> = mutableListOf()

    @LoggedFoldedStates private var lastState: Int? = null
    private var actionStartMillis: Long? = null

    override fun init() {
        foldStateProvider.addCallback(this)
        foldStateProvider.start()
    }

    override fun uninit() {
        foldStateProvider.removeCallback(this)
        foldStateProvider.stop()
    }

    override fun onHingeAngleUpdate(angle: Float) {}

    override fun onFoldUpdate(@FoldUpdate update: Int) {
        val now = clock.elapsedRealtime()
        when (update) {
            FOLD_UPDATE_START_OPENING -> {
                lastState = FULLY_CLOSED
                actionStartMillis = now
            }
            FOLD_UPDATE_START_CLOSING -> actionStartMillis = now
            FOLD_UPDATE_FINISH_HALF_OPEN -> dispatchState(HALF_OPENED)
            FOLD_UPDATE_FINISH_FULL_OPEN -> dispatchState(FULLY_OPENED)
            FOLD_UPDATE_FINISH_CLOSED -> dispatchState(FULLY_CLOSED)
        }
    }

    private fun dispatchState(@LoggedFoldedStates current: Int) {
        val now = clock.elapsedRealtime()
        val previous = lastState
        val lastActionStart = actionStartMillis

        if (previous != null && previous != current && lastActionStart != null) {
            val time = now - lastActionStart
            val foldStateChange = FoldStateChange(previous, current, time)
            outputListeners.forEach { it.onFoldUpdate(foldStateChange) }
            if (DEBUG) {
                Log.d(TAG, "From $previous to $current in $time")
            }
        }

        actionStartMillis = null
        lastState = current
    }

    override fun addCallback(listener: FoldStateLoggingListener) {
        outputListeners.add(listener)
    }

    override fun removeCallback(listener: FoldStateLoggingListener) {
        outputListeners.remove(listener)
    }
}

private const val DEBUG = false
private const val TAG = "FoldStateLoggingProviderImpl"
+16 −9
Original line number Diff line number Diff line
@@ -17,10 +17,11 @@
package com.android.systemui.unfold

import com.android.keyguard.KeyguardUnfoldTransition
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.kotlin.getOrNull
import dagger.BindsInstance
import dagger.Module
import dagger.Provides
@@ -36,15 +37,17 @@ annotation class SysUIUnfoldScope

/**
 * Creates an injectable [SysUIUnfoldComponent] that provides objects that have been scoped with
 * [@SysUIUnfoldScope]. Since [SysUIUnfoldComponent] depends upon:
 * [@SysUIUnfoldScope].
 *
 * Since [SysUIUnfoldComponent] depends upon:
 * * [Optional<UnfoldTransitionProgressProvider>]
 * * [Optional<ScopedUnfoldTransitionProgressProvider>]
 * * [Optional<NaturalRotationProgressProvider>]
 *
 * no objects will get constructed if these parameters are empty.
 */
@Module(subcomponents = [SysUIUnfoldComponent::class])
class SysUIUnfoldModule {
    constructor() {}

    @Provides
    @SysUISingleton
@@ -53,10 +56,14 @@ class SysUIUnfoldModule {
        rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
        @Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
        factory: SysUIUnfoldComponent.Factory
    ) =
        provider.flatMap { p1 ->
            rotationProvider.flatMap { p2 ->
                scopedProvider.map { p3 -> factory.create(p1, p2, p3) }
    ): Optional<SysUIUnfoldComponent> {
        val p1 = provider.getOrNull()
        val p2 = rotationProvider.getOrNull()
        val p3 = scopedProvider.getOrNull()
        return if (p1 == null || p2 == null || p3 == null) {
            Optional.empty()
        } else {
            Optional.of(factory.create(p1, p2, p3))
        }
    }
}
+43 −15
Original line number Diff line number Diff line
@@ -24,8 +24,10 @@ 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.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.time.SystemClockImpl
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
import dagger.Lazy
import dagger.Module
@@ -48,7 +50,7 @@ class UnfoldTransitionModule {
        sensorManager: SensorManager,
        @Main executor: Executor,
        @Main handler: Handler
    ) =
    ): Optional<UnfoldTransitionProgressProvider> =
        if (config.isEnabled) {
            Optional.of(
                createUnfoldTransitionProgressProvider(
@@ -59,13 +61,45 @@ class UnfoldTransitionModule {
                    sensorManager,
                    handler,
                    executor,
                    tracingTagPrefix = "systemui"
                )
            )
                    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
    @Singleton
    fun providesFoldStateLoggingProvider(
        optionalFoldStateProvider: Optional<FoldStateProvider>
    ): Optional<FoldStateLoggingProvider> =
        optionalFoldStateProvider.map { foldStateProvider ->
            FoldStateLoggingProviderImpl(foldStateProvider, SystemClockImpl())
        }

    @Provides
    @Singleton
    fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
@@ -77,13 +111,9 @@ class UnfoldTransitionModule {
        context: Context,
        windowManager: IWindowManager,
        unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
    ) =
        unfoldTransitionProgressProvider.map {
            provider -> NaturalRotationUnfoldProgressProvider(
                context,
                windowManager,
                provider
            )
    ): Optional<NaturalRotationUnfoldProgressProvider> =
        unfoldTransitionProgressProvider.map { provider ->
            NaturalRotationUnfoldProgressProvider(context, windowManager, provider)
        }

    @Provides
@@ -91,10 +121,8 @@ class UnfoldTransitionModule {
    @Singleton
    fun provideStatusBarScopedTransitionProvider(
        source: Optional<NaturalRotationUnfoldProgressProvider>
    ) =
        source.map {
            provider -> ScopedUnfoldTransitionProgressProvider(provider)
        }
    ): Optional<ScopedUnfoldTransitionProgressProvider> =
        source.map { provider -> ScopedUnfoldTransitionProgressProvider(provider) }

    @Provides
    @Singleton
Loading