diff --git a/packages/SystemUI/res/drawable/media_ttt_chip_background_receiver.xml b/packages/SystemUI/res/drawable/media_ttt_chip_background_receiver.xml
new file mode 100644
index 0000000000000..708bc1ac7e8a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_ttt_chip_background_receiver.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
new file mode 100644
index 0000000000000..88feacd9bbd99
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 36d8f87a05813..99643a763ef7c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -977,7 +977,7 @@
4dp
16sp
-
+
16dp
16sp
24dp
@@ -985,6 +985,10 @@
8dp
-8dp
+
+ 100dp
+ 95dp
+
35dp
15dp
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 1e96809b413ba..cffb2f79ebfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -24,6 +24,7 @@ import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
+import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
import com.android.systemui.people.PeopleProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -135,6 +136,7 @@ public interface SysUIComponent {
getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init());
// No init method needed, just needs to be gotten so that it's created.
getMediaTttChipControllerSender();
+ getMediaTttChipControllerReceiver();
getMediaTttCommandLineHelper();
getUnfoldLatencyTracker().init();
}
@@ -192,6 +194,9 @@ public interface SysUIComponent {
/** */
Optional getMediaTttChipControllerSender();
+ /** */
+ Optional getMediaTttChipControllerReceiver();
+
/** */
Optional 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 d76d17910dc27..558f0e6342555 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -28,6 +28,7 @@ import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostStatesManager;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
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.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -93,6 +94,19 @@ public interface MediaModule {
context, windowManager, mainExecutor, backgroundExecutor));
}
+ /** */
+ @Provides
+ @SysUISingleton
+ static Optional providesMediaTttChipControllerReceiver(
+ MediaTttFlags mediaTttFlags,
+ Context context,
+ WindowManager windowManager) {
+ if (!mediaTttFlags.isMediaTttEnabled()) {
+ return Optional.empty();
+ }
+ return Optional.of(new MediaTttChipControllerReceiver(context, windowManager));
+ }
+
/** */
@Provides
@SysUISingleton
@@ -101,11 +115,17 @@ public interface MediaModule {
CommandRegistry commandRegistry,
Context context,
MediaTttChipControllerSender mediaTttChipControllerSender,
+ MediaTttChipControllerReceiver mediaTttChipControllerReceiver,
@Main DelayableExecutor mainExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttCommandLineHelper(
- commandRegistry, context, mediaTttChipControllerSender, mainExecutor));
+ return Optional.of(
+ new MediaTttCommandLineHelper(
+ commandRegistry,
+ context,
+ mediaTttChipControllerSender,
+ mediaTttChipControllerReceiver,
+ 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 93f7b7dff96ca..99b283e8ec028 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -17,12 +17,15 @@
package com.android.systemui.media.taptotransfer
import android.content.Context
+import android.graphics.Color
import android.graphics.drawable.Icon
import android.util.Log
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.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.TransferInitiated
@@ -43,17 +46,26 @@ class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
context: Context,
private val mediaTttChipControllerSender: MediaTttChipControllerSender,
+ private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
@Main private val mainExecutor: DelayableExecutor,
) {
private val appIconDrawable =
- Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+ Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
+ it.setTint(Color.YELLOW)
+ }
init {
- commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
- commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() }
+ commandRegistry.registerCommand(
+ ADD_CHIP_COMMAND_SENDER_TAG) { AddChipCommandSender() }
+ commandRegistry.registerCommand(
+ REMOVE_CHIP_COMMAND_SENDER_TAG) { RemoveChipCommandSender() }
+ commandRegistry.registerCommand(
+ ADD_CHIP_COMMAND_RECEIVER_TAG) { AddChipCommandReceiver() }
+ commandRegistry.registerCommand(
+ REMOVE_CHIP_COMMAND_RECEIVER_TAG) { RemoveChipCommandReceiver() }
}
- inner class AddChipCommand : Command {
+ inner class AddChipCommandSender : Command {
override fun execute(pw: PrintWriter, args: List) {
val otherDeviceName = args[0]
when (args[1]) {
@@ -86,18 +98,40 @@ class MediaTttCommandLineHelper @Inject constructor(
}
override fun help(pw: PrintWriter) {
- pw.println(
- "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG "
+ pw.println("Usage: adb shell cmd statusbar " +
+ "$ADD_CHIP_COMMAND_SENDER_TAG "
)
}
}
- inner class RemoveChipCommand : Command {
+ /** A command to REMOVE the media ttt chip on the SENDER device. */
+ inner class RemoveChipCommandSender : Command {
override fun execute(pw: PrintWriter, args: List) {
mediaTttChipControllerSender.removeChip()
}
override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG")
+ 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) {
+ mediaTttChipControllerReceiver.displayChip(ChipStateReceiver(appIconDrawable))
+ }
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_RECEIVER_TAG")
+ }
+ }
+
+ /** A command to REMOVE the media ttt chip on the RECEIVER device. */
+ inner class RemoveChipCommandReceiver : Command {
+ override fun execute(pw: PrintWriter, args: List) {
+ mediaTttChipControllerReceiver.removeChip()
+ }
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG")
}
}
@@ -107,9 +141,13 @@ class MediaTttCommandLineHelper @Inject constructor(
}
@VisibleForTesting
-const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add"
+const val ADD_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-add-sender"
@VisibleForTesting
-const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove"
+const val REMOVE_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-remove-sender"
+@VisibleForTesting
+const val ADD_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-add-receiver"
+@VisibleForTesting
+const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
@VisibleForTesting
val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleName!!
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
new file mode 100644
index 0000000000000..5397235e01375
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.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.receiver
+
+import android.graphics.drawable.Drawable
+import com.android.systemui.media.taptotransfer.common.MediaTttChipState
+
+/**
+ * A class that stores all the information necessary to display the media tap-to-transfer chip on
+ * the receiver device.
+ */
+class ChipStateReceiver(
+ appIconDrawable: Drawable
+) : MediaTttChipState(appIconDrawable)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
new file mode 100644
index 0000000000000..17809548d1fdc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.receiver
+
+import android.content.Context
+import android.view.ViewGroup
+import android.view.WindowManager
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
+import javax.inject.Inject
+
+/**
+ * A controller to display and hide the Media Tap-To-Transfer chip on the **receiving** device.
+ *
+ * This chip is shown when a user is transferring media to/from a sending device and this device.
+ */
+@SysUISingleton
+class MediaTttChipControllerReceiver @Inject constructor(
+ context: Context,
+ windowManager: WindowManager,
+) : MediaTttChipControllerCommon(
+ context, windowManager, R.layout.media_ttt_chip_receiver
+) {
+
+ override fun updateChipView(chipState: ChipStateReceiver, currentChipView: ViewGroup) {
+ setIcon(chipState, currentChipView)
+ }
+}
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 b29e4b85f81c5..dec5a100e20d2 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,8 @@ package com.android.systemui.media.taptotransfer
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.TransferInitiated
@@ -47,6 +49,8 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Mock
private lateinit var mediaTttChipControllerSender: MediaTttChipControllerSender
+ @Mock
+ private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
@Before
fun setUp() {
@@ -56,73 +60,106 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
commandRegistry,
context,
mediaTttChipControllerSender,
+ mediaTttChipControllerReceiver,
FakeExecutor(FakeSystemClock())
)
}
@Test(expected = IllegalStateException::class)
- fun constructor_addCommandAlreadyRegistered() {
+ fun constructor_addSenderCommandAlreadyRegistered() {
// Since creating the chip controller should automatically register the add command, it
// should throw when registering it again.
commandRegistry.registerCommand(
- ADD_CHIP_COMMAND_TAG
+ ADD_CHIP_COMMAND_SENDER_TAG
) { EmptyCommand() }
}
@Test(expected = IllegalStateException::class)
- fun constructor_removeCommandAlreadyRegistered() {
+ fun constructor_removeSenderCommandAlreadyRegistered() {
// Since creating the chip controller should automatically register the remove command, it
// should throw when registering it again.
commandRegistry.registerCommand(
- REMOVE_CHIP_COMMAND_TAG
+ REMOVE_CHIP_COMMAND_SENDER_TAG
+ ) { EmptyCommand() }
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun constructor_addReceiverCommandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the add command, it
+ // should throw when registering it again.
+ commandRegistry.registerCommand(
+ ADD_CHIP_COMMAND_RECEIVER_TAG
+ ) { EmptyCommand() }
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun constructor_removeReceiverCommandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the remove command, it
+ // should throw when registering it again.
+ commandRegistry.registerCommand(
+ REMOVE_CHIP_COMMAND_RECEIVER_TAG
) { EmptyCommand() }
}
@Test
- fun moveCloserToTransfer_chipDisplayWithCorrectState() {
+ fun sender_moveCloserToTransfer_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
verify(mediaTttChipControllerSender).displayChip(any(MoveCloserToTransfer::class.java))
}
@Test
- fun transferInitiated_chipDisplayWithCorrectState() {
+ fun sender_transferInitiated_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
verify(mediaTttChipControllerSender).displayChip(any(TransferInitiated::class.java))
}
@Test
- fun transferSucceeded_chipDisplayWithCorrectState() {
+ fun sender_transferSucceeded_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
verify(mediaTttChipControllerSender).displayChip(any(TransferSucceeded::class.java))
}
@Test
- fun removeCommand_chipRemoved() {
- commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_TAG))
+ fun sender_removeCommand_chipRemoved() {
+ commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_SENDER_TAG))
verify(mediaTttChipControllerSender).removeChip()
}
+ @Test
+ fun receiver_addCommand_chipAdded() {
+ commandRegistry.onShellCommand(pw, arrayOf(ADD_CHIP_COMMAND_RECEIVER_TAG))
+
+ verify(mediaTttChipControllerReceiver).displayChip(any(ChipStateReceiver::class.java))
+ }
+
+ @Test
+ fun receiver_removeCommand_chipRemoved() {
+ commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_RECEIVER_TAG))
+
+ verify(mediaTttChipControllerReceiver).removeChip()
+ }
+
private fun getMoveCloserToTransferCommand(): Array =
arrayOf(
- ADD_CHIP_COMMAND_TAG,
+ ADD_CHIP_COMMAND_SENDER_TAG,
DEVICE_NAME,
MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME
)
private fun getTransferInitiatedCommand(): Array =
arrayOf(
- ADD_CHIP_COMMAND_TAG,
+ ADD_CHIP_COMMAND_SENDER_TAG,
DEVICE_NAME,
TRANSFER_INITIATED_COMMAND_NAME
)
private fun getTransferSucceededCommand(): Array =
arrayOf(
- ADD_CHIP_COMMAND_TAG,
+ ADD_CHIP_COMMAND_SENDER_TAG,
DEVICE_NAME,
TRANSFER_SUCCEEDED_COMMAND_NAME
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
new file mode 100644
index 0000000000000..2ff472fc7abeb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.receiver
+
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.ImageView
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class MediaTttChipControllerReceiverTest : SysuiTestCase() {
+ private lateinit var controllerReceiver: MediaTttChipControllerReceiver
+
+ @Mock
+ private lateinit var windowManager: WindowManager
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ controllerReceiver = MediaTttChipControllerReceiver(context, windowManager)
+ }
+
+ @Test
+ fun displayChip_chipContainsIcon() {
+ val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+
+ controllerReceiver.displayChip(ChipStateReceiver(drawable))
+
+ assertThat(getChipView().getAppIconDrawable()).isEqualTo(drawable)
+ }
+
+ private fun getChipView(): ViewGroup {
+ val viewCaptor = ArgumentCaptor.forClass(View::class.java)
+ Mockito.verify(windowManager).addView(viewCaptor.capture(), any())
+ return viewCaptor.value as ViewGroup
+ }
+
+ private fun ViewGroup.getAppIconDrawable(): Drawable =
+ (this.requireViewById(R.id.app_icon)).drawable
+}