[Media TTT] Add @SystemApis for all the media transfer callbacks.
This migrates the media transfer from just a service to a full-fledged @SystemApi. Some of the testing infrastructure has not yet been migrated to keep this CL slightly smaller. Bug: 216318437 Test: systemui media.taptotransfer tests *still need to be updated* Change-Id: Idf1f72de1343020668e158d74051c1dfb970cfcd
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Important notes:
|
||||
* <ul>
|
||||
* <li>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.</li>
|
||||
* <li>This state 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.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* Important notes:
|
||||
* <ul>
|
||||
* <li>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.</li>
|
||||
* <li>This state 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.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 <b>sender</b> device.
|
||||
*
|
||||
* <p>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 <b>receiver</b> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -873,12 +873,6 @@
|
||||
android:singleUser="true"
|
||||
android:permission="android.permission.BIND_DREAM_SERVICE" />
|
||||
|
||||
<!-- Service for external clients to do media transfer -->
|
||||
<!-- TODO(b/203800643): Export and guard with a permission. -->
|
||||
<service
|
||||
android:name=".media.taptotransfer.sender.MediaTttSenderService"
|
||||
/>
|
||||
|
||||
<!-- Service for external clients to notify us of nearby media devices -->
|
||||
<!-- TODO(b/216313420): Export and guard with a permission. -->
|
||||
<service
|
||||
|
||||
@@ -1,19 +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;
|
||||
|
||||
parcelable DeviceInfo;
|
||||
@@ -1,41 +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.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
/**
|
||||
* Represents a device that can send or receive media. Includes any device information necessary for
|
||||
* SysUI to display an informative chip to the user.
|
||||
*/
|
||||
class DeviceInfo(val name: String) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(parcel.readString())
|
||||
|
||||
override fun writeToParcel(dest: Parcel?, flags: Int) {
|
||||
dest?.writeString(name)
|
||||
}
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
||||
override fun toString() = "name: $name"
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<DeviceInfo> {
|
||||
override fun createFromParcel(parcel: Parcel) = DeviceInfo(parcel)
|
||||
override fun newArray(size: Int) = arrayOfNulls<DeviceInfo?>(size)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<MediaTttChipControllerSender> 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
|
||||
|
||||
@@ -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<String>) {
|
||||
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 <deviceName> <chipStatus>")
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<ChipStateSender>(
|
||||
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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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<MoveCloserToStartCast>()
|
||||
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<MoveCloserToEndCast>()
|
||||
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<TransferToThisDeviceTriggered>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() {
|
||||
val name = "Fake name"
|
||||
service.transferToReceiverTriggered(mediaInfo, DeviceInfo(name))
|
||||
|
||||
val chipStateCaptor = argumentCaptor<TransferToReceiverTriggered>()
|
||||
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<TransferToReceiverSucceeded>()
|
||||
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<TransferToThisDeviceSucceeded>()
|
||||
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<TransferFailed>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noLongerCloseToReceiver_controllerRemoveChipTriggered() {
|
||||
service.noLongerCloseToReceiver(mediaInfo, DeviceInfo("Fake name"))
|
||||
|
||||
verify(controller).removeChip()
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user