Add setRemotePlaybackInfo to MediaStyle
This method is needed for some system apps to override the output switcher when MediaRouter2 can't be used. PiperOrigin-RevId: 600807119
This commit is contained in:
parent
1f78aa5b2a
commit
b64d754670
@ -15,8 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.session;
|
package androidx.media3.session;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.MEDIA_CONTENT_CONTROL;
|
||||||
import static androidx.core.app.NotificationCompat.COLOR_DEFAULT;
|
import static androidx.core.app.NotificationCompat.COLOR_DEFAULT;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@ -25,13 +29,16 @@ import android.support.v4.media.session.MediaSessionCompat;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
import androidx.annotation.DoNotInline;
|
import androidx.annotation.DoNotInline;
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.annotation.RequiresPermission;
|
||||||
import androidx.core.app.NotificationBuilderWithBuilderAccessor;
|
import androidx.core.app.NotificationBuilderWithBuilderAccessor;
|
||||||
import androidx.media3.common.util.Assertions;
|
|
||||||
import androidx.media3.common.util.NullableType;
|
import androidx.media3.common.util.NullableType;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class containing media specfic {@link androidx.core.app.NotificationCompat.Style styles} that you
|
* Class containing media specfic {@link androidx.core.app.NotificationCompat.Style styles} that you
|
||||||
@ -103,10 +110,14 @@ public class MediaStyleNotificationHelper {
|
|||||||
private static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
|
private static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
|
||||||
private static final int MAX_MEDIA_BUTTONS = 5;
|
private static final int MAX_MEDIA_BUTTONS = 5;
|
||||||
|
|
||||||
/* package */ MediaSession session;
|
/* package */ final MediaSession session;
|
||||||
/* package */ boolean showCancelButton;
|
|
||||||
|
private boolean showCancelButton;
|
||||||
/* package */ int @NullableType [] actionsToShowInCompact;
|
/* package */ int @NullableType [] actionsToShowInCompact;
|
||||||
/* package */ @Nullable PendingIntent cancelButtonIntent;
|
@Nullable /* package */ PendingIntent cancelButtonIntent;
|
||||||
|
/* package */ @MonotonicNonNull CharSequence remoteDeviceName;
|
||||||
|
/* package */ int remoteDeviceIconRes;
|
||||||
|
@Nullable /* package */ PendingIntent remoteDeviceIntent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance with a {@link MediaSession} to this Notification to provide additional
|
* Creates a new instance with a {@link MediaSession} to this Notification to provide additional
|
||||||
@ -172,9 +183,52 @@ public class MediaStyleNotificationHelper {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For media notifications associated with playback on a remote device, provide device
|
||||||
|
* information that will replace the default values for the output switcher chip on the media
|
||||||
|
* control, as well as an intent to use when the output switcher chip is tapped, on devices
|
||||||
|
* where this is supported.
|
||||||
|
*
|
||||||
|
* <p>Most apps should integrate with {@link android.media.MediaRouter2} instead. This method is
|
||||||
|
* only intended for system applications to provide information and/or functionality that would
|
||||||
|
* otherwise be unavailable to the default output switcher because the media originated on a
|
||||||
|
* remote device.
|
||||||
|
*
|
||||||
|
* <p>Also note that this method is a no-op when running on API 33 or lower.
|
||||||
|
*
|
||||||
|
* @param deviceName The name of the remote device to display.
|
||||||
|
* @param iconResource Icon resource, of size 12, representing the device.
|
||||||
|
* @param chipIntent PendingIntent to send when the output switcher is tapped. May be {@code
|
||||||
|
* null}, in which case the output switcher will be disabled. This intent should open an
|
||||||
|
* {@link android.app.Activity} or it will be ignored.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@RequiresPermission(MEDIA_CONTENT_CONTROL)
|
||||||
|
public MediaStyle setRemotePlaybackInfo(
|
||||||
|
CharSequence deviceName,
|
||||||
|
@DrawableRes int iconResource,
|
||||||
|
@Nullable PendingIntent chipIntent) {
|
||||||
|
checkArgument(deviceName != null);
|
||||||
|
this.remoteDeviceName = deviceName;
|
||||||
|
this.remoteDeviceIconRes = iconResource;
|
||||||
|
this.remoteDeviceIntent = chipIntent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(NotificationBuilderWithBuilderAccessor builder) {
|
public void apply(NotificationBuilderWithBuilderAccessor builder) {
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 34 && remoteDeviceName != null) {
|
||||||
|
Api21Impl.setMediaStyle(
|
||||||
|
builder.getBuilder(),
|
||||||
|
Api21Impl.fillInMediaStyle(
|
||||||
|
Api34Impl.setRemotePlaybackInfo(
|
||||||
|
Api21Impl.createMediaStyle(),
|
||||||
|
remoteDeviceName,
|
||||||
|
remoteDeviceIconRes,
|
||||||
|
remoteDeviceIntent),
|
||||||
|
actionsToShowInCompact,
|
||||||
|
session));
|
||||||
|
} else if (Util.SDK_INT >= 21) {
|
||||||
Api21Impl.setMediaStyle(
|
Api21Impl.setMediaStyle(
|
||||||
builder.getBuilder(),
|
builder.getBuilder(),
|
||||||
Api21Impl.fillInMediaStyle(
|
Api21Impl.fillInMediaStyle(
|
||||||
@ -191,7 +245,7 @@ public class MediaStyleNotificationHelper {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
||||||
public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
|
public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 21) {
|
||||||
// No custom content view required
|
// No custom content view required
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -265,7 +319,7 @@ public class MediaStyleNotificationHelper {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
||||||
public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
|
public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 21) {
|
||||||
// No custom content view required
|
// No custom content view required
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -352,8 +406,18 @@ public class MediaStyleNotificationHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(NotificationBuilderWithBuilderAccessor builder) {
|
public void apply(NotificationBuilderWithBuilderAccessor builder) {
|
||||||
|
if (Util.SDK_INT >= 34 && remoteDeviceName != null) {
|
||||||
if (Build.VERSION.SDK_INT >= 24) {
|
Api21Impl.setMediaStyle(
|
||||||
|
builder.getBuilder(),
|
||||||
|
Api21Impl.fillInMediaStyle(
|
||||||
|
Api34Impl.setRemotePlaybackInfo(
|
||||||
|
Api24Impl.createDecoratedMediaCustomViewStyle(),
|
||||||
|
remoteDeviceName,
|
||||||
|
remoteDeviceIconRes,
|
||||||
|
remoteDeviceIntent),
|
||||||
|
actionsToShowInCompact,
|
||||||
|
session));
|
||||||
|
} else if (Util.SDK_INT >= 24) {
|
||||||
Api21Impl.setMediaStyle(
|
Api21Impl.setMediaStyle(
|
||||||
builder.getBuilder(),
|
builder.getBuilder(),
|
||||||
Api21Impl.fillInMediaStyle(
|
Api21Impl.fillInMediaStyle(
|
||||||
@ -370,12 +434,12 @@ public class MediaStyleNotificationHelper {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
||||||
public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
|
public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
|
||||||
if (Build.VERSION.SDK_INT >= 24) {
|
if (Util.SDK_INT >= 24) {
|
||||||
// No custom content view required
|
// No custom content view required
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
boolean hasContentView = mBuilder.getContentView() != null;
|
boolean hasContentView = mBuilder.getContentView() != null;
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 21) {
|
||||||
// If we are on L/M the media notification will only be colored if the expanded
|
// If we are on L/M the media notification will only be colored if the expanded
|
||||||
// version is of media style, so we have to create a custom view for the collapsed
|
// version is of media style, so we have to create a custom view for the collapsed
|
||||||
// version as well in that case.
|
// version as well in that case.
|
||||||
@ -409,7 +473,7 @@ public class MediaStyleNotificationHelper {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
||||||
public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
|
public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
|
||||||
if (Build.VERSION.SDK_INT >= 24) {
|
if (Util.SDK_INT >= 24) {
|
||||||
// No custom big content view required
|
// No custom big content view required
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -423,7 +487,7 @@ public class MediaStyleNotificationHelper {
|
|||||||
}
|
}
|
||||||
RemoteViews bigContentView = generateBigContentView();
|
RemoteViews bigContentView = generateBigContentView();
|
||||||
buildIntoRemoteViews(bigContentView, innerView);
|
buildIntoRemoteViews(bigContentView, innerView);
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 21) {
|
||||||
setBackgroundColor(bigContentView);
|
setBackgroundColor(bigContentView);
|
||||||
}
|
}
|
||||||
return bigContentView;
|
return bigContentView;
|
||||||
@ -440,7 +504,7 @@ public class MediaStyleNotificationHelper {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
@SuppressWarnings("nullness:override.return") // NotificationCompat doesn't annotate @Nullable
|
||||||
public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {
|
public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {
|
||||||
if (Build.VERSION.SDK_INT >= 24) {
|
if (Util.SDK_INT >= 24) {
|
||||||
// No custom heads up content view required
|
// No custom heads up content view required
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -454,7 +518,7 @@ public class MediaStyleNotificationHelper {
|
|||||||
}
|
}
|
||||||
RemoteViews headsUpContentView = generateBigContentView();
|
RemoteViews headsUpContentView = generateBigContentView();
|
||||||
buildIntoRemoteViews(headsUpContentView, innerView);
|
buildIntoRemoteViews(headsUpContentView, innerView);
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 21) {
|
||||||
setBackgroundColor(headsUpContentView);
|
setBackgroundColor(headsUpContentView);
|
||||||
}
|
}
|
||||||
return headsUpContentView;
|
return headsUpContentView;
|
||||||
@ -493,8 +557,8 @@ public class MediaStyleNotificationHelper {
|
|||||||
Notification.MediaStyle style,
|
Notification.MediaStyle style,
|
||||||
@Nullable int[] actionsToShowInCompact,
|
@Nullable int[] actionsToShowInCompact,
|
||||||
MediaSession session) {
|
MediaSession session) {
|
||||||
Assertions.checkNotNull(style);
|
checkNotNull(style);
|
||||||
Assertions.checkNotNull(session);
|
checkNotNull(session);
|
||||||
if (actionsToShowInCompact != null) {
|
if (actionsToShowInCompact != null) {
|
||||||
setShowActionsInCompactView(style, actionsToShowInCompact);
|
setShowActionsInCompactView(style, actionsToShowInCompact);
|
||||||
}
|
}
|
||||||
@ -518,4 +582,23 @@ public class MediaStyleNotificationHelper {
|
|||||||
return new Notification.DecoratedMediaCustomViewStyle();
|
return new Notification.DecoratedMediaCustomViewStyle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(34)
|
||||||
|
private static class Api34Impl {
|
||||||
|
|
||||||
|
private Api34Impl() {}
|
||||||
|
|
||||||
|
// MEDIA_CONTENT_CONTROL permission is required by setRemotePlaybackInfo
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
@SuppressLint({"MissingPermission"})
|
||||||
|
@DoNotInline
|
||||||
|
public static Notification.MediaStyle setRemotePlaybackInfo(
|
||||||
|
Notification.MediaStyle style,
|
||||||
|
CharSequence remoteDeviceName,
|
||||||
|
@DrawableRes int remoteDeviceIconRes,
|
||||||
|
@Nullable PendingIntent remoteDeviceIntent) {
|
||||||
|
style.setRemotePlaybackInfo(remoteDeviceName, remoteDeviceIconRes, remoteDeviceIntent);
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user