Merge "[Media TTT] Add the aidl interface for the sender and a service that implements it."
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a741514..e9e85f1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -855,6 +855,12 @@
android:singleUser="true"
android:permission="android.permission.BIND_DREAM_SERVICE" />
+ <!-- Service for external clients to do media transfer -->
+ <!-- TODO(b/203800643): Export and guard with a permission. -->
+ <service
+ android:name=".media.taptotransfer.sender.MediaTttSenderService"
+ />
+
<receiver
android:name=".tuner.TunerService$ClearReceiver"
android:exported="false">
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2b6c9f5..08fb2c6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2152,8 +2152,8 @@
<!--- ****** Media tap-to-transfer ****** -->
<!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
<string name="media_transfer_undo">Undo</string>
- <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play music on the different device. [CHAR LIMIT=75] -->
- <string name="media_move_closer_to_transfer">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+ <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play media on the different device. [CHAR LIMIT=75] -->
+ <string name="media_move_closer_to_start_cast">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
<!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
<string name="media_transfer_playing">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
new file mode 100644
index 0000000..f34b8d6
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.shared.mediattt;
+
+import android.media.MediaRoute2Info;
+
+/**
+ * A callback interface that can be invoked to trigger media transfer events on System UI.
+ *
+ * This interface is for the *sender* device, which is the device currently playing media. This
+ * sender device can transfer the media to a different device, called the receiver.
+ *
+ * System UI will implement this interface and other services will invoke it.
+ */
+interface IDeviceSenderCallback {
+ /**
+ * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
+ * the user can potentially *start* a cast to the receiver device if the user moves their device
+ * a bit closer.
+ *
+ * Important notes:
+ * - When this callback triggers, the device is close enough to inform the user that
+ * transferring is an option, but the device is *not* close enough to actually initiate a
+ * transfer yet.
+ * - This callback is for *starting* a cast. It should be used when this device is currently
+ * playing media locally and the media should be transferred to be played on the receiver
+ * device instead.
+ */
+ // TODO(b/203800643): Add the otherDeviceInfo parameter.
+ oneway void closeToReceiverToStartCast(in MediaRoute2Info mediaInfo);
+}
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 558f0e6..d1fe7d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.media.dagger;
+import android.app.Service;
import android.content.Context;
import android.view.WindowManager;
@@ -30,6 +31,7 @@
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -38,8 +40,11 @@
import javax.inject.Named;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
/** Dagger module for the media package. */
@Module
@@ -128,4 +133,10 @@
mediaTttChipControllerReceiver,
mainExecutor));
}
+
+ /** Inject into MediaTttSenderService. */
+ @Binds
+ @IntoMap
+ @ClassKey(MediaTttSenderService.class)
+ Service bindMediaTttSenderService(MediaTttSenderService service);
}
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 5a86723..0078b95 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,9 +16,14 @@
package com.android.systemui.media.taptotransfer
+import android.content.ComponentName
import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
import android.graphics.Color
import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
+import android.os.IBinder
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.R
@@ -27,9 +32,11 @@
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
+import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
import com.android.systemui.media.taptotransfer.sender.TransferInitiated
import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -44,11 +51,14 @@
@SysUISingleton
class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
- context: Context,
+ private val context: Context,
private val mediaTttChipControllerSender: MediaTttChipControllerSender,
private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
@Main private val mainExecutor: DelayableExecutor,
) {
+ private var senderCallback: IDeviceSenderCallback? = null
+ private val senderServiceConnection = SenderServiceConnection()
+
private val appIconDrawable =
Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
it.setTint(Color.YELLOW)
@@ -68,14 +78,19 @@
inner class AddChipCommandSender : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
val otherDeviceName = args[0]
+ val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
+ .addFeature("feature")
+ .build()
+
when (args[1]) {
- MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
- mediaTttChipControllerSender.displayChip(
- MoveCloserToTransfer(
- appIconDrawable, APP_ICON_CONTENT_DESCRIPTION, otherDeviceName
- )
- )
+ MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> {
+ runOnService { senderCallback ->
+ senderCallback.closeToReceiverToStartCast(mediaInfo)
+ }
}
+
+ // TODO(b/203800643): Migrate other commands to invoke the service instead of the
+ // controller.
TRANSFER_INITIATED_COMMAND_NAME -> {
val futureTask = FutureTask { fakeUndoRunnable }
mediaTttChipControllerSender.displayChip(
@@ -101,7 +116,7 @@
}
else -> {
pw.println("Chip type must be one of " +
- "$MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME, " +
+ "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
"$TRANSFER_INITIATED_COMMAND_NAME, " +
TRANSFER_SUCCEEDED_COMMAND_NAME
)
@@ -114,19 +129,40 @@
"$ADD_CHIP_COMMAND_SENDER_TAG <deviceName> <chipStatus>"
)
}
+
+ private fun runOnService(command: SenderCallbackCommand) {
+ val currentServiceCallback = senderCallback
+ if (currentServiceCallback != null) {
+ command.run(currentServiceCallback)
+ } else {
+ bindService(command)
+ }
+ }
+
+ private fun bindService(command: SenderCallbackCommand) {
+ senderServiceConnection.pendingCommand = command
+ val binding = context.bindService(
+ Intent(context, MediaTttSenderService::class.java),
+ senderServiceConnection,
+ Context.BIND_AUTO_CREATE
+ )
+ Log.i(TAG, "Starting service binding? $binding")
+ }
}
/** A command to REMOVE the media ttt chip on the SENDER device. */
inner class RemoveChipCommandSender : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
mediaTttChipControllerSender.removeChip()
+ if (senderCallback != null) {
+ context.unbindService(senderServiceConnection)
+ }
}
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_SENDER_TAG")
}
}
-
/** A command to DISPLAY the media ttt chip on the RECEIVER device. */
inner class AddChipCommandReceiver : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
@@ -149,6 +185,29 @@
}
}
+ /** A service connection for [IDeviceSenderCallback]. */
+ private inner class SenderServiceConnection : ServiceConnection {
+ // A command that should be run when the service gets connected.
+ var pendingCommand: SenderCallbackCommand? = null
+
+ override fun onServiceConnected(className: ComponentName, service: IBinder) {
+ val newCallback = IDeviceSenderCallback.Stub.asInterface(service)
+ senderCallback = newCallback
+ pendingCommand?.run(newCallback)
+ pendingCommand = null
+ }
+
+ override fun onServiceDisconnected(className: ComponentName) {
+ senderCallback = null
+ }
+ }
+
+ /** An interface defining a command that should be run on the sender callback. */
+ private fun interface SenderCallbackCommand {
+ /** Runs the command on the provided [senderCallback]. */
+ fun run(senderCallback: IDeviceSenderCallback)
+ }
+
private val fakeUndoRunnable = Runnable {
Log.i(TAG, "Undo runnable triggered")
}
@@ -163,7 +222,7 @@
@VisibleForTesting
const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
@VisibleForTesting
-val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleName!!
+val MOVE_CLOSER_TO_START_CAST_COMMAND_NAME = MoveCloserToStartCast::class.simpleName!!
@VisibleForTesting
val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index b1f6faa..dd434e7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -40,17 +40,18 @@
) : MediaTttChipState(appIconDrawable, appIconContentDescription)
/**
- * 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.
+ * A state representing that the two devices are close but not close enough to *start* a cast to
+ * the receiver device. The chip will instruct the user to move closer in order to initiate the
+ * transfer to the receiver.
*/
-class MoveCloserToTransfer(
+class MoveCloserToStartCast(
appIconDrawable: Drawable,
appIconContentDescription: String,
otherDeviceName: String,
) : ChipStateSender(
appIconDrawable,
appIconContentDescription,
- R.string.media_move_closer_to_transfer,
+ R.string.media_move_closer_to_start_cast,
otherDeviceName
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
new file mode 100644
index 0000000..a7e70fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.sender
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
+import android.os.IBinder
+import com.android.systemui.R
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import javax.inject.Inject
+
+/**
+ * Service that allows external handlers to trigger the media chip on the sender device.
+ */
+class MediaTttSenderService @Inject constructor(
+ context: Context,
+ val controller: MediaTttChipControllerSender
+) : Service() {
+
+ // TODO(b/203800643): Add logging when callbacks trigger.
+ private val binder: IBinder = object : IDeviceSenderCallback.Stub() {
+ override fun closeToReceiverToStartCast(mediaInfo: MediaRoute2Info) {
+ this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo)
+ }
+ }
+
+ // TODO(b/203800643): Use the app icon from the media info instead of a fake one.
+ private val fakeAppIconDrawable =
+ Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
+ it.setTint(Color.YELLOW)
+ }
+
+ override fun onBind(intent: Intent?): IBinder = binder
+
+ private fun closeToReceiverToStartCast(mediaInfo: MediaRoute2Info) {
+ val chipState = MoveCloserToStartCast(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString(),
+ otherDeviceName = FAKE_DEVICE_NAME
+ )
+ controller.displayChip(chipState)
+ }
+}
+
+private const val FAKE_DEVICE_NAME = "Fake Other Device Name"
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 dec5a10..abf53d3 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
@@ -16,23 +16,28 @@
package com.android.systemui.media.taptotransfer
+import android.content.ComponentName
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
import com.android.systemui.media.taptotransfer.sender.TransferInitiated
import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import java.io.PrintWriter
import java.io.StringWriter
@@ -51,10 +56,19 @@
private lateinit var mediaTttChipControllerSender: MediaTttChipControllerSender
@Mock
private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
+ @Mock
+ private lateinit var mediaSenderService: IDeviceSenderCallback.Stub
+ private lateinit var mediaSenderServiceComponentName: ComponentName
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
+ mediaSenderServiceComponentName = ComponentName(context, MediaTttSenderService::class.java)
+ context.addMockService(mediaSenderServiceComponentName, mediaSenderService)
+ whenever(mediaSenderService.queryLocalInterface(anyString())).thenReturn(mediaSenderService)
+ whenever(mediaSenderService.asBinder()).thenReturn(mediaSenderService)
+
mediaTttCommandLineHelper =
MediaTttCommandLineHelper(
commandRegistry,
@@ -102,10 +116,11 @@
}
@Test
- fun sender_moveCloserToTransfer_chipDisplayWithCorrectState() {
- commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ fun sender_moveCloserToStartCast_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(pw, getMoveCloserToStartCastCommand())
- verify(mediaTttChipControllerSender).displayChip(any(MoveCloserToTransfer::class.java))
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+ verify(mediaSenderService).closeToReceiverToStartCast(any())
}
@Test
@@ -143,11 +158,11 @@
verify(mediaTttChipControllerReceiver).removeChip()
}
- private fun getMoveCloserToTransferCommand(): Array<String> =
+ private fun getMoveCloserToStartCastCommand(): Array<String> =
arrayOf(
ADD_CHIP_COMMAND_SENDER_TAG,
DEVICE_NAME,
- MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME
+ MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
)
private fun getTransferInitiatedCommand(): Array<String> =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index caef5b9..ecc4c46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -66,8 +66,8 @@
}
@Test
- fun moveCloserToTransfer_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
- controllerSender.displayChip(moveCloserToTransfer())
+ fun moveCloserToStartCast_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
+ controllerSender.displayChip(moveCloserToStartCast())
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
@@ -192,8 +192,8 @@
}
@Test
- fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
- controllerSender.displayChip(moveCloserToTransfer())
+ fun changeFromCloserToStartToTransferInitiated_loadingIconAppears() {
+ controllerSender.displayChip(moveCloserToStartCast())
controllerSender.displayChip(transferInitiated())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
@@ -216,9 +216,9 @@
}
@Test
- fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() {
+ fun changeFromTransferSucceededToMoveCloserToStart_undoButtonDisappears() {
controllerSender.displayChip(transferSucceeded())
- controllerSender.displayChip(moveCloserToTransfer())
+ controllerSender.displayChip(moveCloserToStartCast())
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
@@ -240,8 +240,8 @@
}
/** Helper method providing default parameters to not clutter up the tests. */
- private fun moveCloserToTransfer() =
- MoveCloserToTransfer(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+ private fun moveCloserToStartCast() =
+ MoveCloserToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferInitiated(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
new file mode 100644
index 0000000..5849ed5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
@@ -0,0 +1,40 @@
+package com.android.systemui.media.taptotransfer.sender
+
+import android.media.MediaRoute2Info
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+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
+
+@SmallTest
+class MediaTttSenderServiceTest : SysuiTestCase() {
+
+ private lateinit var service: MediaTttSenderService
+ private lateinit var callback: IDeviceSenderCallback
+
+ @Mock
+ private lateinit var controller: MediaTttChipControllerSender
+
+ private val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
+ .addFeature("feature")
+ .build()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ service = MediaTttSenderService(context, controller)
+ callback = IDeviceSenderCallback.Stub.asInterface(service.onBind(null))
+ }
+
+ @Test
+ fun closeToReceiverToStartCast_controllerTriggeredWithMoveCloserToStartCastState() {
+ callback.closeToReceiverToStartCast(mediaInfo)
+
+ verify(controller).displayChip(any<MoveCloserToStartCast>())
+ }
+}