Merge "[Media TTT] Add app icon and name to the receiver SystemApi."
This commit is contained in:
committed by
Android (Google) Code Review
commit
dc5dff8698
@@ -914,7 +914,7 @@ package android.app {
|
||||
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride();
|
||||
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int);
|
||||
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferReceiverDisplay(int, @NonNull android.media.MediaRoute2Info);
|
||||
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferReceiverDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable android.graphics.drawable.Icon, @Nullable CharSequence);
|
||||
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferSenderDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
|
||||
field public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0; // 0x0
|
||||
field public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1; // 0x1
|
||||
|
||||
@@ -1012,6 +1012,8 @@ public class StatusBarManager {
|
||||
*
|
||||
* @param displayState the new state for media tap-to-transfer.
|
||||
* @param routeInfo the media route information for the media being transferred.
|
||||
* @param appIcon the icon of the app playing the media.
|
||||
* @param appName the name of the app playing the media.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -1019,11 +1021,13 @@ public class StatusBarManager {
|
||||
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
|
||||
public void updateMediaTapToTransferReceiverDisplay(
|
||||
@MediaTransferReceiverState int displayState,
|
||||
@NonNull MediaRoute2Info routeInfo) {
|
||||
@NonNull MediaRoute2Info routeInfo,
|
||||
@Nullable Icon appIcon,
|
||||
@Nullable CharSequence appName) {
|
||||
Objects.requireNonNull(routeInfo);
|
||||
IStatusBarService svc = getService();
|
||||
try {
|
||||
svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo);
|
||||
svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo, appIcon, appName);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -308,5 +308,7 @@ oneway interface IStatusBar
|
||||
/** Notifies System UI about an update to the media tap-to-transfer receiver state. */
|
||||
void updateMediaTapToTransferReceiverDisplay(
|
||||
int displayState,
|
||||
in MediaRoute2Info routeInfo);
|
||||
in MediaRoute2Info routeInfo,
|
||||
in Icon appIcon,
|
||||
in CharSequence appName);
|
||||
}
|
||||
|
||||
@@ -208,5 +208,7 @@ interface IStatusBarService
|
||||
/** Notifies System UI about an update to the media tap-to-transfer receiver state. */
|
||||
void updateMediaTapToTransferReceiverDisplay(
|
||||
int displayState,
|
||||
in MediaRoute2Info routeInfo);
|
||||
in MediaRoute2Info routeInfo,
|
||||
in Icon appIcon,
|
||||
in CharSequence appName);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.media.dagger;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
@@ -119,12 +120,14 @@ public interface MediaModule {
|
||||
MediaTttFlags mediaTttFlags,
|
||||
CommandQueue commandQueue,
|
||||
Context context,
|
||||
WindowManager windowManager) {
|
||||
WindowManager windowManager,
|
||||
@Main Handler mainHandler) {
|
||||
if (!mediaTttFlags.isMediaTttEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(
|
||||
new MediaTttChipControllerReceiver(commandQueue, context, windowManager));
|
||||
new MediaTttChipControllerReceiver(
|
||||
commandQueue, context, windowManager, mainHandler));
|
||||
}
|
||||
|
||||
/** */
|
||||
|
||||
@@ -147,12 +147,16 @@ class MediaTttCommandLineHelper @Inject constructor(
|
||||
CLOSE_TO_SENDER_STATE ->
|
||||
statusBarManager.updateMediaTapToTransferReceiverDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
|
||||
routeInfo
|
||||
routeInfo,
|
||||
null,
|
||||
null
|
||||
)
|
||||
FAR_FROM_SENDER_STATE ->
|
||||
statusBarManager.updateMediaTapToTransferReceiverDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
|
||||
routeInfo
|
||||
routeInfo,
|
||||
null,
|
||||
null
|
||||
)
|
||||
else ->
|
||||
pw.println("Invalid command name $commandName")
|
||||
|
||||
@@ -36,7 +36,7 @@ import com.android.systemui.R
|
||||
* gets displayed to the user.
|
||||
*/
|
||||
abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
private val context: Context,
|
||||
internal val context: Context,
|
||||
private val windowManager: WindowManager,
|
||||
@LayoutRes private val chipLayoutRes: Int
|
||||
) {
|
||||
|
||||
@@ -30,7 +30,7 @@ import android.util.Log
|
||||
open class MediaTttChipState(
|
||||
internal val appPackageName: String?,
|
||||
) {
|
||||
fun getAppIcon(context: Context): Drawable? {
|
||||
open fun getAppIcon(context: Context): Drawable? {
|
||||
appPackageName ?: return null
|
||||
return try {
|
||||
context.packageManager.getApplicationIcon(appPackageName)
|
||||
@@ -41,7 +41,7 @@ open class MediaTttChipState(
|
||||
}
|
||||
|
||||
/** Returns the name of the app playing the media or null if we can't find it. */
|
||||
fun getAppName(context: Context): String? {
|
||||
open fun getAppName(context: Context): String? {
|
||||
appPackageName ?: return null
|
||||
return try {
|
||||
context.packageManager.getApplicationInfo(
|
||||
|
||||
@@ -16,12 +16,35 @@
|
||||
|
||||
package com.android.systemui.media.taptotransfer.receiver
|
||||
|
||||
import android.content.Context
|
||||
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.
|
||||
*
|
||||
* @property appIconDrawable a drawable representing the icon of the app playing the media. If
|
||||
* present, this will be used in [this.getAppIcon] instead of [appPackageName].
|
||||
* @property appName a name for the app playing the media. If present, this will be used in
|
||||
* [this.getAppName] instead of [appPackageName].
|
||||
*/
|
||||
class ChipStateReceiver(
|
||||
appPackageName: String?,
|
||||
) : MediaTttChipState(appPackageName)
|
||||
private val appIconDrawable: Drawable?,
|
||||
private val appName: CharSequence?
|
||||
) : MediaTttChipState(appPackageName) {
|
||||
override fun getAppIcon(context: Context): Drawable? {
|
||||
if (appIconDrawable != null) {
|
||||
return appIconDrawable
|
||||
}
|
||||
return super.getAppIcon(context)
|
||||
}
|
||||
|
||||
override fun getAppName(context: Context): String? {
|
||||
if (appName != null) {
|
||||
return appName.toString()
|
||||
}
|
||||
return super.getAppName(context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,17 @@ package com.android.systemui.media.taptotransfer.receiver
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.media.MediaRoute2Info
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
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 com.android.systemui.statusbar.CommandQueue
|
||||
import javax.inject.Inject
|
||||
@@ -38,16 +43,19 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
commandQueue: CommandQueue,
|
||||
context: Context,
|
||||
windowManager: WindowManager,
|
||||
@Main private val mainHandler: Handler,
|
||||
) : MediaTttChipControllerCommon<ChipStateReceiver>(
|
||||
context, windowManager, R.layout.media_ttt_chip_receiver
|
||||
) {
|
||||
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
|
||||
override fun updateMediaTapToTransferReceiverDisplay(
|
||||
@StatusBarManager.MediaTransferReceiverState displayState: Int,
|
||||
routeInfo: MediaRoute2Info
|
||||
routeInfo: MediaRoute2Info,
|
||||
appIcon: Icon?,
|
||||
appName: CharSequence?
|
||||
) {
|
||||
this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay(
|
||||
displayState, routeInfo
|
||||
displayState, routeInfo, appIcon, appName
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -58,11 +66,28 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
|
||||
private fun updateMediaTapToTransferReceiverDisplay(
|
||||
@StatusBarManager.MediaTransferReceiverState displayState: Int,
|
||||
routeInfo: MediaRoute2Info
|
||||
routeInfo: MediaRoute2Info,
|
||||
appIcon: Icon?,
|
||||
appName: CharSequence?
|
||||
) {
|
||||
when(displayState) {
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER ->
|
||||
displayChip(ChipStateReceiver(routeInfo.packageName))
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> {
|
||||
val packageName = routeInfo.packageName
|
||||
if (appIcon == null) {
|
||||
displayChip(ChipStateReceiver(packageName, null, appName))
|
||||
} else {
|
||||
appIcon.loadDrawableAsync(
|
||||
context,
|
||||
Icon.OnDrawableLoadedListener { drawable ->
|
||||
displayChip(
|
||||
ChipStateReceiver(packageName, drawable, appName)
|
||||
)},
|
||||
// Notify the listener on the main handler since the listener will update
|
||||
// the UI.
|
||||
mainHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> removeChip()
|
||||
else ->
|
||||
Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
|
||||
|
||||
@@ -453,7 +453,9 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
/** @see IStatusBar#updateMediaTapToTransferReceiverDisplay */
|
||||
default void updateMediaTapToTransferReceiverDisplay(
|
||||
@StatusBarManager.MediaTransferReceiverState int displayState,
|
||||
@NonNull MediaRoute2Info routeInfo) {}
|
||||
@NonNull MediaRoute2Info routeInfo,
|
||||
@Nullable Icon appIcon,
|
||||
@Nullable CharSequence appName) {}
|
||||
}
|
||||
|
||||
public CommandQueue(Context context) {
|
||||
@@ -1208,10 +1210,14 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
@Override
|
||||
public void updateMediaTapToTransferReceiverDisplay(
|
||||
int displayState,
|
||||
MediaRoute2Info routeInfo) {
|
||||
@NonNull MediaRoute2Info routeInfo,
|
||||
@Nullable Icon appIcon,
|
||||
@Nullable CharSequence appName) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = displayState;
|
||||
args.arg2 = routeInfo;
|
||||
args.arg3 = appIcon;
|
||||
args.arg4 = appName;
|
||||
mHandler.obtainMessage(MSG_MEDIA_TRANSFER_RECEIVER_STATE, args).sendToTarget();
|
||||
}
|
||||
|
||||
@@ -1629,9 +1635,11 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
args = (SomeArgs) msg.obj;
|
||||
int receiverDisplayState = (int) args.arg1;
|
||||
MediaRoute2Info receiverRouteInfo = (MediaRoute2Info) args.arg2;
|
||||
Icon appIcon = (Icon) args.arg3;
|
||||
appName = (CharSequence) args.arg4;
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).updateMediaTapToTransferReceiverDisplay(
|
||||
receiverDisplayState, receiverRouteInfo);
|
||||
receiverDisplayState, receiverRouteInfo, appIcon, appName);
|
||||
}
|
||||
args.recycle();
|
||||
break;
|
||||
|
||||
@@ -203,7 +203,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
|
||||
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
|
||||
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER),
|
||||
any()
|
||||
any(),
|
||||
nullable(),
|
||||
nullable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -213,7 +215,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
|
||||
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
|
||||
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER),
|
||||
any()
|
||||
any(),
|
||||
nullable(),
|
||||
nullable()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package com.android.systemui.media.taptotransfer.receiver
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.media.MediaRoute2Info
|
||||
import android.os.Handler
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
@@ -51,7 +53,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
controllerReceiver = MediaTttChipControllerReceiver(commandQueue, context, windowManager)
|
||||
controllerReceiver = MediaTttChipControllerReceiver(
|
||||
commandQueue, context, windowManager, Handler.getMain())
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
verify(commandQueue).addCallback(callbackCaptor.capture())
|
||||
@@ -60,19 +63,24 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun commandQueueCallback_closeToSender_triggersChip() {
|
||||
val appName = "FakeAppName"
|
||||
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
|
||||
routeInfo
|
||||
routeInfo,
|
||||
/* appIcon= */ null,
|
||||
appName
|
||||
)
|
||||
|
||||
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(ROUTE_NAME)
|
||||
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(appName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun commandQueueCallback_farFromSender_noChipShown() {
|
||||
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
|
||||
routeInfo
|
||||
routeInfo,
|
||||
null,
|
||||
null
|
||||
)
|
||||
|
||||
verify(windowManager, never()).addView(any(), any())
|
||||
@@ -82,12 +90,16 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
fun commandQueueCallback_closeThenFar_chipShownThenHidden() {
|
||||
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
|
||||
routeInfo
|
||||
routeInfo,
|
||||
null,
|
||||
null
|
||||
)
|
||||
|
||||
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
|
||||
routeInfo
|
||||
routeInfo,
|
||||
null,
|
||||
null
|
||||
)
|
||||
|
||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||
@@ -96,16 +108,45 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayChip_chipContainsIcon() {
|
||||
val state = ChipStateReceiver(PACKAGE_NAME)
|
||||
fun displayChip_nullAppIconDrawable_iconIsFromPackageName() {
|
||||
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, "appName")
|
||||
|
||||
controllerReceiver.displayChip(state)
|
||||
|
||||
assertThat(getChipView().getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayChip_hasAppIconDrawable_iconIsDrawable() {
|
||||
val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
|
||||
val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName")
|
||||
|
||||
controllerReceiver.displayChip(state)
|
||||
|
||||
assertThat(getChipView().getAppIconView().drawable).isEqualTo(drawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() {
|
||||
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName = null)
|
||||
|
||||
controllerReceiver.displayChip(state)
|
||||
|
||||
assertThat(getChipView().getAppIconView().contentDescription)
|
||||
.isEqualTo(state.getAppName(context))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
|
||||
val appName = "FakeAppName"
|
||||
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName)
|
||||
|
||||
controllerReceiver.displayChip(state)
|
||||
|
||||
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(appName)
|
||||
}
|
||||
|
||||
private fun getChipView(): ViewGroup {
|
||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||
@@ -115,10 +156,9 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
|
||||
}
|
||||
|
||||
private const val ROUTE_NAME = "Test name"
|
||||
private const val PACKAGE_NAME = "com.android.systemui"
|
||||
|
||||
private val routeInfo = MediaRoute2Info.Builder("id", ROUTE_NAME)
|
||||
private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
|
||||
.addFeature("feature")
|
||||
.setPackageName(PACKAGE_NAME)
|
||||
.build()
|
||||
|
||||
@@ -2031,11 +2031,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
@Override
|
||||
public void updateMediaTapToTransferReceiverDisplay(
|
||||
@StatusBarManager.MediaTransferReceiverState int displayState,
|
||||
MediaRoute2Info routeInfo) {
|
||||
MediaRoute2Info routeInfo,
|
||||
@Nullable Icon appIcon,
|
||||
@Nullable CharSequence appName) {
|
||||
enforceMediaContentControl();
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo);
|
||||
mBar.updateMediaTapToTransferReceiverDisplay(
|
||||
displayState, routeInfo, appIcon, appName);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "updateMediaTapToTransferReceiverDisplay", e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user