diff options
| author | 2021-12-21 20:54:35 +0000 | |
|---|---|---|
| committer | 2021-12-22 16:45:35 +0000 | |
| commit | 5fae789837f7dc4335a355074ea237cd3e2c8f67 (patch) | |
| tree | 31f01c3780edbee68a39d40264b7111a0cdfa18b | |
| parent | e087e12997ae4b4d0d22c4aad0824b56d66d8d37 (diff) | |
[Media TTT] Define a common controller that will be used for both the
sender device chip and the receiver device chip.
Code for the receiver device chip will be added in future CLs.
Bug: 203800646
Test: manual (verify sender chip still works)
Test: MediaTttChipControllerSenderTest, MediaTttCommandLineHelperTest
Change-Id: I72c7e089c5b587fcdf5827f4e95f9032d96bbfd7
11 files changed, 322 insertions, 152 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index af0697996f43..1e96809b413b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -23,8 +23,8 @@ import com.android.systemui.InitController; 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.media.taptotransfer.sender.MediaTttChipControllerSender; import com.android.systemui.people.PeopleProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.unfold.SysUIUnfoldComponent; @@ -134,7 +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(); + getMediaTttChipControllerSender(); getMediaTttCommandLineHelper(); getUnfoldLatencyTracker().init(); } @@ -190,7 +190,7 @@ public interface SysUIComponent { Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider(); /** */ - Optional<MediaTttChipController> getMediaTttChipController(); + Optional<MediaTttChipControllerSender> getMediaTttChipControllerSender(); /** */ Optional<MediaTttCommandLineHelper> getMediaTttCommandLineHelper(); 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 a398a7fa4575..d76d17910dc2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -26,9 +26,9 @@ import com.android.systemui.media.MediaDataManager; 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.media.taptotransfer.sender.MediaTttChipControllerSender; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -80,7 +80,7 @@ public interface MediaModule { /** */ @Provides @SysUISingleton - static Optional<MediaTttChipController> providesMediaTttChipController( + static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender( MediaTttFlags mediaTttFlags, Context context, WindowManager windowManager, @@ -89,7 +89,7 @@ public interface MediaModule { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipController( + return Optional.of(new MediaTttChipControllerSender( context, windowManager, mainExecutor, backgroundExecutor)); } @@ -100,12 +100,12 @@ public interface MediaModule { MediaTttFlags mediaTttFlags, CommandRegistry commandRegistry, Context context, - MediaTttChipController mediaTttChipController, + MediaTttChipControllerSender mediaTttChipControllerSender, @Main DelayableExecutor mainExecutor) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of(new MediaTttCommandLineHelper( - commandRegistry, context, mediaTttChipController, mainExecutor)); + commandRegistry, context, mediaTttChipControllerSender, mainExecutor)); } } 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 74983e5bfe03..93f7b7dff96c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -23,6 +23,10 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender +import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer +import com.android.systemui.media.taptotransfer.sender.TransferInitiated +import com.android.systemui.media.taptotransfer.sender.TransferSucceeded import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.concurrency.DelayableExecutor @@ -38,7 +42,7 @@ import javax.inject.Inject class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, context: Context, - private val mediaTttChipController: MediaTttChipController, + private val mediaTttChipControllerSender: MediaTttChipControllerSender, @Main private val mainExecutor: DelayableExecutor, ) { private val appIconDrawable = @@ -54,21 +58,21 @@ class MediaTttCommandLineHelper @Inject constructor( val otherDeviceName = args[0] when (args[1]) { MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> { - mediaTttChipController.displayChip( - MoveCloserToTransfer(otherDeviceName, appIconDrawable) + mediaTttChipControllerSender.displayChip( + MoveCloserToTransfer(appIconDrawable, otherDeviceName) ) } TRANSFER_INITIATED_COMMAND_NAME -> { val futureTask = FutureTask { fakeUndoRunnable } - mediaTttChipController.displayChip( - TransferInitiated(otherDeviceName, appIconDrawable, futureTask) + mediaTttChipControllerSender.displayChip( + TransferInitiated(appIconDrawable, otherDeviceName, futureTask) ) mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME) } TRANSFER_SUCCEEDED_COMMAND_NAME -> { - mediaTttChipController.displayChip( - TransferSucceeded(otherDeviceName, appIconDrawable, fakeUndoRunnable) + mediaTttChipControllerSender.displayChip( + TransferSucceeded(appIconDrawable, otherDeviceName, fakeUndoRunnable) ) } else -> { @@ -90,7 +94,7 @@ class MediaTttCommandLineHelper @Inject constructor( inner class RemoveChipCommand : Command { override fun execute(pw: PrintWriter, args: List<String>) { - mediaTttChipController.removeChip() + mediaTttChipControllerSender.removeChip() } override fun help(pw: PrintWriter) { pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG") @@ -114,3 +118,4 @@ val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!! val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!! private const val FUTURE_WAIT_TIME = 2000L +private const val TAG = "MediaTapToTransferCli" diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md new file mode 100644 index 000000000000..114589119cea --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md @@ -0,0 +1,11 @@ +# Media Tap-To-Transfer + +This package (and child packages) include code for the media tap-to-transfer feature, which +allows users to easily transfer playing media between devices. + +In media transfer, there are two devices: the *sender* and the *receiver*. The sender device will +start and stop media casts to the receiver device. On both devices, System UI will display a chip +informing the user about the media cast occurring. + +This package is structured so that the sender code is in the sender package, the receiver code is +in the receiver package, and code that's shared between them is in the common package. 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 new file mode 100644 index 000000000000..3b429c887796 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt @@ -0,0 +1,104 @@ +/* + * 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.common + +import android.annotation.LayoutRes +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.PixelFormat +import android.view.Gravity +import android.view.LayoutInflater +import android.view.ViewGroup +import android.view.WindowManager +import com.android.internal.widget.CachingIconView +import com.android.systemui.R + +/** + * A superclass controller that provides common functionality for showing chips on the sender device + * and the receiver device. + * + * Subclasses need to override and implement [updateChipView], which is where they can control what + * gets displayed to the user. + */ +abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( + private val context: Context, + private val windowManager: WindowManager, + @LayoutRes private val chipLayoutRes: Int +) { + /** The window layout parameters we'll use when attaching the view to a window. */ + @SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY + private val windowLayoutParams = WindowManager.LayoutParams().apply { + width = WindowManager.LayoutParams.WRAP_CONTENT + height = WindowManager.LayoutParams.WRAP_CONTENT + gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) + type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY + flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + title = "Media Tap-To-Transfer Chip View" + format = PixelFormat.TRANSLUCENT + setTrustedOverlay() + } + + /** The chip view currently being displayed. Null if the chip is not being displayed. */ + var chipView: ViewGroup? = null + + /** + * Displays the chip with the current state. + * + * This method handles inflating and attaching the view, then delegates to [updateChipView] to + * display the correct information in the chip. + */ + fun displayChip(chipState: T) { + val oldChipView = chipView + if (chipView == null) { + chipView = LayoutInflater + .from(context) + .inflate(chipLayoutRes, null) as ViewGroup + } + val currentChipView = chipView!! + + updateChipView(chipState, currentChipView) + + // Add view if necessary + if (oldChipView == null) { + windowManager.addView(chipView, windowLayoutParams) + } + } + + + /** Hides the chip. */ + fun removeChip() { + if (chipView == null) { return } + windowManager.removeView(chipView) + chipView = null + } + + /** + * A method implemented by subclasses to update [currentChipView] based on [chipState]. + */ + abstract fun updateChipView(chipState: T, currentChipView: ViewGroup) + + /** + * An internal method to set the icon on the view. + * + * This is in the common superclass since both the sender and the receiver show an icon. + */ + internal fun setIcon(chipState: T, currentChipView: ViewGroup) { + currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply { + this.setImageDrawable(chipState.appIconDrawable) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt new file mode 100644 index 000000000000..1e475a5e7c82 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt @@ -0,0 +1,28 @@ +/* + * 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.common + +import android.graphics.drawable.Drawable + +/** + * A superclass chip state that will be subclassed by the sender chip and receiver chip. + * + * @property appIconDrawable a drawable representing the icon of the app playing the media. + */ +open class MediaTttChipState( + internal val appIconDrawable: Drawable +) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt index 62e3b1ac8d5c..24943b9e2a1e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt @@ -14,52 +14,52 @@ * limitations under the License. */ -package com.android.systemui.media.taptotransfer +package com.android.systemui.media.taptotransfer.sender import android.graphics.drawable.Drawable import androidx.annotation.StringRes import com.android.systemui.R +import com.android.systemui.media.taptotransfer.common.MediaTttChipState import java.util.concurrent.Future /** - * A class that stores all the information necessary to display the media tap-to-transfer chip in - * certain states. + * A class that stores all the information necessary to display the media tap-to-transfer chip on + * the sender device. * * 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. * * @property chipText a string resource for the text that the chip should display. * @property otherDeviceName the name of the other device involved in the transfer. - * @property appIconDrawable a drawable representing the icon of the app playing the media. */ -sealed class MediaTttChipState( +sealed class ChipStateSender( + appIconDrawable: Drawable, @StringRes internal val chipText: Int, internal val otherDeviceName: String, - internal val appIconDrawable: Drawable, -) +) : MediaTttChipState(appIconDrawable) /** * 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( + appIconDrawable: Drawable, otherDeviceName: String, - appIconDrawable: Drawable -) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName, appIconDrawable) +) : ChipStateSender(appIconDrawable, R.string.media_move_closer_to_transfer, otherDeviceName) /** * A state representing that a transfer has been initiated (but not completed). * * @property future a future that will be resolved when the transfer has either succeeded or failed. * If the transfer succeeded, the future can optionally return an undo runnable (see - * [TransferSucceeded.undoRunnable]). [MediaTttChipController] is responsible for transitioning + * [TransferSucceeded.undoRunnable]). [MediaTttChipControllerSender] is responsible for transitioning * the chip to the [TransferSucceeded] state if the future resolves successfully. */ class TransferInitiated( - otherDeviceName: String, appIconDrawable: Drawable, + otherDeviceName: String, val future: Future<Runnable?> -) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName, appIconDrawable) +) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName) /** * A state representing that a transfer has been successfully completed. @@ -68,7 +68,7 @@ class TransferInitiated( * show an Undo button on the chip if this runnable is present. */ class TransferSucceeded( - otherDeviceName: String, appIconDrawable: Drawable, + otherDeviceName: String, val undoRunnable: Runnable? = null -) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName, appIconDrawable) +) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt index 2b55d634b382..fce4b98f9abe 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt @@ -14,71 +14,40 @@ * limitations under the License. */ -package com.android.systemui.media.taptotransfer +package com.android.systemui.media.taptotransfer.sender -import android.annotation.SuppressLint import android.content.Context -import android.graphics.PixelFormat -import android.view.Gravity -import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.view.WindowManager -import android.widget.LinearLayout import android.widget.TextView -import com.android.internal.widget.CachingIconView import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon import java.util.concurrent.Executor import java.util.concurrent.TimeUnit import javax.inject.Inject -const val TAG = "MediaTapToTransfer" - /** - * A controller to display and hide the Media Tap-To-Transfer chip. This chip is shown when a user - * is currently playing media on a local "media cast sender" device (e.g. a phone) and gets close - * enough to a "media cast receiver" device (e.g. a tablet). This chip encourages the user to - * transfer the media from the sender device to the receiver device. + * A controller to display and hide the Media Tap-To-Transfer chip on the **sending** device. This + * chip is shown when a user is transferring media to/from this device and a receiver device. */ @SysUISingleton -class MediaTttChipController @Inject constructor( - private val context: Context, - private val windowManager: WindowManager, +class MediaTttChipControllerSender @Inject constructor( + context: Context, + windowManager: WindowManager, @Main private val mainExecutor: Executor, @Background private val backgroundExecutor: Executor, +) : MediaTttChipControllerCommon<ChipStateSender>( + context, windowManager, R.layout.media_ttt_chip ) { - @SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY - private val windowLayoutParams = WindowManager.LayoutParams().apply { - width = WindowManager.LayoutParams.WRAP_CONTENT - height = WindowManager.LayoutParams.WRAP_CONTENT - gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) - type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY - flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - title = "Media Tap-To-Transfer Chip View" - format = PixelFormat.TRANSLUCENT - setTrustedOverlay() - } - - /** The chip view currently being displayed. Null if the chip is not being displayed. */ - private var chipView: LinearLayout? = null - /** Displays the chip view for the given state. */ - fun displayChip(chipState: MediaTttChipState) { - val oldChipView = chipView - if (chipView == null) { - chipView = LayoutInflater - .from(context) - .inflate(R.layout.media_ttt_chip, null) as LinearLayout - } - val currentChipView = chipView!! - + override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) { // App icon - currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply { - this.setImageDrawable(chipState.appIconDrawable) - } + setIcon(chipState, currentChipView) // Text currentChipView.requireViewById<TextView>(R.id.text).apply { @@ -108,18 +77,6 @@ class MediaTttChipController @Inject constructor( if (chipState is TransferInitiated) { addFutureCallback(chipState) } - - // Add view if necessary - if (oldChipView == null) { - windowManager.addView(chipView, windowLayoutParams) - } - } - - /** Hides the chip. */ - fun removeChip() { - if (chipView == null) { return } - windowManager.removeView(chipView) - chipView = null } /** @@ -136,7 +93,7 @@ class MediaTttChipController @Inject constructor( mainExecutor.execute { displayChip( TransferSucceeded( - chipState.otherDeviceName, chipState.appIconDrawable, undoRunnable + chipState.appIconDrawable, chipState.otherDeviceName, undoRunnable ) ) } 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 91b529875654..b29e4b85f81c 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 @@ -18,6 +18,10 @@ package com.android.systemui.media.taptotransfer import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender +import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer +import com.android.systemui.media.taptotransfer.sender.TransferInitiated +import com.android.systemui.media.taptotransfer.sender.TransferSucceeded import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.concurrency.FakeExecutor @@ -42,14 +46,17 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper @Mock - private lateinit var mediaTttChipController: MediaTttChipController + private lateinit var mediaTttChipControllerSender: MediaTttChipControllerSender @Before fun setUp() { MockitoAnnotations.initMocks(this) mediaTttCommandLineHelper = MediaTttCommandLineHelper( - commandRegistry, context, mediaTttChipController, FakeExecutor(FakeSystemClock()) + commandRegistry, + context, + mediaTttChipControllerSender, + FakeExecutor(FakeSystemClock()) ) } @@ -75,28 +82,28 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { fun moveCloserToTransfer_chipDisplayWithCorrectState() { commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) - verify(mediaTttChipController).displayChip(any(MoveCloserToTransfer::class.java)) + verify(mediaTttChipControllerSender).displayChip(any(MoveCloserToTransfer::class.java)) } @Test fun transferInitiated_chipDisplayWithCorrectState() { commandRegistry.onShellCommand(pw, getTransferInitiatedCommand()) - verify(mediaTttChipController).displayChip(any(TransferInitiated::class.java)) + verify(mediaTttChipControllerSender).displayChip(any(TransferInitiated::class.java)) } @Test fun transferSucceeded_chipDisplayWithCorrectState() { commandRegistry.onShellCommand(pw, getTransferSucceededCommand()) - verify(mediaTttChipController).displayChip(any(TransferSucceeded::class.java)) + verify(mediaTttChipControllerSender).displayChip(any(TransferSucceeded::class.java)) } @Test fun removeCommand_chipRemoved() { commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_TAG)) - verify(mediaTttChipController).removeChip() + verify(mediaTttChipControllerSender).removeChip() } private fun getMoveCloserToTransferCommand(): Array<String> = diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt new file mode 100644 index 000000000000..b74ba2646ba3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt @@ -0,0 +1,94 @@ +/* + * 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.common + +import android.content.Context +import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon +import android.view.ViewGroup +import android.view.WindowManager +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +class MediaTttChipControllerCommonTest : SysuiTestCase() { + private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState> + + private lateinit var appIconDrawable: Drawable + @Mock + private lateinit var windowManager: WindowManager + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) + controllerCommon = TestControllerCommon(context, windowManager) + } + + @Test + fun displayChip_chipAdded() { + controllerCommon.displayChip(MediaTttChipState(appIconDrawable)) + + verify(windowManager).addView(any(), any()) + } + + @Test + fun displayChip_twice_chipNotAddedTwice() { + controllerCommon.displayChip(MediaTttChipState(appIconDrawable)) + reset(windowManager) + + controllerCommon.displayChip(MediaTttChipState(appIconDrawable)) + verify(windowManager, never()).addView(any(), any()) + } + + @Test + fun removeChip_chipRemoved() { + // First, add the chip + controllerCommon.displayChip(MediaTttChipState(appIconDrawable)) + + // Then, remove it + controllerCommon.removeChip() + + verify(windowManager).removeView(any()) + } + + @Test + fun removeChip_noAdd_viewNotRemoved() { + controllerCommon.removeChip() + + verify(windowManager, never()).removeView(any()) + } + + inner class TestControllerCommon( + context: Context, + windowManager: WindowManager + ) : MediaTttChipControllerCommon<MediaTttChipState>( + context, windowManager, R.layout.media_ttt_chip + ) { + override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) { + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index de6525a2e750..028ec55ce28e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.media.taptotransfer +package com.android.systemui.media.taptotransfer.sender import android.graphics.drawable.Drawable import android.graphics.drawable.Icon @@ -35,21 +35,19 @@ import org.junit.Before import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.concurrent.Future @SmallTest -class MediaTttChipControllerTest : SysuiTestCase() { +class MediaTttChipControllerSenderTest : SysuiTestCase() { private lateinit var appIconDrawable: Drawable private lateinit var fakeMainClock: FakeSystemClock private lateinit var fakeMainExecutor: FakeExecutor private lateinit var fakeBackgroundClock: FakeSystemClock private lateinit var fakeBackgroundExecutor: FakeExecutor - private lateinit var mediaTttChipController: MediaTttChipController + private lateinit var controllerSender: MediaTttChipControllerSender @Mock private lateinit var windowManager: WindowManager @@ -62,48 +60,14 @@ class MediaTttChipControllerTest : SysuiTestCase() { fakeMainExecutor = FakeExecutor(fakeMainClock) fakeBackgroundClock = FakeSystemClock() fakeBackgroundExecutor = FakeExecutor(fakeBackgroundClock) - mediaTttChipController = MediaTttChipController( + controllerSender = MediaTttChipControllerSender( context, windowManager, fakeMainExecutor, fakeBackgroundExecutor ) } @Test - fun displayChip_chipAdded() { - mediaTttChipController.displayChip(moveCloserToTransfer()) - - verify(windowManager).addView(any(), any()) - } - - @Test - fun displayChip_twice_chipNotAddedTwice() { - mediaTttChipController.displayChip(moveCloserToTransfer()) - reset(windowManager) - - mediaTttChipController.displayChip(moveCloserToTransfer()) - verify(windowManager, never()).addView(any(), any()) - } - - @Test - fun removeChip_chipRemoved() { - // First, add the chip - mediaTttChipController.displayChip(moveCloserToTransfer()) - - // Then, remove it - mediaTttChipController.removeChip() - - verify(windowManager).removeView(any()) - } - - @Test - fun removeChip_noAdd_viewNotRemoved() { - mediaTttChipController.removeChip() - - verify(windowManager, never()).removeView(any()) - } - - @Test fun moveCloserToTransfer_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() { - mediaTttChipController.displayChip(moveCloserToTransfer()) + controllerSender.displayChip(moveCloserToTransfer()) val chipView = getChipView() assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable) @@ -115,7 +79,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo() { val future: SettableFuture<Runnable?> = SettableFuture.create() - mediaTttChipController.displayChip(transferInitiated(future)) + controllerSender.displayChip(transferInitiated(future)) // Don't resolve the future in any way and don't run our executors @@ -132,7 +96,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { val future: SettableFuture<Runnable?> = SettableFuture.create() val undoRunnable = Runnable { } - mediaTttChipController.displayChip(transferInitiated(future)) + controllerSender.displayChip(transferInitiated(future)) future.set(undoRunnable) fakeBackgroundExecutor.advanceClockToLast() @@ -153,7 +117,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { fun transferInitiated_futureCancelled_chipRemoved() { val future: SettableFuture<Runnable?> = SettableFuture.create() - mediaTttChipController.displayChip(transferInitiated(future)) + controllerSender.displayChip(transferInitiated(future)) future.cancel(true) fakeBackgroundExecutor.advanceClockToLast() @@ -170,7 +134,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() { val future: SettableFuture<Runnable?> = SettableFuture.create() - mediaTttChipController.displayChip(transferInitiated(future)) + controllerSender.displayChip(transferInitiated(future)) // We won't set anything on the future, but we will still run the executors so that we're // waiting on the future resolving. If we have a bug in our code, then this test will time @@ -188,7 +152,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferSucceeded_appIcon_chipTextContainsDeviceName_noLoadingIcon() { - mediaTttChipController.displayChip(transferSucceeded()) + controllerSender.displayChip(transferSucceeded()) val chipView = getChipView() assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable) @@ -198,7 +162,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferSucceededNullUndoRunnable_noUndo() { - mediaTttChipController.displayChip(transferSucceeded(undoRunnable = null)) + controllerSender.displayChip(transferSucceeded(undoRunnable = null)) val chipView = getChipView() assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -206,7 +170,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferSucceededWithUndoRunnable_undoWithClick() { - mediaTttChipController.displayChip(transferSucceeded { }) + controllerSender.displayChip(transferSucceeded { }) val chipView = getChipView() assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE) @@ -218,7 +182,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { var runnableRun = false val runnable = Runnable { runnableRun = true } - mediaTttChipController.displayChip(transferSucceeded(undoRunnable = runnable)) + controllerSender.displayChip(transferSucceeded(undoRunnable = runnable)) getChipView().getUndoButton().performClick() assertThat(runnableRun).isTrue() @@ -226,32 +190,32 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() { - mediaTttChipController.displayChip(moveCloserToTransfer()) - mediaTttChipController.displayChip(transferInitiated()) + controllerSender.displayChip(moveCloserToTransfer()) + controllerSender.displayChip(transferInitiated()) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE) } @Test fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() { - mediaTttChipController.displayChip(transferInitiated()) - mediaTttChipController.displayChip(transferSucceeded()) + controllerSender.displayChip(transferInitiated()) + controllerSender.displayChip(transferSucceeded()) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE) } @Test fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() { - mediaTttChipController.displayChip(transferInitiated()) - mediaTttChipController.displayChip(transferSucceeded { }) + controllerSender.displayChip(transferInitiated()) + controllerSender.displayChip(transferSucceeded { }) assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE) } @Test fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() { - mediaTttChipController.displayChip(transferSucceeded()) - mediaTttChipController.displayChip(moveCloserToTransfer()) + controllerSender.displayChip(transferSucceeded()) + controllerSender.displayChip(moveCloserToTransfer()) assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE) } @@ -274,17 +238,17 @@ class MediaTttChipControllerTest : SysuiTestCase() { } /** Helper method providing default parameters to not clutter up the tests. */ - private fun moveCloserToTransfer() = MoveCloserToTransfer(DEVICE_NAME, appIconDrawable) + private fun moveCloserToTransfer() = MoveCloserToTransfer(appIconDrawable, DEVICE_NAME) /** Helper method providing default parameters to not clutter up the tests. */ private fun transferInitiated( future: Future<Runnable?> = TEST_FUTURE - ) = TransferInitiated(DEVICE_NAME, appIconDrawable, future) + ) = TransferInitiated(appIconDrawable, DEVICE_NAME, future) /** Helper method providing default parameters to not clutter up the tests. */ private fun transferSucceeded( undoRunnable: Runnable? = null - ) = TransferSucceeded(DEVICE_NAME, appIconDrawable, undoRunnable) + ) = TransferSucceeded(appIconDrawable, DEVICE_NAME, undoRunnable) } private const val DEVICE_NAME = "My Tablet" |