Android 7 中的通知 contentView 和 bigContentView 为空

Notification contentView and bigContentView are null in Android 7

我的应用程序的一部分包含一个自定义锁屏,它需要像正常 android 锁屏一样显示通知。

一切正常,直到 Android 6,我使用了 NotificationListenerService 来检索通知 contentView 和 bigContentView (RemoteViews)。我在我的自定义 RecyclerView 适配器上使用它们来创建一个通知列表,其中包含服务列出的相同通知:

//this is called by NotificationListenerService
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
    AddNotification(sbn);
}

然后使用 StatusBarNotification 检索 contentView 和 bigContentView 并将它们应用到我的自定义回收视图列表视图中:

/**
* Add notification into recycleview
* @param sbn notification to add
*/
private void AddNotification(StatusBarNotification sbn)
{
    Notification notification = sbn.getNotification();

    if(notification==null) return;

    if(notification.bigContentView!=null) {
        //apply bigContentView to my recycleview list notification view
        myListView.notificationview = notification.bigContentView.apply(myContext(), myNotificationLayout);
    }
    else if(notification.contentView!=null) {
        //apply contentView to my recycleview list notification view
        myListView.notificationview = notification.contentView.apply(myContext(), myNotificationLayout);
    }

    //notify recycleview of a new item inserted
    notifyItemInserted(0);
}

Android 7 不再可能,因为从 Android N 开始(如 Android 文档中所述),contentView 和 bigContentView 可能为空(实际上他们是)。 这些非常有用,因为您可以复制通知视图,其中还可以包含一些复杂的操作控件(例如媒体播放器通知,例如 play/pause/stop 控件):

media player notification

是否可以在Android 7及以上版本中创建与原始通知内容相同的视图?

如何复制 RemoteView 行为?是否可以检索所有通知信息(图形、文本、图标、意图等)?

经过调查,无法检索通知的全部内容(即检索通知 Remoteviews),除非发送者使用自定义布局调用 setCustomContentView() and/or setCustomBigContentView()。

也就是说,此处唯一的解决方案是使用与原始通知相同的信息重新创建一个 Remoteview(或者两个,如果您希望同时拥有压缩版本和扩展版本)。为此,从 Bundle notification extras 中提取的信息将用于填充 XML 通知布局,您应该创建类似于以下内容的布局:

Android N notification design

此外,您必须检查 extras Bundle 是否包含 Notification.EXTRA_TEMPLATE 键,如果是,请检查其样式。 XML 布局应复制不同类型的窗框(MediaStyle、InboxStyle、BigPictureStyle)并相应地填充所有字段。

我得到了更好的解决方案:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static RemoteViews getBigContentView(Context context, Notification notification)
{
    if(notification.bigContentView != null)
        return notification.bigContentView;
    else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
        return Notification.Builder.recoverBuilder(context, notification).createBigContentView();
    else
        return null;
}

public static RemoteViews getContentView(Context context, Notification notification)
{
    if(notification.contentView != null)
        return notification.contentView;
    else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
        return Notification.Builder.recoverBuilder(context, notification).createContentView();
    else
        return null;
}

每当您引用 Notification.contentView 时,只需调用 getContentView(...),并为 Notification.bigContentView 调用 getBigContentView(...)。您的代码将支持 android nougat+ 和所有 android 版本。

修改后AddNotification会变成这样:

/**
 * Add notification into recycleview
 *
 * @param sbn notification to add
 */
private void AddNotification(StatusBarNotification sbn)
{
    Notification notification = sbn.getNotification();

    if (notification == null) return;

    RemoteViews remoteViews = getBigContentView(myContext(), notification);
    if(remoteViews == null)
        remoteViews = getContentView(myContext(), notification);

    if (remoteViews != null)
    {
        //apply bigContentView to my recycleview list notification view
        myListView.notificationview = remoteViews.apply(myContext(), myNotificationLayout);
    }

    //notify recycleview of a new item inserted
    notifyItemInserted(0);
}