首页 文章

通知传输控件似乎没有做任何事情

提问于
浏览
0

我创建了一个可以使用MediaBrowserServiceCompat和MediaSessionCompat播放音频的应用程序 . 根据android开发者网站上的说明,我在MediaSessionCompat.Callback() . onPlay()方法中创建了一个通知,该方法使用MediaStyle提供传输控件,当提供相应的令牌时,它应该连接到我的媒体会话 . 即使应用程序关闭并再次打开,in app也会按预期控制播放和暂停工作 . 该服务似乎正在按预期运行 .

但问题是,虽然通知按预期显示,但是包含的暂停按钮似乎无法执行任何操作 . 尽管Android开发人员示例表明应该存在取消按钮,但事实并非如此 . 此外,该示例还表明,通过滑动通知可以停止服务,但事实并非如此 .

可以这么说,以下代码段中没有任何内容正常工作 . 事实上,除了通知之外,它出现了 .

private NotificationCompat.Builder getMediaNotificationBuilder() {

        Intent contentIntent = new Intent(mContext, MainActivity.class);
        contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

        PendingIntent pendingContentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);

        MediaControllerCompat controller = mMediaSession.getController();

        NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, "PODCAST");

        builder
                .setContentTitle("PODCAST")
                .setContentText("THIS IS A PLACE HOLDER.")
                .setSubText("Still a place holder.")

                // Enable launching the player by clicking the notification
                .setContentIntent(pendingContentIntent)

                // Stop the service when the notification is swiped away
                .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext, PlaybackStateCompat.ACTION_STOP))

                // Make the transport controls visible on the lockscreen
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

                // Add an app icon and set its accent color
                // Be careful about the color
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setColor(ContextCompat.getColor(mContext, R.color.colorPrimaryDark))

                // Add a pause button
                .addAction(new NotificationCompat.Action(
                        R.drawable.ic_pause, "Pause",
                        MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
                                PlaybackStateCompat.ACTION_PAUSE)))

                // Take advantage of MediaStyle features
                .setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
                        .setMediaSession(mMediaSession.getSessionToken())
                        .setShowActionsInCompactView(0)

                        // Add a cancel button
                        .setShowCancelButton(true)
                        .setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
                                PlaybackStateCompat.ACTION_STOP)));

        return builder;
    }

然后我继续将此通知传递给

startForground(1, getMediaNotificationBuilder().build())

然后启动该服务 .

如果有必要,我将很乐意分享整个应用程序源代码 . 我相信我在这里错过了一些非常简单的事情 .

更新:

我怀疑我错过了一些非常简单的事情 . 为了让我的MediaBrowserServiceCompat子类对我的通知控件做出反应,我需要从Service基类重写onStartCommand并将Intent传递给我的MediaSessionCompat对象 . 执行此操作后,MediaSessionCompat.Callback应该处理该命令,假设它已被编程执行此操作 . 这就是我的MediaBrowserService类中的代码 .

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.e(LOG_TAG, "onStartCommand(): received intent " + intent.getAction() + " with flags " + flags + " and startId " + startId);
    MediaButtonReceiver.handleIntent(mMediaSession, intent);
    return super.onStartCommand(intent, flags, startId);
}

添加此代码后,您还应该在logcat中看到该方法 . 如果有人在那里仍然遗漏了某些东西,你至少会知道代码响应按下按钮 .

编辑:至于通过刷通知来停止服务,我误解了通知和用户之间的交互 . 用户可以刷新通知,但仅限于媒体首先被暂停 . 标准媒体播放器应用程序的通知控件进一步支持此范例 . 这是有道理的,因为用户在听取其他内容时可能会意外地滑动控件 .

此外,我已经决定为我的MediaBrowserServiceCompat类包含整个源代码,希望这些附加信息能够提供一些讨论的背景信息 .

public class MediaPlaybackService extends MediaBrowserServiceCompat {
private static final String LOG_TAG = "MediaPlaybackService";
private static final String MY_MEDIA_ROOT_ID = "media_root_id";
private static final String MY_EMPTY_MEDIA_ROOT_ID = "empty_root_id";

// Volume levels: Normal and Duck
// VOLUME_DUCK is the volume we set the media player to when we lose audio focus, but are allowed to reduce the volume instead of stopping playback.
public static final float VOLUME_DUCK = 0.2f;
public static final float VOLUME_NORMAL = 1.0f;

private MediaSessionCompat mMediaSession;
private MediaPlayer        mMediaPlayer;

// Current local media player state
private PlaybackStateCompat.Builder mStateBuilder;
private int                         mState = PlaybackStateCompat.STATE_NONE;

private final class MediaSessionCallback extends MediaSessionCompat.Callback implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, AudioManager.OnAudioFocusChangeListener{

