一、岛通知构建方式
开发者有两种方式可以接入岛通知:
- 客户端实现
- MIPUSH 服务实现
以下是两种方式的接入步骤。
1、客户端实现
开发者需要按照发送原生通知的方式来发送岛通知, 在通知中添加岛通知所必须的扩展参数,在支持岛通知的设备上,将以岛通知的形式展示发送的通知。
1.1 构建原生通知
与发送普通通知的流程相同, 需要按照以下流程将发送通知:
- 获取 NotificationManager 对象
- 创建通知通道 NotificationChannel 对象
- 创建通知、指定 id
- 调用 NotificationManager 的 notify 接口发送通知
示例代码如下:
String channelId = "test_channel_id";
String channelName = "test_change_name";
String testTitle = "test_title";
String testText = "test_text";
// 获取 NotificationManager 对象
Context context = getApplicationContext();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
// 创建通知通道,可复用已有通知通道
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
// 小图标资源
int smallIconResId;
// 创建通知
Notification notification = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId)
.build();
// 发送通知
notificationManager.notify(1, notification);1.2 构建岛通知
岛通知信息以 Json 数据的形式添加到原生通知上,若在支持岛通知的设备上发送,系统根据携带扩展参数,将通知按照岛通知样式进行显示。
岛通知内容主要包含以下几部分内容:
- 交互能力数据
- 摘要态数据
- 焦点通知数据
创建岛通知基本代码结构如下:
// 构建岛通知参数的基本结构
String islandParams = "
{
"param_v2": {
// 交互能力数据
// 大岛内容数据
// 小岛内容数据
// 通知内容数据
}
}";
// 创建通知
Notification notification = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId)
.build();
// 添加岛参数
notification.extras.putString("miui.focus.param", islandParams);
// 发送通知
notificationManager.notify(1, notification);数据具体字段及含义请参见下文:基础数据构建, 不同模板有不同的摘要态大小岛和焦点通知数据字段, 详情可以查看模板库说明。
2、MIPUSH 服务实现
沿用现有 MIPUSH 接口, 通过扩展参数的方式支持焦点通知推送场景, 目前仅支持 regId 发送
- 扩展焦点通知 extra 参数,key:miui.focus.param、miui.focus.pic_XXX
- 发送方式:仅支持regId发送
关于 miui.focus.param 和各个模板的字段及含义, 请查看 交互能力设计 和 模版库 库部分
2.1 MIPUSH 发送岛通知示例代码
1. 构造 Sender 对象
Sender sender = new Sender(APP_SECRET);2.构造 Message 对象
# 构造Message
private Message buildMessage() {
String title = "notification title";
String description = "notification description";
Integer notifyId = 123456;
String params = "{
"param_v2": {
// 交互能力数据
// 大岛内容数据
// 小岛内容数据
// 通知内容数据
}
}";
// 图片信息
String extraMiuiFocusPicLarge1 = "https://url/xxxxxxx1.jpg";
String extraMiuiFocusPicLarge2 = "https://url/xxxxxxx2.jpg";
String extraMiuiFocusPicLarge3 = "https://url/xxxxxxx3.jpg";
Message message = new Message.Builder()
.title(title)
.description(description)
// ...其它参数...
.notifyId(notifyId)
.extra("miui.focus.param", params)
.extra("miui.focus.pic_xxx", extraMiuiFocusPicLarge1)
.extra("miui.focus.pic_yyy", extraMiuiFocusPicLarge2)
.extra("miui.focus.pic_zzz", extraMiuiFocusPicLarge3)
// 其他图片信息
//.extra("miui.focus.pic_...", "...")
.build();
return message;
}3.发送信息
Message message = buildMessage();
Result result = sender.send(message, regId, 3);
Log.v("Server response: ", "MessageId: " + result.getMessageId()
+ " ErrorCode: " + result.getErrorCode().toString()
+ " Reason: " + result.getReason());2.2 MIPUSH 接入图片要求
- 系统为通知下载图片所消耗的流量,会分发计算到对应应用上;
- 通知单张图片的大小不得超过100k,超过100k则不下载,视为下载失败;
- 图片下载全流程最大等待180s,超时则下载失败;
- 通知图片链接必须是Https格式,否则按下载失败处理;
- 图片宽高比在 1.78(16:9) - 1(1:1)之间
- 单个通知图片的数量不得大于10张,若超过6张,XMSF只会按顺序下载10张,其他不下载
二、基础数据构建
1、通知属性数据
属性参数用于设置本条通知的基本属性,比如 id、是否为持续性通知、自动消失时间等。
| 参数 | 必选 | 参数类型 | 含义 |
| isShowNotification | 否 | boolean | 是否显示通知条 - true:显示通知 - false: 不显示通知 默认值: true |
| business | 是 | String | 运营场景(数据统计) |
| islandFirstFloat | 否 | boolean | 通知第一次出现时,是否自动显示展开态 - true:展开态 - false:摘要态 默认值:true |
| enableFloat | 否 | boolean | 通知更新时,是否自动展开为展开态 - ture:展开 - false:不展开 默认值:false |
| timeout | 否 | int | 通知的默认消失时间, - 单位:min - 默认值: 720 |
| cancel | 否 | boolean | 是否直接结束通知 针对 mipush ,值为 true 时,触发移除通知 |
| updatable | 否 | boolean | 是否为持续性通知 - 默认: false |
| reopen | 否 | String | 相同 notification id 的通知被 cancel 后,再次发送是否显示, 前提:update = true - reopen:再次显示 - close:不显示 - 默认: close |
| filterWhenNoPermission | 否 | boolean | 应用焦点通知权限被关闭时退化为普通通知的处理方式: - false,通知正常显示不被过滤 - true,通知后被过滤,不显示 - 默认值:false |
| orderId | 否 | String | 订单号, MIPUSH 字段 |
| sequence | 否 | long | 本订单第几次更新通知,避免通知展示乱序。(MIPUSH 实时更新类 必传) |
| param_island | 是 | Object | 岛相关数据, 查看 岛数据 |
| extraInfo | 否 | Object | 额外信息 |
| carType | 否 | String | 汽车类型 |
| carColor | 否 | String | 汽车颜色 |
2、通知内容数据
通知内容数据同时在焦点通知和岛展开态生效,不同模板显示内容不同,具体字段请查看《小米超级岛模板库》
3、岛属性数据
| 必选 | 参数类型 | 含义 | ||
| islandProperty | 否 | int | 岛属性 - 1:信息展示为主 - 2:操作为主 - 默认值: 1 | |
| islandOrder | 否 | boolean | 通知更新时,但摘要态隐藏时,是否更新岛排序: - ture: 更新排序 - false: 不更新 - 默认值:false | |
| islandTimeout | 否 | int | 岛自动消失的时间 - 单位:s - 默认值: 60 * 60 | |
| dismissIsland | 否 | boolean | 摘要态消失 - true:消失 - false: 不消失 | |
| highlightColor | 否 | String | 文字本强调色 | |
| bigIslandArea | 是 | Object | 大岛内容 | |
| smallIslandArea | 是 | Object | 小岛内容 | |
| shareData | 否 | 用于拖拽分享的参数 | ||
| pic | 是 | String | 拖拽时卡片上的展示图片 | |
| title | 是 | String | 拖拽时卡片上的标题文案 | |
| content | 是 | String | 拖拽时卡片上的正文文案 | |
| shareContent | 是 | String | 分享内容 | |
示例代码如下:
// 创建通知
NotificationCompat.Builder builder = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId);
// 构建岛通知参数
String islandParams = "
{
"param_v2": {
"param_island": {
// 大岛内容
"bigIslandArea":{},
// 小岛内容
"smallIslandArea":{},
// 岛分享信息
"shareData": {}
}
}
}";
// 添加岛参数
notification.extras.putString("miui.focus.param", islandParams);
// 发送通知
notificationManager.notify(1, notification);不同摘要态模板对应 bigIslandArea、smallIslandArea 数据不同,具体模板字段请查看《小米超级岛通知模板库》中岛模板相关内容。
4、岛内容数据
岛摘要态不同模板显示内容不同,具体字段请查看《小米超级岛通知模板库》中岛模板相关内容。
5、息屏显示数据
设备处于息屏状态下,将展示岛息屏显示相关数据, 具体参数如下:
| 参数 | 必选 | 参数类型 | 含义 |
| aodTitle | 是 | String | 息屏焦点通知显示文案 |
| aodPic | 否 | String | 息屏模式图标 不传默认为应用图标, 自定义图标需要和miui.focus.pics参数对应 |
示例代码如下:
// 创建通知
NotificationCompat.Builder builder = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId);
// 构建岛通知参数
String islandParams = "
{
"param_v2": {
"aodTitle": "aodTitle",
"aodPic": "miui.focus.pic_aod"
}
}";
// 添加岛参数
notification.extras.putString("miui.focus.param", islandParams);
// 发送通知
notificationManager.notify(1, notification);6、状态栏焦点信息数据
在 OS2 版本 ROM 上,需要传输状态栏焦点信息相关数据,状态栏内容传输方式主要有两种,具体如下:
- 方式一: 通过具体参数传输(推荐)
| 参数 | 必选 | 参数类型 | 含义 |
| ticker | 是 | String | OS2 焦点通知状态栏显示文案 |
| tickerPic | 否 | String | OS2 焦点通知状态栏图标 不传默认为应用图标, 自定义图标需要和miui.focus.pics参数对应 |
| tickerPicDark | 否 | String | 暗黑模式下图标 |
示例代码:
// 创建通知
NotificationCompat.Builder builder = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId);
// 构建岛通知参数
String islandParams = "
{
"param_v2": {
"ticker": "tickerText",
"tickerPic": "miui.focus.pic_ticker"
}
}";
// 添加岛参数
notification.extras.putString("miui.focus.param", islandParams);
// 发送通知
notificationManager.notify(1, notification);- 方式二:自定义 RemoteViews 传输
自定义 RemoteViews 数据通过通知 extras 参数进行传递, 对应 key 如下:
| 参数 | 必选 | 参数类型 | 含义 |
| miui.focus.rvBar | 否 | RemoteViews | Light 模式下自定义状态栏 Key |
| miui.focus.rvBarNight | 否 | RemoteViews | Dark 模式下自定义状态栏 Key |
示例代码:
//自定义状态栏 RemoteViews
public static final String STATUS_BAR_CONTENT_REMOTE = "miui.focus.rvBar";
public static final String STATUS_BAR_CONTENT_REMOTE_NIGHT = "miui.focus.rvBarNight";
// 创建通知
NotificationCompat.Builder builder = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId);
//自定义状态栏 RemoteViews
RemoteViews statusBarView = ...;
RemoteViews darkStatusBarView = ...;
notification.extras.putParcelable(STATUS_BAR_CONTENT_REMOTE, statusBarView);
notification.extras.putParcelable(STATUS_BAR_CONTENT_REMOTE_NIGHT, darkStatusBarView);三、模版库说明
四、模版接入示例
综合以上接入说明,提供岛通知接入示例, 具体如下:
// 构建岛通知参数
String islandParams = "
{
// OS2/OS3
"param_v2": {
"protocol": 1,
// 运营场景, 比如打车场景为 taxi
"business":"taxi",
"enableFloat": true,
"updatable": true,
// 状态栏数据
"ticker": "ticker",
"tickerPic": "miui.focus.pic_ticker",
// 息屏 AOD 数据
"aodTitle": "aodTitle",
"aodPic": "miui.focus.pic_aod",
// 岛数据
"param_island": {
"islandProperty": 1,
// 大岛数据
"bigIslandArea": {
// 大岛 A 区
"imageTextInfoLeft": {
"type": 1,
"picInfo": {
"type": 1,
"pic": "miui.focus.pic_imageText"
},
"tmiui.focus.paramextInfo": {
"frontTitle": "充电中",
"title": "24%",
"content": "剩5分钟",
"useHighLight": false
}
},
// 大岛 B 区
"picInfo": {
"type": 1,
"pic": "miui.focus.pic_imageText"
}
},
// 小岛
"smallIslandArea": {
"picInfo": {
"type": 1,
"pic": "miui.focus.pic_imageText"
}
},
// 分享数据
"shareData": {
"title": "share_title"
}
},
// 焦点通知数据
"baseInfo": {
"title": "待取件",
"content": "安宁华庭2区8号底商店菜鸟驿站",
"colorTitle": "#006EFF",
"type": 2
},
"hintInfo": {
"type": 1,
"title": "2件包裹",
"actionInfo": {
"action": "miui.focus.action_test"
}
},
"extraInfo": {
"carType": "YU7",
"carColor": "白色"
}
}
}
";
// 创建通知
Notification notification = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId)
.build();
Bundle bundle = new Bundle();
// 添加 Action 数据
Bundle actions = new Bundle();
Intent intent = new Intent(ACTION_FOCUS_NOTIFICATION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)
Notification.Action action = new Notification.Action
.Builder(Icon.createWithResource(this, R.drawable.xxxx), "xxx", pendingIntent)
.build();
actions.putParcelable("miui.focus.action_test", action);
bundle.putBundle("miui.focus.actions", actions);
// 添加图片数据
Bundle pics = new Bundle();
pics.putParcelable("miui.focus.pic_imageText", Icon.createWithResource(this, R.drawable.xxx));
pics.putParcelable("miui.focus.pic_highlight", Icon.createWithResource(this, R.drawable.xxx));
bundle.putBundle("miui.focus.pics", pics);
builder.addExtras(bundle);
Notification notification = builder.build();
// 添加岛参数
notification.extras.putString("miui.focus.param", islandParams);
// 发送通知
notificationManager.notify(1, notification);五、查询接口
SystemUI 提供以下接口,可以查询当前系统版本是否支持岛通知、岛通知版本以及应用是否开启焦点通知权限, 具体接口如下:
1、查询当前 OS 是否支持岛功能
- 反射获取
// 调用
boolean supportIsland = isSupportIsland("persist.sys.feature.island", false);
/**
*
* @return true:支持岛
* false:不支持岛
*/
private boolean isSupportIsland(String key, boolean defaultValue) {
try {
Class<?> clazz = Class.forName("android.os.SystemProperties");
Method method = clazz.getDeclaredMethod("getBoolean", String.class, boolean.class);
Object object = method.invoke(null, key, false);
if (!(object instanceof Boolean)) {
return defaultValue;
}
return (Boolean) object;
} catch (Exception e) {
return defaultValue;
}
}2、查询当前 OS 是否支持焦点通知以及相应版本
/**
* 返回值含义:
* 1: OS1 版本, 支持 OS1 版本焦点通知模板
* 2: OS2 版本, 支持 OS2 版本焦点通知模板
* 3: OS3 版本, 支持 OS3 版本小米超级岛通知模板
*/
int focusProtocolVersion = Settings.System.getInt(
context.getContentResolver(),
"notification_focus_protocol", 0);OS2 和 OS3 支持焦点通知模版不同,同时只在 OS3 版本上支持岛, 开发者可以根据查询版本结果,有选择的选择接入 OS2 焦点通知 或者 OS3 岛通知。
3、查询当前应用是否开启焦点通知权限
// 耗时操作
// OS1之前无焦点通知功能的版本,返回false
// OS1 OS2 OS3上,焦点通知权限关闭时,返回false,焦点通知权限打开时,返回true
public static boolean hasFocusPermission(Context ctx) {
boolean canShowFocus = false;
try {
Uri uri = Uri.parse("content://miui.statusbar.notification.public");
Bundle extras = new Bundle();
extras.putString("package", ctx.getPackageName());
Bundle bundle = ctx.getContentResolver().call(uri, "canShowFocus", null, extras);
canShowFocus = bundle.getBoolean("canShowFocus", false);
} catch (Exception e) {
}
return canShowFocus;
}业务方可以根据以上查询结果, 选择是否发送焦点通知或者岛通知。
六、附录
1、图片数据参数:miui.focus.pics
焦点通知或者摘要态内容需要使用自定义图片, 那么需要通过以下方式将图片添加到通知参数。
示例代码如下:
// 创建通知
NotificationCompat.Builder builder = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId);
// 添加图片相关数据
Bundle pics = new Bundle();
pics.putParcelable("miui.focus.pic_start",
Icon.createWithResource(this, R.drawable.xxxx));
pics.putParcelable("miui.focus.pic_end",
Icon.createWithResource(this, R.drawable.xxxx));
bundle.putBundle("miui.focus.pics", pics);
builder.addExtras(bundle);
// 构建岛通知参数
String islandParams = "
{
"param_v2": {
"smallIsland": {
"picInfo": {
"type": 1,
// 使用 miui.focus.pic_start 对应的 Icon
"pic": "miui.focus.pic_start"
}
}
}
}";
Notification notification = builder.build();
// 添加岛参数
notification.extras.putString("miui.focus.param", islandParams);
// 发送通知
notificationManager.notify(1, notification);2、Action 数据参数
2.1 miui.focus.actions
开发者可以使用原生 API: Notification.Action 指定按钮的点击动作, 需要通过以下方式将 Action 添加到通知参数。
示例代码如下:
// 创建通知
NotificationCompat.Builder builder = new Notification.Builder(context, channelId)
.setContentTitle(testTitle)
.setContentText(testText)
.setSmallIcon(smallIconResId);
// 构建 Action 参数
Bundle actions = new Bundle();
Intent intent1 = new Intent(ACTION_FOCUS_NOTIFICATION);
// 针对触发广播的 PendingIntent 需要添加 FLAG_RECEIVER_FOREGROUND
intent1.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)
Notification.Action action1 = new Notification.Action
.Builder(Icon.createWithResource(this, R.drawable.pausebutton), "title", pendingIntent1)
.build();
actions.putParcelable("miui.focus.action_1", action1);
Intent intent2 = new Intent(ACTION_FOCUS_NOTIFICATION);
PendingIntent pendingIntent2 = PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)
Notification.Action action2 = new Notification.Action
.Builder(Icon.createWithResource(this, R.drawable.pausebutton), "title", pendingIntent2)
.build();
actions.putParcelable("miui.focus.action_2", action2);
// 将 action 数据添加到通知中
bundle.putBundle("miui.focus.actions", actions);
builder.addExtras(bundle);
// 构建岛通知参数
String islandParams = "
{
"param_v2": {
"actions": [
{
// 通过 key 来指定 Action
"action": "miui.focus.action_1"
},
{
"action": "miui.focus.action_2"
}
]
}
}";
Notification notification = builder.build();
// 添加岛参数
notification.extras.putString("miui.focus.param", islandParams);
// 发送通知
notificationManager.notify(1, notification);n2.2 ActionInfo 自定义 Action
除了使用原生Notification.Action , 也可以通过 ActionInfo 来自定义构建按钮点击效果,具体可以查看模板库中 ActionInfo 字段相关含义。