【问题标题】:Android Notification.MediaStyle not responding to MediaSession updatesAndroid Notification.MediaStyle 未响应 MediaSession 更新
【发布时间】:2015-01-24 10:54:09
【问题描述】:

根据Notification.MediaStyle 文档,将MediaSession.Token 附加到给定的MediaStyle 应将其挂钩到关联的MediaSession 对象。

引用如下:

最后,如果您使用 setMediaSession(MediaSession.Token) 附加 MediaSession.Token,系统 UI 可以将其识别为表示活动媒体会话的通知并做出相应响应(例如,通过在锁屏中显示专辑插图)。

然后文档会给出在通知中使用 MediaStyle 的代码 sn-p,如下所示:

Notification noti = new Notification.Builder()
    .setSmallIcon(R.drawable.ic_stat_player)
    .setContentTitle("Track title")     // these three lines are optional
    .setContentText("Artist - Album")   // if you use
    .setLargeIcon(albumArtBitmap))      // setMediaSession(token)
    .setStyle(new Notification.MediaStyle()
        .setMediaSession(mySession))
    .build();

明确说明(来自可选这个词)ContentTitle/ContentText 参数在与令牌对象(可能由 MediaStyle 实例自动填充)一起使用时不是必需的。

在尝试将此 MediaStyle 实现到我当前正在构建的音乐播放器时,我注意到尽管音乐播放器在整个过程中反复更新元数据和播放状态,但使用 MediaStyle 设置样式的通知根本没有更新。唯一出现的是带有通知“小图标”的空白通知,当视图展开时,该通知的“小图标”会超出比例。

为了说明这个问题,我在下面创建了一个包含两个按钮的小型测试应用程序。一键更新通知(使用 MediaStyle 创建一个新通知并发送到通知管理器)。另一个按钮生成元数据和假播放状态来模拟播放。我注意到,无论我按什么顺序按下按钮组合,通知都不会更新为我希望它更新的样式。

以下表示代表我的测试用例的活动类。大部分是样板Activity代码,最后两个方法updateNotification()updateState()链接到两个按钮的onClick属性,作为OnClickListeners方便。

public class MediaStyleTestActivity extends Activity {

    private MediaSession mSession;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_style_test);

        mSession = new MediaSession(this, "TestSession");
        mSession.setActive(true);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        mSession.release();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_media_style_test, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void updateState(View v){
        MediaMetadata.Builder mMetaBuilder = new MediaMetadata.Builder();

        mMetaBuilder.putText(MediaMetadata.METADATA_KEY_TITLE, "Blue Jeans");
        mMetaBuilder.putText(MediaMetadata.METADATA_KEY_ALBUM, "Born to Die");
        mMetaBuilder.putText(MediaMetadata.METADATA_KEY_ARTIST, "Lana Del Rey");
        mMetaBuilder.putText(MediaMetadata.METADATA_KEY_ALBUM_ARTIST, "Lana Del Rey");
        mMetaBuilder.putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, 3);
        mMetaBuilder.putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, 15);
        mMetaBuilder.putLong(MediaMetadata.METADATA_KEY_DISC_NUMBER, 1);

        Bitmap albumArt = BitmapFactory.decodeResource(getResources(), R.drawable.album_art);

        mMetaBuilder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, albumArt);

        PlaybackState.Builder stateBuilder = new PlaybackState.Builder();

        stateBuilder.setActiveQueueItemId(MediaSession.QueueItem.UNKNOWN_ID);

        long actions = PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS;

        stateBuilder.setActions(actions);
        stateBuilder.setState(PlaybackState.STATE_PLAYING, 0, 1.0f);

        mSession.setMetadata(mMetaBuilder.build());
        mSession.setPlaybackState(stateBuilder.build());
    }

    public void updateNotification(View v){
        Notification.Builder nBuilder = new Notification.Builder(this);

        nBuilder.setOngoing(true);
        nBuilder.setShowWhen(false);
        nBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);

        nBuilder.setSmallIcon(android.R.drawable.ic_media_play);
        nBuilder.setStyle(new Notification.MediaStyle().setMediaSession(mSession.getSessionToken()));

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(100, nBuilder.build());
    }
}

请忽略此处对MediaSession的不当使用,这仅用于演示目的。

我设备上的应用程序:

我确实注意到锁屏正在更新专辑封面,但没有更多:

如前所述,无论我尝试哪种 MediaSession/MediaStyle/Notification 更新组合,似乎都不会导致通知的内容视图填充提供的元数据(包括设置一个有效的 MediaSession.QueueItem 示例中未显示)。 Logcat 根本不显示任何内容,即使在通知不顺利或失败的详细信息下也是如此。

除非我在这里遗漏了一些非常明显的东西,否则我做错了什么?


作为补充信息,我将任何兼容性库用于通知和 MediaSession。我的应用程序的这个组件仅适用于 API 21+,因此不需要向后兼容支持。

在我的生产代码中,实际的 MediaSession 保存在前台服务中,我尝试从各种线程(UI/android 使用的任何媒体线程/我自己的自定义线程)进行更新,但无济于事。

【问题讨论】:

    标签: android android-notifications


    【解决方案1】:

    你是对的。从今天起,setContentTitlesetContextTextsetLargeIcon 需要显示通知标题、文本和图像。 (正在解决的文档问题)

    旁白:另外请注意,您应该致电mediaSession.setActive(true)

    【讨论】:

    • 那么你是说官方文档不正确吗?而且我忘记在示例中将 setActive() 调用为 true,但我在生产代码中这样做无济于事。
    • 是的,文档正在修复中。 (setActive 不会改变这种行为,但需要使会话可发现,因此作为旁白提及)
    • 对不起,迟到的评论。但是,如果必须在每次播放状态/元数据更改时手动更新通知,那么将 MediaSession 令牌传递给 MediaStyle 有什么用呢?似乎每个播放的新曲目都会产生一个新的 MediaStyle 正在创建(作为通知构建过程的一部分)使得整个会话令牌被传递给 MediaStyle 相当多余。
    • 不同的媒体应用程序可能希望以不同的方式将元数据字段映射到通知标题/文本,并且大多数应用程序需要根据播放状态更新通知操作,因此无论如何它们都需要一个 MediaController.Callback。会话令牌对于系统 UI 获取通知对象上不可用的额外信息非常重要,例如锁定屏幕的高分辨率专辑封面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    • 2013-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多