    private Context mContext;

    private AudioManager mAudioManager;

    // Declare the "SHIT THAT'S LOUD" intent, any broadcast receiver
    // that is connected to it will trigger when the headphones come unplugged
    private IntentFilter shitThatsLoudIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
    private BroadcastReceiver shitThatsLoudBroadcastReceiver = new BroadcastReceiver() {
        // TODO: Put me in a separate class
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(LOG_TAG, "SHIT THATS LOUD! The headphones have come unplugged!");
        }
    };

    private MediaSessionCallback(Context context) {
        super();

        mContext = context;
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);

        initMediaPlayer();
    }

    private void initMediaPlayer() {
        try {
            mMediaPlayer = new MediaPlayer();

            mMediaPlayer.setDataSource("https://www.blogtalkradio.com/kylekulinski/2018/10/15/the-kyle-kulinski-show.mp3");
            mMediaPlayer.setOnPreparedListener  (this);
            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnErrorListener     (this);

            mMediaPlayer.prepare();
        } catch (IOException e) {
            Log.e(LOG_TAG, ".initMediaPlayer(): IOException: "+e.toString());
        }
    }

    private void mediaPlay() {
        registerReceiver(shitThatsLoudBroadcastReceiver, shitThatsLoudIntentFilter);
        if (mAudioManager.requestAudioFocus(getAudioFocusRequest()) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            Log.d(LOG_TAG, "Audio focus request granted.");

            mState = PlaybackStateCompat.STATE_PLAYING;

            mStateBuilder.setActions(PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP);
            mStateBuilder.setState(mState, mMediaPlayer.getCurrentPosition(), 1.0f, SystemClock.elapsedRealtime());

            mMediaSession.setPlaybackState(mStateBuilder.build());
            mMediaSession.setActive(true);

            mMediaPlayer.start();

            startService(new Intent(mContext, MediaPlaybackService.class));
            startForeground(1, getMediaNotificationBuilder().build());
        }
    }

    private void mediaPause() {

        unregisterReceiver(shitThatsLoudBroadcastReceiver);

        mState = PlaybackStateCompat.STATE_PAUSED;

        mStateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP);
        mStateBuilder.setState(mState, mMediaPlayer.getCurrentPosition(), 1.0f, SystemClock.elapsedRealtime());

        mMediaSession.setPlaybackState(mStateBuilder.build());

        mMediaPlayer.pause();

        stopForeground(false);

    }

    private void releaseResources() {

        mMediaSession.setActive(false);

        mAudioManager.abandonAudioFocusRequest(getAudioFocusRequest());

        unregisterReceiver(shitThatsLoudBroadcastReceiver);

        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.reset();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }

        stopSelf();
        stopForeground(true);
    }

    private NotificationCompat.Builder getMediaNotificationBuilder() {

        Intent contentIntent = new Intent(mContext, MainActivity.class);
        contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

        PendingIntent pendingContentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);

        MediaControllerCompat controller = mMediaSession.getController();

        NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, "PODCAST");

        builder
                .setContentTitle("PODCAST")
                .setContentText("THIS IS A PLACE HOLDER.")
                .setSubText("Still a place holder.")

                // Enable launching the player by clicking the notification
                .setContentIntent(pendingContentIntent)

                // Stop the service when the notification is swiped away
                .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext, PlaybackStateCompat.ACTION_STOP))

                // Make the transport controls visible on the lockscreen
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

                // Add an app icon and set its accent color
                // Be careful about the color
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setColor(ContextCompat.getColor(mContext, R.color.colorPrimaryDark))

                // Add a pause button
                .addAction(new NotificationCompat.Action(
                        R.drawable.ic_pause, "Pause",
                        MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
                                PlaybackStateCompat.ACTION_PLAY_PAUSE)))

                // Take advantage of MediaStyle features
                .setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
                        .setMediaSession(mMediaSession.getSessionToken())
                        .setShowActionsInCompactView(0)

                        // Add a cancel button
                        .setShowCancelButton(true)
                        .setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
                                PlaybackStateCompat.ACTION_STOP)));

        return builder;
    }

    @Override
    public void onPlay() {
        super.onPlay();
        Log.d(LOG_TAG, "I tried to play music");

        mediaPlay();
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(LOG_TAG, "I Tried to pause");

        mediaPause();
    }

    @Override
    public void onStop() {
        super.onStop();
        releaseResources();
    }

    private AudioFocusRequest getAudioFocusRequest() {
        // Request audio focus for playback, this registers the afChangeListener
        AudioAttributes attrs = new AudioAttributes.Builder()
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build();
        AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                .setOnAudioFocusChangeListener(this)
                .setAudioAttributes(attrs)
                .build();

        return audioFocusRequest;
    }

    @Override
    public void onAudioFocusChange(int focusChange) {

        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                Log.d(LOG_TAG, "Audio focus has been restored after it was transiently arrested by and intrusive app.  We can now start playing audio normally again.");
                mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL);
                mediaPlay();
                break;

            case AudioManager.AUDIOFOCUS_LOSS:
                Log.d(LOG_TAG, "Audio focus was lost flat out. Save what we were doing so we don't forget about it later.");
                mediaPause();
                break;

            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                Log.d(LOG_TAG, "Audio focus was lost (Transient) but we might get it back later, still stop and save though.");
                mediaPause();
                break;

            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                Log.d(LOG_TAG, "Audio focus was lost but was just need to keep it down instead of stopping.");
                mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK);
                break;

            default:
                Log.d(LOG_TAG, "Ignoring unsupported audio focus change: "+focusChange);
                break;

        }

    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        Log.d(LOG_TAG, "MediaSessionCallback.onPrepared(): MediaPlayer is prepared!");
        // The media player is done preparing. That means we can start playing if we
        // have audio focus.
    }

    @Override
    public void onCompletion(MediaPlayer mp) {

    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        Log.e(LOG_TAG, "Media player error: what=" + what + ", extra=" + extra);
        return false; // true indicates we handled the error
    }
}

