diff --git a/core/api/system-current.txt b/core/api/system-current.txt index fd716f341540a..3572d011ab570 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -910,6 +910,19 @@ 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 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 + field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1; // 0x1 + field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0; // 0x0 + field public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8; // 0x8 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6; // 0x6 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4; // 0x4 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2; // 0x2 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7; // 0x7 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5; // 0x5 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3; // 0x3 field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1 field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0 } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 56c301f30d5fc..8fcb07f578e82 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -16,6 +16,7 @@ package android.app; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,6 +28,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.graphics.drawable.Icon; +import android.media.MediaRoute2Info; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -39,6 +41,7 @@ import android.view.View; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.NotificationVisibility; import java.lang.annotation.Retention; @@ -338,6 +341,166 @@ public class StatusBarManager { @Retention(RetentionPolicy.SOURCE) public @interface NavBarModeOverride {} + /** + * State indicating that this sender device 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: + *

+ * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0; + + /** + * State indicating that this sender device is close to a receiver device, so the user can + * potentially *end* a cast on the receiver device if the user moves this device a bit closer. + *

+ * Important notes: + *

+ * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1; + + /** + * State indicating that a media transfer from this sender device to a receiver device has been + * started. + *

+ * Important note: This state is for *starting* a cast. It should be used when this device is + * currently playing media locally and the media has started being transferred to the receiver + * device instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2; + + /** + * State indicating that a media transfer from the receiver and back to this sender device + * has been started. + *

+ * Important note: This state is for *ending* a cast. It should be used when media is currently + * being played on the receiver device and the media has started being transferred to play + * locally instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3; + + /** + * State indicating that a media transfer from this sender device to a receiver device has + * finished successfully. + *

+ * Important note: This state is for *starting* a cast. It should be used when this device had + * previously been playing media locally and the media has successfully been transferred to the + * receiver device instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4; + + /** + * State indicating that a media transfer from the receiver and back to this sender device has + * finished successfully. + *

+ * Important note: This state is for *ending* a cast. It should be used when media was + * previously being played on the receiver device and has been successfully transferred to play + * locally on this device instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5; + + /** + * State indicating that the attempted transfer to the receiver device has failed. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6; + + /** + * State indicating that the attempted transfer back to this device has failed. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7; + + /** + * State indicating that this sender device is no longer close to the receiver device. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8; + + /** @hide */ + @IntDef(prefix = {"MEDIA_TRANSFER_SENDER_STATE_"}, value = { + MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, + MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED, + MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MediaTransferSenderState {} + + /** + * State indicating that this receiver device is close to a sender device, so the user can + * potentially start or end a cast to the receiver device if the user moves the sender device a + * bit closer. + *

+ * Important note: This state represents that 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. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0; + + /** + * State indicating that this receiver device is no longer close to the sender device. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1; + + /** @hide */ + @IntDef(prefix = {"MEDIA_TRANSFER_RECEIVER_STATE_"}, value = { + MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MediaTransferReceiverState {} + @UnsupportedAppUsage private Context mContext; private IStatusBarService mService; @@ -789,6 +952,81 @@ public class StatusBarManager { return navBarModeOverride; } + /** + * Notifies the system of a new media tap-to-transfer state for the sender device. + * + *

The callback should only be provided for the {@link + * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED} or {@link + * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED} states, since those are the + * only states where an action can be un-done. + * + * @param displayState the new state for media tap-to-transfer. + * @param routeInfo the media route information for the media being transferred. + * @param undoExecutor an executor to run the callback on and must be provided if the + * callback is non-null. + * @param undoCallback a callback that will be triggered if the user elects to undo a media + * transfer. + * + * @throws IllegalArgumentException if an undo callback is provided for states that are not a + * succeeded state. + * @throws IllegalArgumentException if an executor is not provided when a callback is. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void updateMediaTapToTransferSenderDisplay( + @MediaTransferSenderState int displayState, + @NonNull MediaRoute2Info routeInfo, + @Nullable Executor undoExecutor, + @Nullable Runnable undoCallback + ) { + Objects.requireNonNull(routeInfo); + if (displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED + && displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED + && undoCallback != null) { + throw new IllegalArgumentException( + "The undoCallback should only be provided when the state is a " + + "transfer succeeded state"); + } + if (undoCallback != null && undoExecutor == null) { + throw new IllegalArgumentException( + "You must pass an executor when you pass an undo callback"); + } + IStatusBarService svc = getService(); + try { + UndoCallback callbackProxy = null; + if (undoExecutor != null) { + callbackProxy = new UndoCallback(undoExecutor, undoCallback); + } + svc.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, callbackProxy); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Notifies the system of a new media tap-to-transfer state for the receiver device. + * + * @param displayState the new state for media tap-to-transfer. + * @param routeInfo the media route information for the media being transferred. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void updateMediaTapToTransferReceiverDisplay( + @MediaTransferReceiverState int displayState, + @NonNull MediaRoute2Info routeInfo) { + Objects.requireNonNull(routeInfo); + IStatusBarService svc = getService(); + try { + svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + /** @hide */ public static String windowStateToString(int state) { if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING"; @@ -1071,4 +1309,29 @@ public class StatusBarManager { mExecutor.execute(() -> mCallback.accept(userResponse)); } } + + /** + * @hide + */ + static final class UndoCallback extends IUndoMediaTransferCallback.Stub { + @NonNull + private final Executor mExecutor; + @NonNull + private final Runnable mCallback; + + UndoCallback(@NonNull Executor executor, @NonNull Runnable callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onUndoTriggered() { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(mCallback); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + } } diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 23ebc9f949156..51eb4296d7aee 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -24,12 +24,14 @@ import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsHbmListener; +import android.media.MediaRoute2Info; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.service.notification.StatusBarNotification; import android.view.InsetsVisibilities; import com.android.internal.statusbar.IAddTileResultCallback; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.view.AppearanceRegion; @@ -296,4 +298,15 @@ oneway interface IStatusBar void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback); void cancelRequestAddTile(in String packageName); + + /** Notifies System UI about an update to the media tap-to-transfer sender state. */ + void updateMediaTapToTransferSenderDisplay( + int displayState, + in MediaRoute2Info routeInfo, + in IUndoMediaTransferCallback undoCallback); + + /** Notifies System UI about an update to the media tap-to-transfer receiver state. */ + void updateMediaTapToTransferReceiverDisplay( + int displayState, + in MediaRoute2Info routeInfo); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index f28325e3cc51b..0c45e5b3eab4b 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -24,6 +24,7 @@ import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsHbmListener; +import android.media.MediaRoute2Info; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; @@ -33,6 +34,7 @@ import com.android.internal.logging.InstanceId; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.ISessionListener; import com.android.internal.statusbar.IStatusBar; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; @@ -196,4 +198,15 @@ interface IStatusBarService */ void onSessionStarted(int sessionType, in InstanceId instanceId); void onSessionEnded(int sessionType, in InstanceId instanceId); + + /** Notifies System UI about an update to the media tap-to-transfer sender state. */ + void updateMediaTapToTransferSenderDisplay( + int displayState, + in MediaRoute2Info routeInfo, + in IUndoMediaTransferCallback undoCallback); + + /** Notifies System UI about an update to the media tap-to-transfer receiver state. */ + void updateMediaTapToTransferReceiverDisplay( + int displayState, + in MediaRoute2Info routeInfo); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl similarity index 69% rename from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl rename to core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl index b47be8736d233..3dd29807be012 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl +++ b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.android.systemui.shared.mediattt; +package com.android.internal.statusbar; /** - * An interface that will be invoked by System UI if the user choose to undo a transfer. - * - * Other services will implement this interface and System UI will invoke it. + * An interface that will be invoked if the user chooses to undo a transfer. */ -interface IUndoTransferCallback { +interface IUndoMediaTransferCallback { /** - * Invoked by SystemUI when the user requests to undo the media transfer that just occurred. + * Invoked to notify callers that the user has chosen to undo the media transfer that just + * occurred. * * Implementors of this method are repsonsible for actually undoing the transfer. */ diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index f83431b58c625..776a5117cb2ec 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -873,12 +873,6 @@ android:singleUser="true" android:permission="android.permission.BIND_DREAM_SERVICE" /> - - - - { - override fun createFromParcel(parcel: Parcel) = DeviceInfo(parcel) - override fun newArray(size: Int) = arrayOfNulls(size) - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl deleted file mode 100644 index eb1c9d058e208..0000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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; -import com.android.systemui.shared.mediattt.DeviceInfo; -import com.android.systemui.shared.mediattt.IUndoTransferCallback; - -/** - * An 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 IDeviceSenderService { - /** - * 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. - */ - oneway void closeToReceiverToStartCast( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that this device (the sender) is close to a receiver device, so - * the user can potentially *end* a cast on the receiver device if the user moves this 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 *ending* a cast. It should be used when media is currently being - * played on the receiver device and the media should be transferred to play locally - * instead. - */ - oneway void closeToReceiverToEndCast( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver - * device has been started. - * - * Important notes: - * - This callback is for *starting* a cast. It should be used when this device is currently - * playing media locally and the media has started being transferred to the receiver device - * instead. - */ - oneway void transferToReceiverTriggered( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that a media transfer from the receiver and back to this device - * (the sender) has been started. - * - * Important notes: - * - This callback is for *ending* a cast. It should be used when media is currently being - * played on the receiver device and the media has started being transferred to play locally - * instead. - */ - oneway void transferToThisDeviceTriggered( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver - * device has finished successfully. - * - * Important notes: - * - This callback is for *starting* a cast. It should be used when this device had previously - * been playing media locally and the media has successfully been transferred to the - * receiver device instead. - * - * @param undoCallback will be invoked if the user chooses to undo this transfer. - */ - oneway void transferToReceiverSucceeded( - in MediaRoute2Info mediaInfo, - in DeviceInfo otherDeviceInfo, - in IUndoTransferCallback undoCallback); - - /** - * Invoke to notify System UI that a media transfer from the receiver and back to this device - * (the sender) has finished successfully. - * - * Important notes: - * - This callback is for *ending* a cast. It should be used when media was previously being - * played on the receiver device and has been successfully transferred to play locally on - * this device instead. - * - * @param undoCallback will be invoked if the user chooses to undo this transfer. - */ - oneway void transferToThisDeviceSucceeded( - in MediaRoute2Info mediaInfo, - in DeviceInfo otherDeviceInfo, - in IUndoTransferCallback undoCallback); - - /** - * Invoke to notify System UI that the attempted transfer has failed. - * - * This callback will be used for both the transfer that should've *started* playing the media - * on the receiver and the transfer that should've *ended* the playing on the receiver. - */ - oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that this device is no longer close to the receiver device. - */ - oneway void noLongerCloseToReceiver( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); -} 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 29938a05a3275..86845091d99cf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -31,7 +31,7 @@ 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.media.taptotransfer.sender.MediaTttSenderService; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.commandline.CommandRegistry; import java.util.Optional; @@ -100,11 +100,12 @@ public interface MediaModule { static Optional providesMediaTttChipControllerSender( MediaTttFlags mediaTttFlags, Context context, - WindowManager windowManager) { + WindowManager windowManager, + CommandQueue commandQueue) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipControllerSender(context, windowManager)); + return Optional.of(new MediaTttChipControllerSender(context, windowManager, commandQueue)); } /** */ @@ -138,12 +139,6 @@ public interface MediaModule { mediaTttChipControllerReceiver)); } - /** Inject into MediaTttSenderService. */ - @Binds - @IntoMap - @ClassKey(MediaTttSenderService.class) - Service bindMediaTttSenderService(MediaTttSenderService service); - /** Inject into NearbyMediaDevicesService. */ @Binds @IntoMap 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 37208515120a9..bbcbfba5bbe71 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -16,21 +16,16 @@ package com.android.systemui.media.taptotransfer -import android.content.ComponentName +import android.app.StatusBarManager 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 import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver -import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast import com.android.systemui.media.taptotransfer.sender.TransferFailed @@ -38,9 +33,6 @@ import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTrigger import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded -import com.android.systemui.shared.mediattt.DeviceInfo -import com.android.systemui.shared.mediattt.IDeviceSenderService -import com.android.systemui.shared.mediattt.IUndoTransferCallback import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter @@ -56,10 +48,7 @@ class MediaTttCommandLineHelper @Inject constructor( private val context: Context, private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver, ) { - private var senderService: IDeviceSenderService? = null - private val senderServiceConnection = SenderServiceConnection() - - private val appIconDrawable = + private val appIconDrawable = Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also { it.setTint(Color.YELLOW) } @@ -75,115 +64,24 @@ class MediaTttCommandLineHelper @Inject constructor( /** All commands for the sender device. */ inner class SenderCommand : Command { override fun execute(pw: PrintWriter, args: List) { - val otherDeviceName = args[0] - val mediaInfo = MediaRoute2Info.Builder("id", "Test Name") - .addFeature("feature") - .build() - val otherDeviceInfo = DeviceInfo(otherDeviceName) + val routeInfo = MediaRoute2Info.Builder("id", args[0]) + .addFeature("feature") + .build() - when (args[1]) { - MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> { - runOnService { senderService -> - senderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo) - } - } - MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> { - runOnService { senderService -> - senderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo) - } - } - TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME -> { - runOnService { senderService -> - senderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo) - } - } - TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME -> { - runOnService { senderService -> - senderService.transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo) - } - } - TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME -> { - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() { - Log.i(TAG, "Undo transfer to receiver callback triggered") - // The external services that implement this callback would kick off a - // transfer back to this device, so mimic that here. - runOnService { senderService -> - senderService - .transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo) - } - } - } - runOnService { senderService -> - senderService - .transferToReceiverSucceeded(mediaInfo, otherDeviceInfo, undoCallback) - } - } - TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME -> { - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() { - Log.i(TAG, "Undo transfer to this device callback triggered") - // The external services that implement this callback would kick off a - // transfer back to the receiver, so mimic that here. - runOnService { senderService -> - senderService - .transferToReceiverTriggered(mediaInfo, otherDeviceInfo) - } - } - } - runOnService { senderService -> - senderService - .transferToThisDeviceSucceeded(mediaInfo, otherDeviceInfo, undoCallback) - } - } - TRANSFER_FAILED_COMMAND_NAME -> { - runOnService { senderService -> - senderService.transferFailed(mediaInfo, otherDeviceInfo) - } - } - NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME -> { - runOnService { senderService -> - senderService.noLongerCloseToReceiver(mediaInfo, otherDeviceInfo) - context.unbindService(senderServiceConnection) - } - } - else -> { - pw.println("Sender command must be one of " + - "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " + - "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " + - "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " + - "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " + - "$TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME, " + - "$TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME, " + - "$TRANSFER_FAILED_COMMAND_NAME, " + - NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME - ) - } - } + val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) + as StatusBarManager + statusBarManager.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, + routeInfo, + /* undoExecutor= */ null, + /* undoCallback= */ null + ) + // TODO(b/216318437): Migrate the rest of the callbacks to StatusBarManager. } override fun help(pw: PrintWriter) { pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND ") } - - private fun runOnService(command: SenderServiceCommand) { - val currentService = senderService - if (currentService != null) { - command.run(currentService) - } else { - bindService(command) - } - } - - private fun bindService(command: SenderServiceCommand) { - 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 DISPLAY the media ttt chip on the RECEIVER device. */ @@ -207,29 +105,6 @@ class MediaTttCommandLineHelper @Inject constructor( pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG") } } - - /** A service connection for [IDeviceSenderService]. */ - private inner class SenderServiceConnection : ServiceConnection { - // A command that should be run when the service gets connected. - var pendingCommand: SenderServiceCommand? = null - - override fun onServiceConnected(className: ComponentName, service: IBinder) { - val newCallback = IDeviceSenderService.Stub.asInterface(service) - senderService = newCallback - pendingCommand?.run(newCallback) - pendingCommand = null - } - - override fun onServiceDisconnected(className: ComponentName) { - senderService = null - } - } - - /** An interface defining a command that should be run on the sender service. */ - private fun interface SenderServiceCommand { - /** Runs the command on the provided [senderService]. */ - fun run(senderService: IDeviceSenderService) - } } @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 c656df2e0a356..118a04ccdcd0e 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 @@ -19,9 +19,9 @@ package com.android.systemui.media.taptotransfer.sender import android.content.Context import android.graphics.drawable.Drawable import android.view.View +import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R import com.android.systemui.media.taptotransfer.common.MediaTttChipState -import com.android.systemui.shared.mediattt.IUndoTransferCallback /** * A class that stores all the information necessary to display the media tap-to-transfer chip on @@ -130,7 +130,7 @@ class TransferToReceiverSucceeded( appIconDrawable: Drawable, appIconContentDescription: String, private val otherDeviceName: String, - val undoCallback: IUndoTransferCallback? = null + val undoCallback: IUndoMediaTransferCallback? = null ) : ChipStateSender(appIconDrawable, appIconContentDescription) { override fun getChipTextString(context: Context): String { return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName) @@ -169,7 +169,7 @@ class TransferToThisDeviceSucceeded( appIconDrawable: Drawable, appIconContentDescription: String, private val otherDeviceName: String, - val undoCallback: IUndoTransferCallback? = null + val undoCallback: IUndoMediaTransferCallback? = null ) : ChipStateSender(appIconDrawable, appIconContentDescription) { override fun getChipTextString(context: Context): String { return context.getString(R.string.media_transfer_playing_this_device) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt index 453e3d627bc8b..c510e358e4a5e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt @@ -16,14 +16,20 @@ package com.android.systemui.media.taptotransfer.sender +import android.app.StatusBarManager import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Icon +import android.media.MediaRoute2Info import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.TextView +import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon +import com.android.systemui.statusbar.CommandQueue import javax.inject.Inject /** @@ -34,9 +40,36 @@ import javax.inject.Inject class MediaTttChipControllerSender @Inject constructor( context: Context, windowManager: WindowManager, + private val commandQueue: CommandQueue ) : MediaTttChipControllerCommon( context, windowManager, R.layout.media_ttt_chip ) { + // TODO(b/216141276): Use app icon from media route info instead of this fake one. + private val fakeAppIconDrawable = + Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also { + it.setTint(Color.YELLOW) + } + + private val commandQueueCallback = object : CommandQueue.Callbacks { + override fun updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState displayState: Int, + routeInfo: MediaRoute2Info, + undoCallback: IUndoMediaTransferCallback? + ) { + // TODO(b/216318437): Trigger displayChip with the right state based on displayState. + displayChip( + MoveCloserToStartCast( + // TODO(b/217418566): This app icon content description is incorrect -- + // routeInfo.name is the name of the device, not the name of the app. + fakeAppIconDrawable, routeInfo.name.toString(), routeInfo.name.toString() + ) + ) + } + } + + init { + commandQueue.addCallback(commandQueueCallback) + } /** Displays the chip view for the given state. */ override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) { 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 deleted file mode 100644 index 717752e536b0e..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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.DeviceInfo -import com.android.systemui.shared.mediattt.IUndoTransferCallback -import com.android.systemui.shared.mediattt.IDeviceSenderService -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 : IDeviceSenderService.Stub() { - override fun closeToReceiverToStartCast( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo) - } - - override fun closeToReceiverToEndCast( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo) - } - - override fun transferFailed( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.transferFailed(mediaInfo) - } - - override fun transferToReceiverTriggered( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo) - } - - override fun transferToThisDeviceTriggered( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.transferToThisDeviceTriggered(mediaInfo) - } - - override fun transferToReceiverSucceeded( - mediaInfo: MediaRoute2Info, - otherDeviceInfo: DeviceInfo, - undoCallback: IUndoTransferCallback - ) { - this@MediaTttSenderService.transferToReceiverSucceeded( - mediaInfo, otherDeviceInfo, undoCallback - ) - } - - override fun transferToThisDeviceSucceeded( - mediaInfo: MediaRoute2Info, - otherDeviceInfo: DeviceInfo, - undoCallback: IUndoTransferCallback - ) { - this@MediaTttSenderService.transferToThisDeviceSucceeded( - mediaInfo, otherDeviceInfo, undoCallback - ) - } - - override fun noLongerCloseToReceiver( - mediaInfo: MediaRoute2Info, - otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.noLongerCloseToReceiver() - } - } - - // 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, otherDeviceInfo: DeviceInfo - ) { - val chipState = MoveCloserToStartCast( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name - ) - controller.displayChip(chipState) - } - - private fun closeToReceiverToEndCast(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) { - val chipState = MoveCloserToEndCast( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name - ) - controller.displayChip(chipState) - } - - private fun transferFailed(mediaInfo: MediaRoute2Info) { - val chipState = TransferFailed( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString() - ) - controller.displayChip(chipState) - } - - private fun transferToReceiverTriggered( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - val chipState = TransferToReceiverTriggered( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name - ) - controller.displayChip(chipState) - } - - private fun transferToThisDeviceTriggered(mediaInfo: MediaRoute2Info) { - val chipState = TransferToThisDeviceTriggered( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString() - ) - controller.displayChip(chipState) - } - - private fun transferToReceiverSucceeded( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback - ) { - val chipState = TransferToReceiverSucceeded( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name, - undoCallback = undoCallback - ) - controller.displayChip(chipState) - } - - private fun transferToThisDeviceSucceeded( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback - ) { - val chipState = TransferToThisDeviceSucceeded( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name, - undoCallback = undoCallback - ) - controller.displayChip(chipState) - } - - private fun noLongerCloseToReceiver() { - controller.removeChip() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 9d43d303b834a..2f5eaa6212831 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -43,6 +43,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IUdfpsHbmListener; import android.inputmethodservice.InputMethodService.BackDispositionMode; +import android.media.MediaRoute2Info; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -63,6 +64,7 @@ import androidx.annotation.NonNull; import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.IStatusBar; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.GcUtils; import com.android.internal.view.AppearanceRegion; @@ -156,6 +158,8 @@ public class CommandQueue extends IStatusBar.Stub implements private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT; private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT; private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT; + private static final int MSG_MEDIA_TRANSFER_SENDER_STATE = 64 << MSG_SHIFT; + private static final int MSG_MEDIA_TRANSFER_RECEIVER_STATE = 65 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -439,6 +443,17 @@ public class CommandQueue extends IStatusBar.Stub implements * @see IStatusBar#cancelRequestAddTile */ default void cancelRequestAddTile(@NonNull String packageName) {} + + /** @see IStatusBar#updateMediaTapToTransferSenderDisplay */ + default void updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState int displayState, + @NonNull MediaRoute2Info routeInfo, + @Nullable IUndoMediaTransferCallback undoCallback) {} + + /** @see IStatusBar#updateMediaTapToTransferReceiverDisplay */ + default void updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState int displayState, + @NonNull MediaRoute2Info routeInfo) {} } public CommandQueue(Context context) { @@ -1177,6 +1192,29 @@ public class CommandQueue extends IStatusBar.Stub implements mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_CANCEL, s).sendToTarget(); } + @Override + public void updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState int displayState, + MediaRoute2Info routeInfo, + IUndoMediaTransferCallback undoCallback + ) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = displayState; + args.arg2 = routeInfo; + args.arg3 = undoCallback; + mHandler.obtainMessage(MSG_MEDIA_TRANSFER_SENDER_STATE, args).sendToTarget(); + } + + @Override + public void updateMediaTapToTransferReceiverDisplay( + int displayState, + MediaRoute2Info routeInfo) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = displayState; + args.arg2 = routeInfo; + mHandler.obtainMessage(MSG_MEDIA_TRANSFER_RECEIVER_STATE, args).sendToTarget(); + } + private final class H extends Handler { private H(Looper l) { super(l); @@ -1574,6 +1612,29 @@ public class CommandQueue extends IStatusBar.Stub implements for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).cancelRequestAddTile(packageName); } + break; + case MSG_MEDIA_TRANSFER_SENDER_STATE: + args = (SomeArgs) msg.obj; + int displayState = (int) args.arg1; + MediaRoute2Info routeInfo = (MediaRoute2Info) args.arg2; + IUndoMediaTransferCallback undoCallback = + (IUndoMediaTransferCallback) args.arg3; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).updateMediaTapToTransferSenderDisplay( + displayState, routeInfo, undoCallback); + } + args.recycle(); + break; + case MSG_MEDIA_TRANSFER_RECEIVER_STATE: + args = (SomeArgs) msg.obj; + int receiverDisplayState = (int) args.arg1; + MediaRoute2Info receiverRouteInfo = (MediaRoute2Info) args.arg2; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).updateMediaTapToTransferReceiverDisplay( + receiverDisplayState, receiverRouteInfo); + } + args.recycle(); + break; } } } 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 81ae2093c830d..5f800eb80ec21 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 @@ -21,9 +21,6 @@ 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.MediaTttSenderService -import com.android.systemui.shared.mediattt.DeviceInfo -import com.android.systemui.shared.mediattt.IDeviceSenderService import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.mockito.any @@ -54,19 +51,10 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { @Mock private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver - @Mock - private lateinit var mediaSenderService: IDeviceSenderService.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, @@ -100,6 +88,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { ) { EmptyCommand() } } + /* TODO(b/216318437): Revive these tests using the new SystemApis. @Test fun sender_moveCloserToStartCast_serviceCallbackCalled() { commandRegistry.onShellCommand(pw, getMoveCloserToStartCastCommand()) @@ -182,6 +171,8 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { verify(mediaSenderService).noLongerCloseToReceiver(any(), any()) } + */ + @Test fun receiver_addCommand_chipAdded() { commandRegistry.onShellCommand(pw, arrayOf(ADD_CHIP_COMMAND_RECEIVER_TAG)) 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 6b4eebe680f9a..58f4818b3de58 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 @@ -24,9 +24,10 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.test.filters.SmallTest +import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.shared.mediattt.IUndoTransferCallback +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -46,12 +47,14 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager + @Mock + private lateinit var commandQueue: CommandQueue @Before fun setUp() { MockitoAnnotations.initMocks(this) appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) - controllerSender = MediaTttChipControllerSender(context, windowManager) + controllerSender = MediaTttChipControllerSender(context, windowManager, commandQueue) } @Test @@ -133,7 +136,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_withUndoRunnable_undoWithClick() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToReceiverSucceeded(undoCallback)) @@ -146,7 +149,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() { var undoCallbackCalled = false - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() { undoCallbackCalled = true } @@ -160,7 +163,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToReceiverSucceeded(undoCallback)) @@ -194,7 +197,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_withUndoRunnable_undoWithClick() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback)) @@ -207,7 +210,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() { var undoCallbackCalled = false - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() { undoCallbackCalled = true } @@ -221,7 +224,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_undoButtonClick_switchesToTransferToReceiverTriggered() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback)) @@ -267,7 +270,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(transferToReceiverTriggered()) controllerSender.displayChip( transferToReceiverSucceeded( - object : IUndoTransferCallback.Stub() { + object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } ) @@ -327,13 +330,13 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { TransferToThisDeviceTriggered(appIconDrawable, APP_ICON_CONTENT_DESC) /** Helper method providing default parameters to not clutter up the tests. */ - private fun transferToReceiverSucceeded(undoCallback: IUndoTransferCallback? = null) = + private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = TransferToReceiverSucceeded( appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback ) /** Helper method providing default parameters to not clutter up the tests. */ - private fun transferToThisDeviceSucceeded(undoCallback: IUndoTransferCallback? = null) = + private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = TransferToThisDeviceSucceeded( appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback ) 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 deleted file mode 100644 index 64542cb2f647a..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt +++ /dev/null @@ -1,126 +0,0 @@ -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.DeviceInfo -import com.android.systemui.shared.mediattt.IDeviceSenderService -import com.android.systemui.shared.mediattt.IUndoTransferCallback -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.capture -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Ignore -import org.junit.Test -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@SmallTest -@Ignore("b/216286227") -class MediaTttSenderServiceTest : SysuiTestCase() { - - private lateinit var service: IDeviceSenderService - - @Mock - private lateinit var controller: MediaTttChipControllerSender - - private val mediaInfo = MediaRoute2Info.Builder("id", "Test Name") - .addFeature("feature") - .build() - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - val mediaTttSenderService = MediaTttSenderService(context, controller) - service = IDeviceSenderService.Stub.asInterface(mediaTttSenderService.onBind(null)) - } - - @Test - fun closeToReceiverToStartCast_controllerTriggeredWithCorrectState() { - val name = "Fake name" - service.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name)) - - val chipStateCaptor = argumentCaptor() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - } - - @Test - fun closeToReceiverToEndCast_controllerTriggeredWithCorrectState() { - val name = "Fake name" - service.closeToReceiverToEndCast(mediaInfo, DeviceInfo(name)) - - val chipStateCaptor = argumentCaptor() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - } - - @Test - fun transferToThisDeviceTriggered_controllerTriggeredWithCorrectState() { - service.transferToThisDeviceTriggered(mediaInfo, DeviceInfo("Fake name")) - - verify(controller).displayChip(any()) - } - - @Test - fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() { - val name = "Fake name" - service.transferToReceiverTriggered(mediaInfo, DeviceInfo(name)) - - val chipStateCaptor = argumentCaptor() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - } - - @Test - fun transferToReceiverSucceeded_controllerTriggeredWithCorrectState() { - val name = "Fake name" - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() {} - } - service.transferToReceiverSucceeded(mediaInfo, DeviceInfo(name), undoCallback) - - val chipStateCaptor = argumentCaptor() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - assertThat(chipState.undoCallback).isEqualTo(undoCallback) - } - - @Test - fun transferToThisDeviceSucceeded_controllerTriggeredWithCorrectState() { - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() {} - } - service.transferToThisDeviceSucceeded(mediaInfo, DeviceInfo("name"), undoCallback) - - val chipStateCaptor = argumentCaptor() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.undoCallback).isEqualTo(undoCallback) - } - - @Test - fun transferFailed_controllerTriggeredWithTransferFailedState() { - service.transferFailed(mediaInfo, DeviceInfo("Fake name")) - - verify(controller).displayChip(any()) - } - - @Test - fun noLongerCloseToReceiver_controllerRemoveChipTriggered() { - service.noLongerCloseToReceiver(mediaInfo, DeviceInfo("Fake name")) - - verify(controller).removeChip() - } -} diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 8a87c96fcaaa4..94f483c65eb53 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -53,6 +53,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.fingerprint.IUdfpsHbmListener; +import android.media.MediaRoute2Info; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -92,6 +93,7 @@ import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.ISessionListener; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.internal.statusbar.StatusBarIcon; @@ -1265,6 +1267,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D "StatusBarManagerService"); } + private void enforceMediaContentControl() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, + "StatusBarManagerService"); + } + /** * For targetSdk S+ we require STATUS_BAR. For targetSdk < S, we only require EXPAND_STATUS_BAR * but also require that it falls into one of the allowed use-cases to lock down abuse vector. @@ -1987,6 +1995,53 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D return false; } + /** + * Notifies the system of a new media tap-to-transfer state for the *sender* device. See + * {@link StatusBarManager.updateMediaTapToTransferSenderDisplay} for more information. + * + * @param undoCallback a callback that will be triggered if the user elects to undo a media + * transfer. + * + * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL} + * permission. + */ + @Override + public void updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState int displayState, + @NonNull MediaRoute2Info routeInfo, + @Nullable IUndoMediaTransferCallback undoCallback + ) { + enforceMediaContentControl(); + if (mBar != null) { + try { + mBar.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, undoCallback); + } catch (RemoteException e) { + Slog.e(TAG, "updateMediaTapToTransferSenderDisplay", e); + } + } + } + + /** + * Notifies the system of a new media tap-to-transfer state for the *receiver* device. See + * {@link StatusBarManager.updateMediaTapToTransferReceiverDisplay} for more information. + * + * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL} + * permission. + */ + @Override + public void updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState int displayState, + MediaRoute2Info routeInfo) { + enforceMediaContentControl(); + if (mBar != null) { + try { + mBar.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo); + } catch (RemoteException e) { + Slog.e(TAG, "updateMediaTapToTransferReceiverDisplay", e); + } + } + } + /** @hide */ public void passThroughShellCommand(String[] args, FileDescriptor fd) { enforceStatusBarOrShell();