[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
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -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> =
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user