@Override
public void onCreate() {
    super.onCreate();

    // Create a MediaSessionCompat
    mMediaSession = new MediaSessionCompat(this, LOG_TAG);
    // Set the session's token so that client activities can communicate with it.
    setSessionToken(mMediaSession.getSessionToken());
    // MediaSessionCallback() has methods that handle callbacks from a media controller
    mMediaSession.setCallback(new MediaSessionCallback(this));
    // Enable callbacks from media buttons and transport controls
    mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
            MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
    );

    // Set initial PlaybackState with ACTION_PLAY, so that media buttons start the player
    mStateBuilder = new PlaybackStateCompat.Builder()
            .setActions(
                    PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE
            );
    mMediaSession.setPlaybackState(mStateBuilder.build());

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.e(LOG_TAG, "onStartCommand(): received intent " + intent.getAction() + " with flags " + flags + " and startId " + startId);
    MediaButtonReceiver.handleIntent(mMediaSession, intent);
    return super.onStartCommand(intent, flags, startId);
}

@Nullable
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
    return new BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID, null);
}

@Override
public void onLoadChildren(@NonNull String parentMediaId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
    //  Browsing not allowed
    if (TextUtils.equals(MY_EMPTY_MEDIA_ROOT_ID, parentMediaId)) {
        result.sendResult(null);
        return;
    }

    // TODO: If in the future we decide that we do want this class to handle the podcast metadata
    // Then we must adapt what ever data podcastFactory produces into a List of MediaBrowserCompat.MediaItem objects
    // The constructor of MediaItem requires that a MediaDescription object be passed to it.
    // MediaDescription has a builder class which contains methods for setting Title, Artist, Uri, etc...

    // MediaDescription.Builder mMediaDescriptionBuilder = new MediaDescription.Builder();

    // mMediaDescriptionBuilder.setTitle(String);
    // mMediaDescriptionBuilder.setMediaUri(String);

    // MediaDescription mMediaDescription = mMediaDescriptionBuilder.build()

    // MediaBrowserCompat.MediaItem mMediaItem =
    //     new MediaBrowserCompat.MediaItem(
    //          mMediaDescription,
    //          int flags -> FLAG_BROWSABLE and/or FLAG_PLAYABLE
    // );

    // add MediaItem to SomeList

    // result.sendResult(SomeList);
}

1 回答

  • 0

    我已经回答了我自己的问题 . 详细信息包含在原始问题中 .

相关问题