From 733f7fb1bb057f3969d359820e43a245156ab944 Mon Sep 17 00:00:00 2001 From: Caitlin Cassidy Date: Mon, 6 Dec 2021 17:14:45 +0000 Subject: [PATCH] [Media TTT] Refactor the command line interface into its own class and use sealed classes for the chip states. The sealed class doesn't do anything in this CL, but it will become useful in future CLs. Sample command: adb shell cmd statusbar media-ttt-chip-add Tablet TransferInitiated Bug: 203800327 Test: MediaTttCommandLineHelperTest, MediaTttChipControllerTest Test: Re-tested adb commands and they still trigger the correct UIs Change-Id: Iffd53c5648dac0307d282158122365613fb7b653 --- .../systemui/dagger/SysUIComponent.java | 5 + .../systemui/media/dagger/MediaModule.java | 17 ++- .../taptotransfer/MediaTttChipController.kt | 66 +-------- .../media/taptotransfer/MediaTttChipState.kt | 56 ++++++++ .../MediaTttCommandLineHelper.kt | 89 ++++++++++++ .../MediaTttChipControllerTest.kt | 100 +++----------- .../MediaTttCommandLineHelperTest.kt | 128 ++++++++++++++++++ 7 files changed, 322 insertions(+), 139 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt create mode 100644 packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 38e4d78920bc5..2e0c0f45a960d 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -24,6 +24,7 @@ import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.media.taptotransfer.MediaTttChipController; +import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.people.PeopleProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.unfold.SysUIUnfoldComponent; @@ -133,6 +134,7 @@ public interface SysUIComponent { getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init()); // No init method needed, just needs to be gotten so that it's created. getMediaTttChipController(); + getMediaTttCommandLineHelper(); } /** @@ -182,6 +184,9 @@ public interface SysUIComponent { /** */ Optional getMediaTttChipController(); + /** */ + Optional getMediaTttCommandLineHelper(); + /** * Member injection into the supplied argument. */ 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 cf679f070c3a1..a1f8455f9ac8a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -25,6 +25,7 @@ import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.media.MediaHostStatesManager; import com.android.systemui.media.taptotransfer.MediaTttChipController; +import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.MediaTttFlags; import com.android.systemui.statusbar.commandline.CommandRegistry; @@ -78,11 +79,23 @@ public interface MediaModule { static Optional providesMediaTttChipController( MediaTttFlags mediaTttFlags, Context context, - CommandRegistry commandRegistry, WindowManager windowManager) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipController(commandRegistry, context, windowManager)); + return Optional.of(new MediaTttChipController(context, windowManager)); + } + + /** */ + @Provides + @SysUISingleton + static Optional providesMediaTttCommandLineHelper( + MediaTttFlags mediaTttFlags, + CommandRegistry commandRegistry, + MediaTttChipController mediaTttChipController) { + if (!mediaTttFlags.isMediaTttEnabled()) { + return Optional.empty(); + } + return Optional.of(new MediaTttCommandLineHelper(commandRegistry, mediaTttChipController)); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt index af8fc0e3c7f9a..1ec6a39347ca3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt @@ -24,13 +24,8 @@ import android.view.View import android.view.WindowManager import android.widget.LinearLayout import android.widget.TextView -import androidx.annotation.StringRes -import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.commandline.Command -import com.android.systemui.statusbar.commandline.CommandRegistry -import java.io.PrintWriter import javax.inject.Inject /** @@ -41,14 +36,9 @@ import javax.inject.Inject */ @SysUISingleton class MediaTttChipController @Inject constructor( - commandRegistry: CommandRegistry, private val context: Context, private val windowManager: WindowManager, ) { - init { - commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() } - commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() } - } private val windowLayoutParams = WindowManager.LayoutParams().apply { width = WindowManager.LayoutParams.WRAP_CONTENT @@ -65,7 +55,8 @@ class MediaTttChipController @Inject constructor( /** The chip view currently being displayed. Null if the chip is not being displayed. */ private var chipView: LinearLayout? = null - private fun displayChip(chipType: ChipType, otherDeviceName: String) { + /** Displays the chip view for the given state. */ + fun displayChip(chipState: MediaTttChipState) { val oldChipView = chipView if (chipView == null) { chipView = LayoutInflater @@ -76,16 +67,16 @@ class MediaTttChipController @Inject constructor( // Text currentChipView.requireViewById(R.id.text).apply { - text = context.getString(chipType.chipText, otherDeviceName) + text = context.getString(chipState.chipText, chipState.otherDeviceName) } // Loading - val showLoading = chipType == ChipType.TRANSFER_INITIATED + val showLoading = chipState is TransferInitiated currentChipView.requireViewById(R.id.loading).visibility = if (showLoading) { View.VISIBLE } else { View.GONE } // Undo - val showUndo = chipType == ChipType.TRANSFER_SUCCEEDED + val showUndo = chipState is TransferSucceeded currentChipView.requireViewById(R.id.undo).visibility = if (showUndo) { View.VISIBLE } else { View.GONE } @@ -94,53 +85,10 @@ class MediaTttChipController @Inject constructor( } } - private fun removeChip() { + /** Hides the chip. */ + fun removeChip() { if (chipView == null) { return } windowManager.removeView(chipView) chipView = null } - - @VisibleForTesting - enum class ChipType( - @StringRes internal val chipText: Int - ) { - MOVE_CLOSER_TO_TRANSFER(R.string.media_move_closer_to_transfer), - TRANSFER_INITIATED(R.string.media_transfer_playing), - TRANSFER_SUCCEEDED(R.string.media_transfer_playing), - } - - inner class AddChipCommand : Command { - override fun execute(pw: PrintWriter, args: List) { - val chipTypeArg = args[1] - ChipType.values().forEach { - if (it.name == chipTypeArg) { - displayChip(it, otherDeviceName = args[0]) - return - } - } - - pw.println("Chip type must be one of " + - ChipType.values().map { it.name }.reduce { acc, s -> "$acc, $s" }) - } - - override fun help(pw: PrintWriter) { - pw.println( - "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG " - ) - } - } - - inner class RemoveChipCommand : Command { - override fun execute(pw: PrintWriter, args: List) = removeChip() - override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG") - } - } - - companion object { - @VisibleForTesting - const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add" - @VisibleForTesting - const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove" - } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt new file mode 100644 index 0000000000000..05b9552a0a713 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 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.media.taptotransfer + +import androidx.annotation.StringRes +import com.android.systemui.R + +/** + * A class that stores all the information necessary to display the media tap-to-transfer chip in + * certain states. + * + * This is a sealed class where each subclass represents a specific chip state. Each subclass can + * contain additional information that is necessary for only that state. + */ +sealed class MediaTttChipState( + /** A string resource for the text that the chip should display. */ + @StringRes internal val chipText: Int, + /** The name of the other device involved in the transfer. */ + internal val otherDeviceName: String +) + +/** + * A state representing that the two devices are close but not close enough to initiate a transfer. + * The chip will instruct the user to move closer in order to initiate the transfer. + */ +class MoveCloserToTransfer( + otherDeviceName: String +) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName) + +/** + * A state representing that a transfer has been initiated (but not completed). + */ +class TransferInitiated( + otherDeviceName: String +) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName) + +/** + * A state representing that a transfer has been successfully completed. + */ +class TransferSucceeded( + otherDeviceName: String +) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt new file mode 100644 index 0000000000000..9f05c92ff4431 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 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.media.taptotransfer + +import androidx.annotation.VisibleForTesting +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.commandline.Command +import com.android.systemui.statusbar.commandline.CommandRegistry +import java.io.PrintWriter +import javax.inject.Inject + +/** + * A helper class to test the media tap-to-transfer chip via the command line. See inner classes for + * command usages. + */ +@SysUISingleton +class MediaTttCommandLineHelper @Inject constructor( + commandRegistry: CommandRegistry, + private val mediaTttChipController: MediaTttChipController +) { + init { + commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() } + commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() } + } + + inner class AddChipCommand : Command { + override fun execute(pw: PrintWriter, args: List) { + val otherDeviceName = args[0] + when (args[1]) { + MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> { + mediaTttChipController.displayChip(MoveCloserToTransfer(otherDeviceName)) + } + TRANSFER_INITIATED_COMMAND_NAME -> { + mediaTttChipController.displayChip(TransferInitiated(otherDeviceName)) + } + TRANSFER_SUCCEEDED_COMMAND_NAME -> { + mediaTttChipController.displayChip(TransferSucceeded(otherDeviceName)) + } + else -> { + pw.println("Chip type must be one of " + + "$MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME, " + + "$TRANSFER_INITIATED_COMMAND_NAME, " + + TRANSFER_SUCCEEDED_COMMAND_NAME + ) + } + } + } + + override fun help(pw: PrintWriter) { + pw.println( + "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG " + ) + } + } + + inner class RemoveChipCommand : Command { + override fun execute(pw: PrintWriter, args: List) { + mediaTttChipController.removeChip() + } + override fun help(pw: PrintWriter) { + pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG") + } + } +} + +@VisibleForTesting +const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add" +@VisibleForTesting +const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove" +@VisibleForTesting +val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleName!! +@VisibleForTesting +val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!! +@VisibleForTesting +val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!! diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt index 923f018f8bcdc..50d94dd6ca5f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt @@ -23,8 +23,6 @@ import android.widget.TextView import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.commandline.Command -import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -35,83 +33,58 @@ import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -import java.io.PrintWriter -import java.io.StringWriter -import java.util.concurrent.Executor @SmallTest class MediaTttChipControllerTest : SysuiTestCase() { private lateinit var mediaTttChipController: MediaTttChipController - private val inlineExecutor = Executor { command -> command.run() } - private val commandRegistry = CommandRegistry(context, inlineExecutor) - private val pw = PrintWriter(StringWriter()) - @Mock private lateinit var windowManager: WindowManager @Before fun setUp() { MockitoAnnotations.initMocks(this) - mediaTttChipController = MediaTttChipController(commandRegistry, context, windowManager) - } - - @Test(expected = IllegalStateException::class) - fun constructor_addCommmandAlreadyRegistered() { - // Since creating the chip controller should automatically register the add command, it - // should throw when registering it again. - commandRegistry.registerCommand( - MediaTttChipController.ADD_CHIP_COMMAND_TAG - ) { EmptyCommand() } - } - - @Test(expected = IllegalStateException::class) - fun constructor_removeCommmandAlreadyRegistered() { - // Since creating the chip controller should automatically register the remove command, it - // should throw when registering it again. - commandRegistry.registerCommand( - MediaTttChipController.REMOVE_CHIP_COMMAND_TAG - ) { EmptyCommand() } + mediaTttChipController = MediaTttChipController(context, windowManager) } @Test - fun addChipCommand_chipAdded() { - commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + fun displayChip_chipAdded() { + mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) verify(windowManager).addView(any(), any()) } @Test - fun addChipCommand_twice_chipNotAddedTwice() { - commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + fun displayChip_twice_chipNotAddedTwice() { + mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) reset(windowManager) - commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) verify(windowManager, never()).addView(any(), any()) } @Test - fun removeChipCommand_chipRemoved() { + fun removeChip_chipRemoved() { // First, add the chip - commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) // Then, remove it - commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG)) + mediaTttChipController.removeChip() verify(windowManager).removeView(any()) } @Test - fun removeChipCommand_noAdd_viewNotRemoved() { - commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG)) + fun removeChip_noAdd_viewNotRemoved() { + mediaTttChipController.removeChip() verify(windowManager, never()).removeView(any()) } @Test fun moveCloserToTransfer_chipTextContainsDeviceName_noLoadingIcon_noUndo() { - commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) val chipView = getChipView() assertThat(chipView.getChipText()).contains(DEVICE_NAME) @@ -121,7 +94,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferInitiated_chipTextContainsDeviceName_loadingIcon_noUndo() { - commandRegistry.onShellCommand(pw, getTransferInitiatedCommand()) + mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME)) val chipView = getChipView() assertThat(chipView.getChipText()).contains(DEVICE_NAME) @@ -131,7 +104,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferSucceeded_chipTextContainsDeviceName_noLoadingIcon_undo() { - commandRegistry.onShellCommand(pw, getTransferSucceededCommand()) + mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME)) val chipView = getChipView() assertThat(chipView.getChipText()).contains(DEVICE_NAME) @@ -141,57 +114,36 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() { - commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) - commandRegistry.onShellCommand(pw, getTransferInitiatedCommand()) + mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) + mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME)) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE) } @Test fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() { - commandRegistry.onShellCommand(pw, getTransferInitiatedCommand()) - commandRegistry.onShellCommand(pw, getTransferSucceededCommand()) + mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME)) + mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME)) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE) } @Test fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() { - commandRegistry.onShellCommand(pw, getTransferInitiatedCommand()) - commandRegistry.onShellCommand(pw, getTransferSucceededCommand()) + mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME)) + mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME)) assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.VISIBLE) } @Test fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() { - commandRegistry.onShellCommand(pw, getTransferSucceededCommand()) - commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME)) + mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.GONE) } - private fun getMoveCloserToTransferCommand(): Array = - arrayOf( - MediaTttChipController.ADD_CHIP_COMMAND_TAG, - DEVICE_NAME, - MediaTttChipController.ChipType.MOVE_CLOSER_TO_TRANSFER.name - ) - - private fun getTransferInitiatedCommand(): Array = - arrayOf( - MediaTttChipController.ADD_CHIP_COMMAND_TAG, - DEVICE_NAME, - MediaTttChipController.ChipType.TRANSFER_INITIATED.name - ) - - private fun getTransferSucceededCommand(): Array = - arrayOf( - MediaTttChipController.ADD_CHIP_COMMAND_TAG, - DEVICE_NAME, - MediaTttChipController.ChipType.TRANSFER_SUCCEEDED.name - ) - private fun LinearLayout.getChipText(): String = (this.requireViewById(R.id.text)).text as String @@ -206,14 +158,6 @@ class MediaTttChipControllerTest : SysuiTestCase() { verify(windowManager).addView(viewCaptor.capture(), any()) return viewCaptor.value as LinearLayout } - - class EmptyCommand : Command { - override fun execute(pw: PrintWriter, args: List) { - } - - override fun help(pw: PrintWriter) { - } - } } private const val DEVICE_NAME = "My Tablet" 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 new file mode 100644 index 0000000000000..4b85fa9fb75cf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 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.media.taptotransfer + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.commandline.Command +import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.util.mockito.any +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.io.PrintWriter +import java.io.StringWriter +import java.util.concurrent.Executor + +@SmallTest +class MediaTttCommandLineHelperTest : SysuiTestCase() { + + private val inlineExecutor = Executor { command -> command.run() } + private val commandRegistry = CommandRegistry(context, inlineExecutor) + private val pw = PrintWriter(StringWriter()) + + private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper + + @Mock + private lateinit var mediaTttChipController: MediaTttChipController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + mediaTttCommandLineHelper = + MediaTttCommandLineHelper(commandRegistry, mediaTttChipController) + } + + @Test(expected = IllegalStateException::class) + fun constructor_addCommandAlreadyRegistered() { + // Since creating the chip controller should automatically register the add command, it + // should throw when registering it again. + commandRegistry.registerCommand( + ADD_CHIP_COMMAND_TAG + ) { EmptyCommand() } + } + + @Test(expected = IllegalStateException::class) + fun constructor_removeCommandAlreadyRegistered() { + // Since creating the chip controller should automatically register the remove command, it + // should throw when registering it again. + commandRegistry.registerCommand( + REMOVE_CHIP_COMMAND_TAG + ) { EmptyCommand() } + } + + @Test + fun moveCloserToTransfer_chipDisplayWithCorrectState() { + commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + + verify(mediaTttChipController).displayChip(any(MoveCloserToTransfer::class.java)) + } + + @Test + fun transferInitiated_chipDisplayWithCorrectState() { + commandRegistry.onShellCommand(pw, getTransferInitiatedCommand()) + + verify(mediaTttChipController).displayChip(any(TransferInitiated::class.java)) + } + + @Test + fun transferSucceeded_chipDisplayWithCorrectState() { + commandRegistry.onShellCommand(pw, getTransferSucceededCommand()) + + verify(mediaTttChipController).displayChip(any(TransferSucceeded::class.java)) + } + + @Test + fun removeCommand_chipRemoved() { + commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_TAG)) + + verify(mediaTttChipController).removeChip() + } + + private fun getMoveCloserToTransferCommand(): Array = + arrayOf( + ADD_CHIP_COMMAND_TAG, + DEVICE_NAME, + MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME + ) + + private fun getTransferInitiatedCommand(): Array = + arrayOf( + ADD_CHIP_COMMAND_TAG, + DEVICE_NAME, + TRANSFER_INITIATED_COMMAND_NAME + ) + + private fun getTransferSucceededCommand(): Array = + arrayOf( + ADD_CHIP_COMMAND_TAG, + DEVICE_NAME, + TRANSFER_SUCCEEDED_COMMAND_NAME + ) + + class EmptyCommand : Command { + override fun execute(pw: PrintWriter, args: List) { + } + + override fun help(pw: PrintWriter) { + } + } +} + +private const val DEVICE_NAME = "My Tablet"