[Media TTT] Add NoLongerCloseToReceiver callback.

Bug: 203800643
Bug: 203800347
Test: verify `adb shell cmd statusbar media-ttt-chip-sender Device
NoLongerCloseToReceiver` hides the chip
Test: media.taptotransfer tests

Change-Id: Iceea7c10b23dc1b175e11335ca4fcf695ca98fb6
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
index 67259f4bb..eb1c9d0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
@@ -124,4 +124,10 @@
      * on the receiver and the transfer that should've *ended* the playing on the receiver.
      */
     oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+    /**
+     * Invoke to notify System UI that this device is no longer close to the receiver device.
+     */
+    oneway void noLongerCloseToReceiver(
+        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index dd60b30..4baef3a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -112,7 +112,6 @@
             MediaTttFlags mediaTttFlags,
             CommandRegistry commandRegistry,
             Context context,
-            MediaTttChipControllerSender mediaTttChipControllerSender,
             MediaTttChipControllerReceiver mediaTttChipControllerReceiver) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
@@ -121,7 +120,6 @@
                 new MediaTttCommandLineHelper(
                         commandRegistry,
                         context,
-                        mediaTttChipControllerSender,
                         mediaTttChipControllerReceiver));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index da93e92..3720851 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
 import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
 import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
 import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
@@ -55,7 +54,6 @@
 class MediaTttCommandLineHelper @Inject constructor(
     commandRegistry: CommandRegistry,
     private val context: Context,
-    private val mediaTttChipControllerSender: MediaTttChipControllerSender,
     private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
 ) {
     private var senderService: IDeviceSenderService? = null
@@ -67,17 +65,15 @@
         }
 
     init {
-        commandRegistry.registerCommand(
-            ADD_CHIP_COMMAND_SENDER_TAG) { AddChipCommandSender() }
-        commandRegistry.registerCommand(
-            REMOVE_CHIP_COMMAND_SENDER_TAG) { RemoveChipCommandSender() }
+        commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
         commandRegistry.registerCommand(
             ADD_CHIP_COMMAND_RECEIVER_TAG) { AddChipCommandReceiver() }
         commandRegistry.registerCommand(
             REMOVE_CHIP_COMMAND_RECEIVER_TAG) { RemoveChipCommandReceiver() }
     }
 
-    inner class AddChipCommandSender : Command {
+    /** All commands for the sender device. */
+    inner class SenderCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
             val otherDeviceName = args[0]
             val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
@@ -145,24 +141,29 @@
                         senderService.transferFailed(mediaInfo, otherDeviceInfo)
                     }
                 }
+                NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME -> {
+                    runOnService { senderService ->
+                        senderService.noLongerCloseToReceiver(mediaInfo, otherDeviceInfo)
+                        context.unbindService(senderServiceConnection)
+                    }
+                }
                 else -> {
-                    pw.println("Chip type must be one of " +
+                    pw.println("Sender command must be one of " +
                             "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
                             "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
                             "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " +
                             "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " +
                             "$TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME, " +
                             "$TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME, " +
-                            TRANSFER_FAILED_COMMAND_NAME
+                            "$TRANSFER_FAILED_COMMAND_NAME, " +
+                            NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
                     )
                 }
             }
         }
 
         override fun help(pw: PrintWriter) {
-            pw.println("Usage: adb shell cmd statusbar " +
-                    "$ADD_CHIP_COMMAND_SENDER_TAG <deviceName> <chipStatus>"
-            )
+            pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>")
         }
 
         private fun runOnService(command: SenderServiceCommand) {
@@ -185,19 +186,6 @@
         }
     }
 
-    /** A command to REMOVE the media ttt chip on the SENDER device. */
-    inner class RemoveChipCommandSender : Command {
-        override fun execute(pw: PrintWriter, args: List<String>) {
-            mediaTttChipControllerSender.removeChip()
-            if (senderService != null) {
-                context.unbindService(senderServiceConnection)
-            }
-        }
-        override fun help(pw: PrintWriter) {
-            pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_SENDER_TAG")
-        }
-    }
-
     /** A command to DISPLAY the media ttt chip on the RECEIVER device. */
     inner class AddChipCommandReceiver : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
@@ -245,7 +233,7 @@
 }
 
 @VisibleForTesting
-const val ADD_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-add-sender"
+const val SENDER_COMMAND = "media-ttt-chip-sender"
 @VisibleForTesting
 const val REMOVE_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-remove-sender"
 @VisibleForTesting
@@ -268,6 +256,8 @@
     TransferToThisDeviceSucceeded::class.simpleName!!
 @VisibleForTesting
 val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!
+@VisibleForTesting
+val NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME = "NoLongerCloseToReceiver"
 
 private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
 private const val TAG = "MediaTapToTransferCli"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 67721a5..adae07b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -81,6 +81,9 @@
 
     /** Hides the chip. */
     fun removeChip() {
+        // TODO(b/203800347): We may not want to hide the chip if we're currently in a
+        //  TransferTriggered state: Once the user has initiated the transfer, they should be able
+        //  to move away from the receiver device but still see the status of the transfer.
         if (chipView == null) { return }
         windowManager.removeView(chipView)
         chipView = null
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
index 8d9d7a9..717752e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
@@ -88,6 +88,13 @@
                 mediaInfo, otherDeviceInfo, undoCallback
             )
         }
