Merge "Trigger unfold animation without treshold while on keyguard" into tm-qpr-dev
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 209d93f..1482cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -20,6 +20,7 @@
 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 @@
     fun getUnfoldHapticsPlayer(): UnfoldHapticsPlayer
 
     fun getUnfoldLightRevealOverlayAnimation(): UnfoldLightRevealOverlayAnimation
+
+    fun getUnfoldKeyguardVisibilityManager(): UnfoldKeyguardVisibilityManager
 }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldKeyguardVisibilityListener.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldKeyguardVisibilityListener.kt
new file mode 100644
index 0000000..59558ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldKeyguardVisibilityListener.kt
@@ -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"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 7d5f06c..6086e16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -29,6 +29,7 @@
 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 @@
     @Mock
     private lateinit var rotationChangeProvider: RotationChangeProvider
 
+    @Mock
+    private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider
+
     @Captor
     private lateinit var rotationListener: ArgumentCaptor<RotationListener>
 
@@ -87,6 +91,7 @@
                 screenOnStatusProvider,
                 foldProvider,
                 activityTypeProvider,
+                unfoldKeyguardVisibilityProvider,
                 rotationChangeProvider,
                 context.mainExecutor,
                 handler
@@ -380,6 +385,47 @@
     }
 
     @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)
         assertThat(testHingeAngleProvider.isStarted).isFalse()
@@ -445,6 +491,10 @@
         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) {
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 8f4ee4d..3fa5469 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -28,6 +28,9 @@
 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 @@
                 scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
                     // Always present callback that logs animation beginning and end.
                     addCallback(tracingListener)
-                })
+                }
+            )
         }
 
     @Provides
@@ -77,4 +81,16 @@
         } else {
             EmptyHingeAngleProvider
         }
+
+    @Provides
+    @Singleton
+    fun unfoldKeyguardVisibilityProvider(
+        impl: UnfoldKeyguardVisibilityManagerImpl
+    ): UnfoldKeyguardVisibilityProvider = impl
+
+    @Provides
+    @Singleton
+    fun unfoldKeyguardVisibilityManager(
+        impl: UnfoldKeyguardVisibilityManagerImpl
+    ): UnfoldKeyguardVisibilityManager = impl
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 808128d..5b45897 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -31,6 +31,7 @@
 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 @@
     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 @@
      */
     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 @@
     }
 
 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
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/UnfoldKeyguardVisibilityProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/UnfoldKeyguardVisibilityProvider.kt
new file mode 100644
index 0000000..9f0efa0
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/UnfoldKeyguardVisibilityProvider.kt
@@ -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()
+}