Trigger unfold animation without treshold while on keyguard

When folding, the fold animation triggers at different angles based on what's on the screen. Before this cl, it was triggering regardless of the angle only while on launcher, and after a certain threshold in all other cases.

While on keyguard, the angle depended by the app that was in foreground. (e.g. if calculator was open, and device locked, the threshold was 60 degrees).

After this change, while the keyguard is visible, there is no threshold.

It has been needed to create the intermediate UnfoldKeyguardVisibilityListener as DeviceFoldStateProvider is in the global dagger scope, while KeyguardStateController in the sysui subcomponent (so, injecting KeyguardStateController directly in DeviceFoldStateProvider is not possible).

From launcher, it is expected we don't have the information whether keyguard is visible (as we don't need it).

Bug: 261313897
Test: DeviceFoldStateProviderTest & manual
Change-Id: I07f133e96b16572448754ce2009f55e9e316b136
This commit is contained in:
Nicolo' Mazzucato
2022-12-28 08:24:50 +00:00
parent b4c0f42bd1
commit aa55dda145
6 changed files with 169 additions and 4 deletions

View File

@@ -20,6 +20,7 @@ import com.android.keyguard.KeyguardUnfoldTransition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.kotlin.getOrNull
@@ -95,4 +96,6 @@ interface SysUIUnfoldComponent {
fun getUnfoldHapticsPlayer(): UnfoldHapticsPlayer
fun getUnfoldLightRevealOverlayAnimation(): UnfoldLightRevealOverlayAnimation
fun getUnfoldKeyguardVisibilityManager(): UnfoldKeyguardVisibilityManager
}

View File

@@ -0,0 +1,39 @@
package com.android.systemui.unfold
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
import javax.inject.Inject
/**
* Used to set the keyguard visibility state to [UnfoldKeyguardVisibilityManager].
*
* It is not possible to directly inject a sysui class (e.g. [KeyguardStateController]) into
* [DeviceStateProvider], as it can't depend on google sysui directly. So,
* [UnfoldKeyguardVisibilityManager] is provided to clients, that can set the keyguard visibility
* accordingly.
*/
@SysUISingleton
class UnfoldKeyguardVisibilityListener
@Inject
constructor(
keyguardStateController: KeyguardStateController,
unfoldComponent: Optional<SysUIUnfoldComponent>,
) {
private val unfoldKeyguardVisibilityManager =
unfoldComponent.getOrNull()?.getUnfoldKeyguardVisibilityManager()
private val delegate = { keyguardStateController.isVisible }
fun init() {
unfoldKeyguardVisibilityManager?.setKeyguardVisibleDelegate(delegate).also {
Log.d(TAG, "setKeyguardVisibleDelegate set")
}
}
}
private const val TAG = "UnfoldKeyguardVisibilityListener"

View File

