Android 11: DecoratedMediaCustomViewStyle игнорирует setCustomContentView
Мое приложение для Android используется для удаленного управления медиаплеером (Winamp на ПК). Чтобы позволить пользователю управлять удаленным проигрывателем, даже если приложение Android в настоящее время не активно, он использует MediaSession, связанный с фоновой службой и системным уведомлением. В системном уведомлении используется стиль DecoratedMediaCustomViewStyle, и я установил настраиваемое представление с помощью методов setCustomContentView() и setCustomBigContentView(). Я вызываю setPlaybackToRemote() в MediaSession с VolumeProviderCompat. Это важное различие, потому что мультимедиа воспроизводятся не на устройстве Android, а на удаленном устройстве. И нет, это не Chromecast, а что-то совсем другое (Winamp на ПК по кастомному протоколу).
Все это отлично работает в версиях Android до 11 (API 30). Но в Android 11 пользовательский вид (как "обычный", так и "большой") полностью игнорируется. Вот скриншоты "большого" уведомления, вверху на API 27 (то же, что и API 28 и 29), за которым следует API 30.
Обратите внимание, что в верхнем не упоминается дважды "Ampwifi", а справа есть кнопка "X". Эта кнопка "X" важна, поскольку она позволяет пользователю закрыть уведомление и выключить фоновую службу.
Я просмотрел документы и журналы изменений Android 11 и ничего не нашел об этом. У меня также есть более старый образ AVD из предварительной версии Android "R", где он также работал, как в API 29 и старше. Мне интересно, действительно ли это ошибка Android? Если нет, кто-нибудь еще сталкивался с этим, и у кого-нибудь есть предложения, как это обойти?
Я попытался просто не связывать MediaSession с уведомлением. Это фактически восстанавливает настраиваемый вид, но я теряю все преимущества MediaSession (интеграция Bluetooth/Google Assistant и автоматическая окраска кнопок действий с уведомлениями). Так что исправить это было бы здорово.
Вот код:
/////
final ConnectionProfile profile = mSettings.getActiveConnectionProfile();
final String profileName = profile != null ? profile.name : "";
final RemoteViews contentView = getContent(profileName);
final RemoteViews bigContentView = getBigContent(profileName);
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(ServiceBackgroundMediapPlayer.this, NotificationChannelBackgroundMediaPlayerServiceId)
.setColorized(true)
.setOngoing(true)
.setAutoCancel(false)
.setContentIntent(getDefaultIntent())
.setDeleteIntent(getDeleteIntent())
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(getBitmap())
.setContentTitle("Ampwifi")
.setContentText(profileName)
.addAction(R.drawable.ic_media_previous, null, MediaButtonReceiver.buildMediaButtonPendingIntent(ServiceBackgroundMediapPlayer.this, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS))
.addAction(R.drawable.ic_media_play, null, MediaButtonReceiver.buildMediaButtonPendingIntent(ServiceBackgroundMediapPlayer.this, PlaybackStateCompat.ACTION_PLAY))
.addAction(R.drawable.ic_media_pause, null, MediaButtonReceiver.buildMediaButtonPendingIntent(ServiceBackgroundMediapPlayer.this, PlaybackStateCompat.ACTION_PAUSE))
.addAction(R.drawable.ic_media_stop, null, MediaButtonReceiver.buildMediaButtonPendingIntent(ServiceBackgroundMediapPlayer.this, PlaybackStateCompat.ACTION_STOP))
.addAction(R.drawable.ic_media_next, null, MediaButtonReceiver.buildMediaButtonPendingIntent(ServiceBackgroundMediapPlayer.this, PlaybackStateCompat.ACTION_SKIP_TO_NEXT))
.setStyle(getStyle())
.setCustomContentView(contentView)
.setCustomBigContentView(bigContentView)
.setOnlyAlertOnce(true);
mNotification = notificationBuilder.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(R.id.ServiceBackgroundMediapPlayerNotification, mNotification);
/////
public NotificationCompat.Style getStyle() {
return new androidx.media.app.NotificationCompat.DecoratedMediaCustomViewStyle()
.setShowCancelButton(true)
.setCancelButtonIntent(getDeleteIntent())
.setShowActionsInCompactView(1, 2, 4)
.setMediaSession(getMediaSessionToken());
}
public RemoteViews getContent(String title) {
final RemoteViews layout = new RemoteViews("com.blitterhead.ampwifi", R.layout.notification_media);
if (layout != null) {
layout.setTextViewText(R.id.title, title);
}
return layout;
}
public RemoteViews getBigContent(String title) {
final RemoteViews layout = new RemoteViews("com.blitterhead.ampwifi", R.layout.notification_media_big);
if (layout != null) {
layout.setTextViewText(R.id.title, title);
layout.setOnClickPendingIntent(R.id.dismiss, getDeleteIntent());
}
return layout;
}
private MediaSessionCompat.Token getMediaSessionToken() {
if (_mediaSessionToken == null) {
PlaybackStateCompat playbackState = mPlaybackStateBuilder
.setActiveQueueItemId(1)
.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_NEXT)
.setState(PlaybackStateCompat.STATE_PLAYING, 0, 1.0f)
.build();
ComponentName mediaButtonReceiver = new ComponentName(getApplicationContext(), MediaButtonReceiver.class);
mMediaSession = new MediaSessionCompat(ServiceBackgroundMediapPlayer.this, MediaSessionTag, mediaButtonReceiver, null);
mMediaSession.setPlaybackState(playbackState);
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setMetadata(getMetaData());
mMediaSession.setCallback(getMediaSessionCallback());
mMediaSession.setActive(true);
if (mSettings.getBackgroundMediaVolume()) {
mMediaSession.setPlaybackToRemote(getVolumeProvider());
}
_mediaSessionToken = mMediaSession.getSessionToken();
setSessionToken(_mediaSessionToken);
}
return _mediaSessionToken;
}