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

Commit d4768d6e authored by Chandru S's avatar Chandru S Committed by Android (Google) Code Review
Browse files

Merge "Provide API for accessing the current state of secure camera." into tm-qpr-dev

parents a3f6566a 1487f631
Loading
Loading
Loading
Loading
+175 −155
Original line number Diff line number Diff line
@@ -24,8 +24,8 @@ import android.content.res.Resources
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
import android.widget.FrameLayout
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -40,8 +40,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.log.dagger.KeyguardLargeClockLog
import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockTickRate
@@ -53,22 +53,24 @@ import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.Executor
import javax.inject.Inject

/**
 * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
 * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
 */
open class ClockEventController @Inject constructor(
open class ClockEventController
@Inject
constructor(
    private val keyguardInteractor: KeyguardInteractor,
    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
    private val broadcastDispatcher: BroadcastDispatcher,
@@ -115,7 +117,8 @@ open class ClockEventController @Inject constructor(
    private var disposableHandle: DisposableHandle? = null
    private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)

    private val mLayoutChangedListener = object : View.OnLayoutChangeListener {
    private val mLayoutChangedListener =
        object : View.OnLayoutChangeListener {
            private var currentSmallClockView: View? = null
            private var currentLargeClockView: View? = null
            private var currentSmallClockLocation = IntArray(2)
@@ -141,10 +144,14 @@ open class ClockEventController @Inject constructor(

                // SMALL CLOCK
                if (parent.id == R.id.lockscreen_clock_view) {
            // view bounds have changed due to clock size changing (i.e. different character widths)
            // AND/OR the view has been translated when transitioning between small and large clock
            if (view != currentSmallClockView ||
                !view.locationOnScreen.contentEquals(currentSmallClockLocation)) {
                    // view bounds have changed due to clock size changing (i.e. different character
                    // widths)
                    // AND/OR the view has been translated when transitioning between small and
                    // large clock
                    if (
                        view != currentSmallClockView ||
                            !view.locationOnScreen.contentEquals(currentSmallClockLocation)
                    ) {
                        currentSmallClockView = view
                        currentSmallClockLocation = view.locationOnScreen
                        updateRegionSampler(view)
@@ -152,8 +159,10 @@ open class ClockEventController @Inject constructor(
                }
                // LARGE CLOCK
                else if (parent.id == R.id.lockscreen_clock_view_large) {
            if (view != currentLargeClockView ||
                !view.locationOnScreen.contentEquals(currentLargeClockLocation)) {
                    if (
                        view != currentLargeClockView ||
                            !view.locationOnScreen.contentEquals(currentLargeClockLocation)
                    ) {
                        currentLargeClockView = view
                        currentLargeClockLocation = view.locationOnScreen
                        updateRegionSampler(view)
@@ -189,13 +198,15 @@ open class ClockEventController @Inject constructor(

    private fun updateRegionSampler(sampledRegion: View) {
        regionSampler?.stopRegionSampler()
        regionSampler = createRegionSampler(
        regionSampler =
            createRegionSampler(
                    sampledRegion,
                    mainExecutor,
                    bgExecutor,
                    regionSamplingEnabled,
                    ::updateColors
        )?.apply { startRegionSampler() }
                )
                ?.apply { startRegionSampler() }

        updateColors()
    }
@@ -212,7 +223,8 @@ open class ClockEventController @Inject constructor(
            mainExecutor,
            bgExecutor,
            regionSamplingEnabled,
            updateColors)
            updateColors
        )
    }

    var regionSampler: RegionSampler? = null
@@ -224,7 +236,8 @@ open class ClockEventController @Inject constructor(
    private var smallClockIsDark = true
    private var largeClockIsDark = true

    private val configListener = object : ConfigurationController.ConfigurationListener {
    private val configListener =
        object : ConfigurationController.ConfigurationListener {
            override fun onThemeChanged() {
                clock?.events?.onColorPaletteChanged(resources)
                updateColors()
@@ -235,7 +248,8 @@ open class ClockEventController @Inject constructor(
            }
        }

    private val batteryCallback = object : BatteryStateChangeCallback {
    private val batteryCallback =
        object : BatteryStateChangeCallback {
            override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
                if (isKeyguardVisible && !isCharging && charging) {
                    clock?.animations?.charge()
@@ -244,13 +258,15 @@ open class ClockEventController @Inject constructor(
            }
        }

    private val localeBroadcastReceiver = object : BroadcastReceiver() {
    private val localeBroadcastReceiver =
        object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                clock?.events?.onLocaleChanged(Locale.getDefault())
            }
        }

    private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
    private val keyguardUpdateMonitorCallback =
        object : KeyguardUpdateMonitorCallback() {
            override fun onKeyguardVisibilityChanged(visible: Boolean) {
                isKeyguardVisible = visible
                if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -295,7 +311,8 @@ open class ClockEventController @Inject constructor(
        configurationController.addCallback(configListener)
        batteryController.addCallback(batteryCallback)
        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
        disposableHandle = parent.repeatWhenAttached {
        disposableHandle =
            parent.repeatWhenAttached {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    listenForDozing(this)
                    if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -344,10 +361,18 @@ open class ClockEventController @Inject constructor(
    }

    private fun updateFontSizes() {
        clock?.smallClock?.events?.onFontSettingChanged(
            resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat())
        clock?.largeClock?.events?.onFontSettingChanged(
            resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat())
        clock
            ?.smallClock
            ?.events
            ?.onFontSettingChanged(
                resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
            )
        clock
            ?.largeClock
            ?.events
            ?.onFontSettingChanged(
                resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
            )
    }

    private fun handleDoze(doze: Float) {
@@ -359,34 +384,25 @@ open class ClockEventController @Inject constructor(

    @VisibleForTesting
    internal fun listenForDozeAmount(scope: CoroutineScope): Job {
        return scope.launch {
            keyguardInteractor.dozeAmount.collect {
                handleDoze(it)
            }
        }
        return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
    }

    @VisibleForTesting
    internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
        return scope.launch {
            keyguardTransitionInteractor.dozeAmountTransition.collect {
                handleDoze(it.value)
            }
            keyguardTransitionInteractor.dozeAmountTransition.collect { handleDoze(it.value) }
        }
    }

    /**
     * When keyguard is displayed again after being gone, the clock must be reset to full
     * dozing.
     * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
     */
    @VisibleForTesting
    internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
        return scope.launch {
            keyguardTransitionInteractor.anyStateToAodTransition.filter {
                it.transitionState == TransitionState.FINISHED
            }.collect {
                handleDoze(1f)
            }
            keyguardTransitionInteractor.anyStateToAodTransition
                .filter { it.transitionState == TransitionState.FINISHED }
                .collect { handleDoze(1f) }
        }
    }

@@ -399,19 +415,19 @@ open class ClockEventController @Inject constructor(
                ) { localDozeAmount, localIsDozing ->
                    localDozeAmount > dozeAmount || localIsDozing
                }
            .collect { localIsDozing ->
                isDozing = localIsDozing
            }
                .collect { localIsDozing -> isDozing = localIsDozing }
        }
    }

    class TimeListener(val clockFace: ClockFaceController, val executor: DelayableExecutor) {
        val predrawListener = ViewTreeObserver.OnPreDrawListener {
        val predrawListener =
            ViewTreeObserver.OnPreDrawListener {
                clockFace.events.onTimeTick()
                true
            }

        val secondsRunnable = object : Runnable {
        val secondsRunnable =
            object : Runnable {
                override fun run() {
                    if (!isRunning) {
                        return
@@ -432,7 +448,9 @@ open class ClockEventController @Inject constructor(

            isRunning = true
            when (clockFace.events.tickRate) {
                ClockTickRate.PER_MINUTE -> {/* Handled by KeyguardClockSwitchController */}
                ClockTickRate.PER_MINUTE -> {
                    /* Handled by KeyguardClockSwitchController */
                }
                ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
                ClockTickRate.PER_FRAME -> {
                    clockFace.view.viewTreeObserver.addOnPreDrawListener(predrawListener)
@@ -442,7 +460,9 @@ open class ClockEventController @Inject constructor(
        }

        fun stop() {
            if (!isRunning) { return }
            if (!isRunning) {
                return
            }

            isRunning = false
            clockFace.view.viewTreeObserver.removeOnPreDrawListener(predrawListener)
+28 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.graphics.Point
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -31,7 +33,6 @@ import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.CommandQueue.Callbacks
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -41,7 +42,9 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart

/**
 * Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -52,6 +55,7 @@ class KeyguardInteractor
constructor(
    private val repository: KeyguardRepository,
    private val commandQueue: CommandQueue,
    featureFlags: FeatureFlags,
) {
    /**
     * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -129,6 +133,29 @@ constructor(
     */
    val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState

    /** Keyguard is present and is not occluded. */
    val isKeyguardVisible: Flow<Boolean> =
        combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded }

    /** Whether camera is launched over keyguard. */
    var isSecureCameraActive =
        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
            combine(
                    isKeyguardVisible,
                    repository.isBouncerShowing,
                    onCameraLaunchDetected,
                ) { isKeyguardVisible, isBouncerShowing, cameraLaunchEvent ->
                    when {
                        isKeyguardVisible -> false
                        isBouncerShowing -> false
                        else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
                    }
                }
                .onStart { emit(false) }
        } else {
            flowOf(false)
        }

    /** The approximate location on the screen of the fingerprint sensor, if one is available. */
    val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation

+4 −2
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import java.util.*
import java.util.TimeZone
import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever

@@ -105,7 +105,9 @@ class ClockEventControllerTest : SysuiTestCase() {
        repository = FakeKeyguardRepository()

        underTest = ClockEventController(
            KeyguardInteractor(repository = repository, commandQueue = commandQueue),
            KeyguardInteractor(repository = repository,
                    commandQueue = commandQueue,
                    featureFlags = featureFlags),
            KeyguardTransitionInteractor(repository = transitionRepository),
            broadcastDispatcher,
            batteryController,
+3 −1
Original line number Diff line number Diff line
@@ -159,7 +159,9 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
                mAuthRippleController,
                mResources,
                new KeyguardTransitionInteractor(mTransitionRepository),
                new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue),
                new KeyguardInteractor(new FakeKeyguardRepository(),
                        mCommandQueue,
                        mFeatureFlags),
                mFeatureFlags
        );
    }
+10 −7
Original line number Diff line number Diff line
@@ -157,25 +157,28 @@ class CustomizationProviderTest : SysuiTestCase() {
                dumpManager = mock(),
                userHandle = UserHandle.SYSTEM,
            )
        val featureFlags =
            FakeFeatureFlags().apply {
                set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
                set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
                set(Flags.REVAMPED_WALLPAPER_UI, true)
                set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
                set(Flags.FACE_AUTH_REFACTOR, true)
            }
        underTest.interactor =
            KeyguardQuickAffordanceInteractor(
                keyguardInteractor =
                    KeyguardInteractor(
                        repository = FakeKeyguardRepository(),
                        commandQueue = commandQueue,
                        featureFlags = featureFlags,
                    ),
                registry = mock(),
                lockPatternUtils = lockPatternUtils,
                keyguardStateController = keyguardStateController,
                userTracker = userTracker,
                activityStarter = activityStarter,
                featureFlags =
                    FakeFeatureFlags().apply {
                        set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
                        set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
                        set(Flags.REVAMPED_WALLPAPER_UI, true)
                        set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
                    },
                featureFlags = featureFlags,
                repository = { quickAffordanceRepository },
                launchAnimator = launchAnimator,
            )
Loading