Android: кнопки действий NotificationCompat.MediaStyle ничего не делают - PullRequest
0 голосов
/ 31 марта 2020

У меня есть простое Android приложение, содержащее одно Activity и Service, полученное из MediaBrowserServiceCompat. Я успешно настроил его на воспроизведение аудио из моей основной деятельности, используя MediaBrowserCompat и MediaControllerCompat. Он может даже воспроизводить и приостанавливать звук из моих наушников Bluetooth. Все хорошо.

Моя проблема связана с уведомлением NotificationCompat.MediaStyle, которое появляется на экране блокировки и в области уведомлений. Уведомление отображается правильно. Однако, когда я добавляю кнопки, используя addAction() и MediaButtonReceiver.buildMediaButtonPendingIntent, они ничего не делают. Если я вместо этого добавлю фиктивный PendingIntent, который просто запускает мою основную деятельность, он работает нормально.

Вот мой код для генерации уведомления (извинения, это C# работает в Xamarin, поэтому регистр и имена будут немного отличается от того, что вы могли бы ожидать). Это внутри моего класса обслуживания.

var builder = new NotificationCompat.Builder(this, CHANNEL_ID)
    .SetVisibility(NotificationCompat.VisibilityPublic)
    .SetSmallIcon(Resource.Drawable.ic_launcher)
    .SetContentTitle("Title")
    .SetContentText("Content")
    .SetSubText("Subtext")
    .SetLargeIcon(icon)
    .SetColor(Android.Graphics.Color.DarkOrange)

    .SetContentIntent(intent)
    .SetDeleteIntent(MediaButtonReceiver.BuildMediaButtonPendingIntent(this, PlaybackStateCompat.ActionStop))

    .AddAction(new NotificationCompat.Action(
        Resource.Drawable.ic_pause, "Pause",
        MediaButtonReceiver.BuildMediaButtonPendingIntent(this, PlaybackStateCompat.ActionPause)))

    .SetStyle(new Android.Support.V4.Media.App.NotificationCompat.MediaStyle()
        .SetShowActionsInCompactView(0)
        .SetMediaSession(this.mediaSession.SessionToken)
        .SetShowCancelButton(true)
        .SetCancelButtonIntent(MediaButtonReceiver.BuildMediaButtonPendingIntent(this, PlaybackStateCompat.ActionStop))
    );

this.StartForeground(NOTIFICATION_ID, builder.Build());

Вот что я уже рассмотрел, чтобы попытаться решить эту проблему:

  • Когда я начинаю воспроизведение, я использую MediaSession.setActive(true)
  • Каждый раз, когда я запускаю и останавливаю воспроизведение, я устанавливаю соответствующие действия в PlaybackStateCompat
  • У меня правильно установлен токен сеанса.
  • У меня нет в моем манифесте есть что-то, настроенное как MediaButtonReceiver, и я не настроил ничего для обработки android.intent.action.MEDIA_BUTTON, потому что я нацеливаюсь на Android 5.0 и выше и использую классы *Compat, и я понимаю, что это больше не требуется.

Я знаю, что события мультимедийных кнопок правильно перенаправляются в мое приложение, поскольку мои кнопки наушников Bluetooth работают. Я попробовал это в своей машине, и там тоже работает. Это просто кнопки в уведомлении, которые не будут работать. Я ожидаю, что они будут генерировать вызовы для соответствующих методов MediaSessionCompat.Callback. Это неверно? Что я здесь не так делаю?

Буду признателен за любые указатели.


ОБНОВЛЕНИЕ: Я все заработал. Мне нужно было добавить следующее внутри <application> узла манифеста:

<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

... и следующее внутри узла Сервиса, который реализует MediaBrowserServiceCompat:

<intent-filter>
    <action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>

Я все еще немного растерялся из-за , почему это было необходимо, поскольку нажатия кнопок от моих наушников Bluetooth и автомобильной информационно-развлекательной системы были перенаправлены в приложение просто отлично. Что еще более важно, Google говорит :

Если у вас уже есть MediaBrowserServiceCompat в вашем приложении, MediaButtonReceiver будет доставлять полученные ключевые события в MediaBrowserServiceCompat по умолчанию. Вы можете обрабатывать их в своих MediaSessionCompat.Callback.

Они дали это как альтернативу опции "Обработка сервисов ACTION_MEDIA_BUTTON", поэтому я понял, что это не так нужно сделать что-нибудь еще с моим манифестом. Если бы кто-нибудь мог просветить меня здесь, я был бы признателен за это.

Но, несмотря на это, это сработало для меня.

1 Ответ

0 голосов
/ 01 апреля 2020

Возможно, вы не настроили действия. Посмотрите код ниже, он показывает, как связать кнопки с намерением. Пожалуйста, измените его для устройств android O, для которых требуется канал.

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.support.v7.app.NotificationCompat;

/**
 * Keeps track of a notification and updates it automatically for a given MediaSession. This is
 * required so that the music service don't get killed during playback.
 */
public class MediaNotificationManager extends BroadcastReceiver {
    private static final int NOTIFICATION_ID = 412;
    private static final int REQUEST_CODE = 100;

    private static final String ACTION_PAUSE = "com.example.android.musicplayercodelab.pause";
    private static final String ACTION_PLAY = "com.example.android.musicplayercodelab.play";
    private static final String ACTION_NEXT = "com.example.android.musicplayercodelab.next";
    private static final String ACTION_PREV = "com.example.android.musicplayercodelab.prev";

    private final MusicService mService;

    private final NotificationManager mNotificationManager;

    private final NotificationCompat.Action mPlayAction;
    private final NotificationCompat.Action mPauseAction;
    private final NotificationCompat.Action mNextAction;
    private final NotificationCompat.Action mPrevAction;

    private boolean mStarted;

    public MediaNotificationManager(MusicService service) {
        mService = service;

        String pkg = mService.getPackageName();
        PendingIntent playIntent =
                PendingIntent.getBroadcast(
                        mService,
                        REQUEST_CODE,
                        new Intent(ACTION_PLAY).setPackage(pkg),
                        PendingIntent.FLAG_CANCEL_CURRENT);
        PendingIntent pauseIntent =
                PendingIntent.getBroadcast(
                        mService,
                        REQUEST_CODE,
                        new Intent(ACTION_PAUSE).setPackage(pkg),
                        PendingIntent.FLAG_CANCEL_CURRENT);
        PendingIntent nextIntent =
                PendingIntent.getBroadcast(
                        mService,
                        REQUEST_CODE,
                        new Intent(ACTION_NEXT).setPackage(pkg),
                        PendingIntent.FLAG_CANCEL_CURRENT);
        PendingIntent prevIntent =
                PendingIntent.getBroadcast(
                        mService,
                        REQUEST_CODE,
                        new Intent(ACTION_PREV).setPackage(pkg),
                        PendingIntent.FLAG_CANCEL_CURRENT);

        mPlayAction =
                new NotificationCompat.Action(
                        R.drawable.ic_play_arrow_white_24dp,
                        mService.getString(R.string.label_play),
                        playIntent);
        mPauseAction =
                new NotificationCompat.Action(
                        R.drawable.ic_pause_white_24dp,
                        mService.getString(R.string.label_pause),
                        pauseIntent);
        mNextAction =
                new NotificationCompat.Action(
                        R.drawable.ic_skip_next_white_24dp,
                        mService.getString(R.string.label_next),
                        nextIntent);
        mPrevAction =
                new NotificationCompat.Action(
                        R.drawable.ic_skip_previous_white_24dp,
                        mService.getString(R.string.label_previous),
                        prevIntent);

        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_NEXT);
        filter.addAction(ACTION_PAUSE);
        filter.addAction(ACTION_PLAY);
        filter.addAction(ACTION_PREV);

        mService.registerReceiver(this, filter);

        mNotificationManager =
                (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);

        // Cancel all notifications to handle the case where the Service was killed and
        // restarted by the system.
        mNotificationManager.cancelAll();
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        switch (action) {
            case ACTION_PAUSE:
                mService.mCallback.onPause();
                break;
            case ACTION_PLAY:
                mService.mCallback.onPlay();
                break;
            case ACTION_NEXT:
                mService.mCallback.onSkipToNext();
                break;
            case ACTION_PREV:
                mService.mCallback.onSkipToPrevious();
                break;
        }
    }

    public void update(
            MediaMetadataCompat metadata,
            PlaybackStateCompat state,
            MediaSessionCompat.Token token) {
        if (state == null
                || state.getState() == PlaybackStateCompat.STATE_STOPPED
                || state.getState() == PlaybackStateCompat.STATE_NONE) {
            mService.stopForeground(true);
            try {
                mService.unregisterReceiver(this);
            } catch (IllegalArgumentException ex) {
                // ignore receiver not registered
            }
            mService.stopSelf();
            return;
        }
        if (metadata == null) {
            return;
        }
        boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mService);
        MediaDescriptionCompat description = metadata.getDescription();

        notificationBuilder
                .setStyle(
                        new NotificationCompat.MediaStyle()
                                .setMediaSession(token)
                                .setShowActionsInCompactView(0, 1, 2))
                .setColor(
                        mService.getApplication().getResources().getColor(R.color.notification_bg))
                .setSmallIcon(R.drawable.ic_notification)
                .setVisibility(Notification.VISIBILITY_PUBLIC)
                .setContentIntent(createContentIntent())
                .setContentTitle(description.getTitle())
                .setContentText(description.getSubtitle())
                .setLargeIcon(MusicLibrary.getAlbumBitmap(mService, description.getMediaId()))
                .setOngoing(isPlaying)
                .setWhen(isPlaying ? System.currentTimeMillis() - state.getPosition() : 0)
                .setShowWhen(isPlaying)
                .setUsesChronometer(isPlaying);

        // If skip to next action is enabled
        if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
            notificationBuilder.addAction(mPrevAction);
        }

        notificationBuilder.addAction(isPlaying ? mPauseAction : mPlayAction);

        // If skip to prev action is enabled
        if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
            notificationBuilder.addAction(mNextAction);
        }

        Notification notification = notificationBuilder.build();

        if (isPlaying && !mStarted) {
            mService.startService(new Intent(mService.getApplicationContext(), MusicService.class));
            mService.startForeground(NOTIFICATION_ID, notification);
            mStarted = true;
        } else {
            if (!isPlaying) {
                mService.stopForeground(false);
                mStarted = false;
            }
            mNotificationManager.notify(NOTIFICATION_ID, notification);
        }
    }

    private PendingIntent createContentIntent() {
        Intent openUI = new Intent(mService, MusicPlayerActivity.class);
        openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        return PendingIntent.getActivity(
                mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT);
    }
}
...