+
+        override fun noLongerCloseToReceiver(
+            mediaInfo: MediaRoute2Info,
+            otherDeviceInfo: DeviceInfo
+        ) {
+            this@MediaTttSenderService.noLongerCloseToReceiver()
+        }
     }
 
     // TODO(b/203800643): Use the app icon from the media info instead of a fake one.
@@ -168,4 +175,8 @@
         )
         controller.displayChip(chipState)
     }
+
+    private fun noLongerCloseToReceiver() {
+        controller.removeChip()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 9b16305..a1ec38f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
 import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
 import com.android.systemui.shared.mediattt.DeviceInfo
 import com.android.systemui.shared.mediattt.IDeviceSenderService
@@ -52,8 +51,6 @@
     private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper
 
     @Mock
-    private lateinit var mediaTttChipControllerSender: MediaTttChipControllerSender
-    @Mock
     private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
     @Mock
     private lateinit var mediaSenderService: IDeviceSenderService.Stub
@@ -72,27 +69,15 @@
             MediaTttCommandLineHelper(
                 commandRegistry,
                 context,
-                mediaTttChipControllerSender,
                 mediaTttChipControllerReceiver,
             )
     }
 
     @Test(expected = IllegalStateException::class)
-    fun constructor_addSenderCommandAlreadyRegistered() {
-        // Since creating the chip controller should automatically register the add command, it
+    fun constructor_senderCommandAlreadyRegistered() {
+        // Since creating the chip controller should automatically register the sender command, it
         // should throw when registering it again.
-        commandRegistry.registerCommand(
-            ADD_CHIP_COMMAND_SENDER_TAG
-        ) { EmptyCommand() }
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun constructor_removeSenderCommandAlreadyRegistered() {
-        // Since creating the chip controller should automatically register the remove command, it
-        // should throw when registering it again.
-        commandRegistry.registerCommand(
-            REMOVE_CHIP_COMMAND_SENDER_TAG
-        ) { EmptyCommand() }
+        commandRegistry.registerCommand(SENDER_COMMAND) { EmptyCommand() }
     }
 
     @Test(expected = IllegalStateException::class)
@@ -187,10 +172,12 @@
     }
 
     @Test
-    fun sender_removeCommand_chipRemoved() {
-        commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_SENDER_TAG))
+    fun sender_noLongerCloseToReceiver_serviceCallbackCalledAndServiceUnbound() {
+        commandRegistry.onShellCommand(pw, getNoLongerCloseToReceiverCommand())
 
-        verify(mediaTttChipControllerSender).removeChip()
+        // Once we're no longer close to the receiver, we should unbind the service.
+        assertThat(context.isBound(mediaSenderServiceComponentName)).isFalse()
+        verify(mediaSenderService).noLongerCloseToReceiver(any(), any())
     }
 
     @Test
@@ -209,53 +196,60 @@
 
     private fun getMoveCloserToStartCastCommand(): Array<String> =
         arrayOf(
-            ADD_CHIP_COMMAND_SENDER_TAG,
+            SENDER_COMMAND,
             DEVICE_NAME,
             MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
         )
 
     private fun getMoveCloserToEndCastCommand(): Array<String> =
         arrayOf(
-            ADD_CHIP_COMMAND_SENDER_TAG,
+            SENDER_COMMAND,
             DEVICE_NAME,
             MOVE_CLOSER_TO_END_CAST_COMMAND_NAME
         )
 
     private fun getTransferToReceiverTriggeredCommand(): Array<String> =
         arrayOf(
-            ADD_CHIP_COMMAND_SENDER_TAG,
+            SENDER_COMMAND,
             DEVICE_NAME,
             TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME
         )
 
     private fun getTransferToThisDeviceTriggeredCommand(): Array<String> =
         arrayOf(
-            ADD_CHIP_COMMAND_SENDER_TAG,
+            SENDER_COMMAND,
             DEVICE_NAME,
             TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME
         )
 
     private fun getTransferToReceiverSucceededCommand(): Array<String> =
         arrayOf(
-            ADD_CHIP_COMMAND_SENDER_TAG,
+            SENDER_COMMAND,
             DEVICE_NAME,
             TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME
         )
 
     private fun getTransferToThisDeviceSucceededCommand(): Array<String> =
         arrayOf(
-            ADD_CHIP_COMMAND_SENDER_TAG,
+            SENDER_COMMAND,
             DEVICE_NAME,
             TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME
         )
 
     private fun getTransferFailedCommand(): Array<String> =
         arrayOf(
-            ADD_CHIP_COMMAND_SENDER_TAG,
+            SENDER_COMMAND,
             DEVICE_NAME,
             TRANSFER_FAILED_COMMAND_NAME
         )
 
+    private fun getNoLongerCloseToReceiverCommand(): Array<String> =
+        arrayOf(
+            SENDER_COMMAND,
+            DEVICE_NAME,
+            NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
+        )
+
     class EmptyCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
index e7304d4..11b727e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
@@ -114,4 +114,11 @@
 
         verify(controller).displayChip(any<TransferFailed>())
     }
+
+    @Test
+    fun noLongerCloseToReceiver_controllerRemoveChipTriggered() {
+        service.noLongerCloseToReceiver(mediaInfo, DeviceInfo("Fake name"))
+
+        verify(controller).removeChip()
+    }
 }