Merge "[Device Controls] Handle taps correctly when the tile is in an inactive state." into sc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 2ab5a3a..6d3190f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -96,21 +96,32 @@
     }
 
     override fun handleClick(view: View?) {
-        if (state.state == Tile.STATE_ACTIVE) {
-            mUiHandler.post {
-                val i = Intent().apply {
-                    component = ComponentName(mContext, ControlsActivity::class.java)
-                    addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-                    putExtra(ControlsUiController.EXTRA_ANIMATE, true)
-                }
-                if (keyguardStateController.isUnlocked()) {
-                    val animationController = view?.let {
-                        ActivityLaunchAnimator.Controller.fromView(it)
-                    }
-                    mActivityStarter.startActivity(i, true /* dismissShade */, animationController)
-                } else {
+        if (state.state == Tile.STATE_UNAVAILABLE) {
+            return
+        }
+
+        val intent = Intent().apply {
+            component = ComponentName(mContext, ControlsActivity::class.java)
+            addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+            putExtra(ControlsUiController.EXTRA_ANIMATE, true)
+        }
+        val animationController = view?.let {
+            ActivityLaunchAnimator.Controller.fromView(it)
+        }
+
+        mUiHandler.post {
+            if (keyguardStateController.isUnlocked) {
+                mActivityStarter.startActivity(
+                        intent, true /* dismissShade */, animationController)
+            } else {
+                if (state.state == Tile.STATE_ACTIVE) {
                     mHost.collapsePanels()
-                    mContext.startActivity(i)
+                    // With an active tile, don't use ActivityStarter so that the activity is
+                    // started without prompting keyguard unlock.
+                    mContext.startActivity(intent)
+                } else {
+                    mActivityStarter.postStartActivityDismissingKeyguard(
+                            intent, 0 /* delay */, animationController)
                 }
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 580cd35..6d1bbd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.qs.tiles
 
 import android.content.ComponentName
-import android.os.Handler
 import android.content.Context
 import android.content.Intent
+import android.os.Handler
 import android.provider.Settings
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
@@ -52,14 +52,17 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.never
+import org.mockito.Mockito.nullable
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
 import java.util.Optional
 
@@ -95,6 +98,8 @@
     @Captor
     private lateinit var listingCallbackCaptor:
             ArgumentCaptor<ControlsListingController.ControlsListingCallback>
+    @Captor
+    private lateinit var intentCaptor: ArgumentCaptor<Intent>
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: DeviceControlsTile
@@ -259,21 +264,21 @@
     }
 
     @Test
-    fun testNoDialogWhenUnavailable() {
+    fun handleClick_unavailable_noActivityStarted() {
         tile.click(null /* view */)
         testableLooper.processAllMessages()
 
-        verify(activityStarter, never()).startActivity(any(), anyBoolean(),
-                any<ActivityLaunchAnimator.Controller>())
+        verifyZeroInteractions(activityStarter)
     }
 
     @Test
-    fun testDialogShowWhenAvailable() {
+    fun handleClick_availableAndLocked_activityStarted() {
         verify(controlsListingController).observe(
                 any(LifecycleOwner::class.java),
                 capture(listingCallbackCaptor)
         )
         `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
+        `when`(keyguardStateController.isUnlocked).thenReturn(false)
 
         listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
         testableLooper.processAllMessages()
@@ -281,18 +286,44 @@
         tile.click(null /* view */)
         testableLooper.processAllMessages()
 
-        verify(activityStarter).startActivity(any(), eq(true) /* dismissShade */,
-                eq(null) as ActivityLaunchAnimator.Controller?)
+        // The activity should be started right away and not require a keyguard dismiss.
+        verifyZeroInteractions(activityStarter)
+        verify(spiedContext).startActivity(intentCaptor.capture())
+        assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
     }
 
     @Test
-    fun testNoDialogWhenInactive() {
+    fun handleClick_availableAndUnlocked_activityStarted() {
+        verify(controlsListingController).observe(
+                any(LifecycleOwner::class.java),
+                capture(listingCallbackCaptor)
+        )
+        `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
+        `when`(keyguardStateController.isUnlocked).thenReturn(true)
+
+        listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+        testableLooper.processAllMessages()
+
+        tile.click(null /* view */)
+        testableLooper.processAllMessages()
+
+        verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
+        verify(activityStarter).startActivity(
+                intentCaptor.capture(),
+                eq(true) /* dismissShade */,
+                nullable(ActivityLaunchAnimator.Controller::class.java))
+        assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
+    }
+
+    @Test
+    fun handleClick_availableAfterUnlockAndIsLocked_keyguardDismissRequired() {
         verify(controlsListingController).observe(
             any(LifecycleOwner::class.java),
             capture(listingCallbackCaptor)
         )
         `when`(controlsComponent.getVisibility())
             .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK)
+        `when`(keyguardStateController.isUnlocked).thenReturn(false)
 
         listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
         testableLooper.processAllMessages()
@@ -300,8 +331,39 @@
         tile.click(null /* view */)
         testableLooper.processAllMessages()
 
-        verify(activityStarter, never()).startActivity(any(), anyBoolean(),
-                any<ActivityLaunchAnimator.Controller>())
+        verify(activityStarter, never()).startActivity(
+                any(),
+                anyBoolean() /* dismissShade */,
+                nullable(ActivityLaunchAnimator.Controller::class.java))
+        verify(activityStarter).postStartActivityDismissingKeyguard(
+                intentCaptor.capture(),
+                anyInt(),
+                nullable(ActivityLaunchAnimator.Controller::class.java))
+        assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
+    }
+
+    @Test
+    fun handleClick_availableAfterUnlockAndIsUnlocked_activityStarted() {
+        verify(controlsListingController).observe(
+                any(LifecycleOwner::class.java),
+                capture(listingCallbackCaptor)
+        )
+        `when`(controlsComponent.getVisibility())
+                .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK)
+        `when`(keyguardStateController.isUnlocked).thenReturn(true)
+
+        listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+        testableLooper.processAllMessages()
+
+        tile.click(null /* view */)
+        testableLooper.processAllMessages()
+
+        verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
+        verify(activityStarter).startActivity(
+                intentCaptor.capture(),
+                eq(true) /* dismissShade */,
+                nullable(ActivityLaunchAnimator.Controller::class.java))
+        assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
     }
 
     private fun createTile(): DeviceControlsTile {
@@ -319,3 +381,5 @@
         )
     }
 }
+
+private const val CONTROLS_ACTIVITY_CLASS_NAME = "com.android.systemui.controls.ui.ControlsActivity"