@@ -29,6 +29,7 @@ import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListen
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
@@ -56,6 +57,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
@Mock
private lateinit var rotationChangeProvider: RotationChangeProvider
@Mock
private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider
@Captor
private lateinit var rotationListener: ArgumentCaptor<RotationListener>
@@ -87,6 +91,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
screenOnStatusProvider,
foldProvider,
activityTypeProvider,
unfoldKeyguardVisibilityProvider,
rotationChangeProvider,
context.mainExecutor,
handler
@@ -379,6 +384,47 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
@Test
fun startClosingEvent_whileNotOnKeyguardAndNotOnLauncher_doesNotTriggerBeforeThreshold() {
setKeyguardVisibility(visible = false)
setupForegroundActivityType(isHomeActivity = false)
sendHingeAngleEvent(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
assertThat(foldUpdates).isEmpty()
}
@Test
fun startClosingEvent_whileKeyguardStateNotAvailable_triggerBeforeThreshold() {
setKeyguardVisibility(visible = null)
sendHingeAngleEvent(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
@Test
fun startClosingEvent_whileonKeyguard_doesTriggerBeforeThreshold() {
setKeyguardVisibility(visible = true)
sendHingeAngleEvent(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
@Test
fun startClosingEvent_whileNotOnKeyguard_triggersAfterThreshold() {
setKeyguardVisibility(visible = false)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1)
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
@Test
fun screenOff_whileFolded_hingeAngleProviderRemainsOff() {
setFoldState(folded = true)
@@ -445,6 +491,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
}
private fun setKeyguardVisibility(visible: Boolean?) {
whenever(unfoldKeyguardVisibilityProvider.isKeyguardVisible).thenReturn(visible)
}
private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) {
val runnableDelay = scheduledRunnableDelay ?: throw Exception("No runnable scheduled.")
if (waitTime >= runnableDelay) {

View File

@@ -28,6 +28,9 @@ 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.Module
import dagger.Provides
import java.util.Optional
@@ -57,7 +60,8 @@ class UnfoldSharedModule {
scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
// Always present callback that logs animation beginning and end.
addCallback(tracingListener)
})
}
)
}
@Provides
@@ -77,4 +81,16 @@ class UnfoldSharedModule {
} else {
EmptyHingeAngleProvider
}
@Provides
@Singleton
fun unfoldKeyguardVisibilityProvider(
impl: UnfoldKeyguardVisibilityManagerImpl
): UnfoldKeyguardVisibilityProvider = impl
@Provides
@Singleton
fun unfoldKeyguardVisibilityManager(
impl: UnfoldKeyguardVisibilityManagerImpl
): UnfoldKeyguardVisibilityManager = impl
}

View File

@@ -31,6 +31,7 @@ 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 com.android.systemui.unfold.util.CurrentActivityTypeProvider
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -42,6 +43,7 @@ constructor(
private val screenStatusProvider: ScreenStatusProvider,
private val foldProvider: FoldProvider,
private val activityTypeProvider: CurrentActivityTypeProvider,
private val unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider,
private val rotationChangeProvider: RotationChangeProvider,
@UnfoldMain private val mainExecutor: Executor,
@UnfoldMain private val handler: Handler
@@ -152,12 +154,13 @@ constructor(
*/
private fun getClosingThreshold(): Int? {
val isHomeActivity = activityTypeProvider.isHomeActivity ?: return null
val isKeyguardVisible = unfoldKeyguardVisibilityProvider.isKeyguardVisible == true
if (DEBUG) {
Log.d(TAG, "isHomeActivity=$isHomeActivity")
Log.d(TAG, "isHomeActivity=$isHomeActivity, isOnKeyguard=$isKeyguardVisible")
}
return if (isHomeActivity) {
return if (isHomeActivity || isKeyguardVisible) {
null
} else {
START_CLOSING_ON_APPS_THRESHOLD_DEGREES
@@ -257,7 +260,7 @@ fun @receiver:FoldUpdate Int.name() =
}
private const val TAG = "DeviceFoldProvider"
private const val DEBUG = false
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
/** Threshold after which we consider the device fully unfolded. */
@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f

View File

@@ -0,0 +1,54 @@
/*
* 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.util
import java.lang.ref.WeakReference
import javax.inject.Inject
import javax.inject.Singleton
interface UnfoldKeyguardVisibilityProvider {
/**
* True when the keyguard is visible.
*
* Might be [null] when it is not known.
*/
val isKeyguardVisible: Boolean?
}
/** Used to notify keyguard visibility. */
interface UnfoldKeyguardVisibilityManager {
/** Sets the delegate. [delegate] should return true when the keyguard is visible. */
fun setKeyguardVisibleDelegate(delegate: () -> Boolean)
}
/**
* Keeps a [WeakReference] for the keyguard visibility provider.
*
* It is a weak reference because this is in the global scope, while the delegate might be set from
* another subcomponent (that might have shorter lifespan).
*/
@Singleton
class UnfoldKeyguardVisibilityManagerImpl @Inject constructor() :
UnfoldKeyguardVisibilityProvider, UnfoldKeyguardVisibilityManager {
private var delegatedProvider: WeakReference<() -> Boolean?>? = null
override fun setKeyguardVisibleDelegate(delegate: () -> Boolean) {
delegatedProvider = WeakReference(delegate)
}
override val isKeyguardVisible: Boolean?
get() = delegatedProvider?.get()?.invoke()
}