如何在 Android 上响应各种信息通知
有没有想过使用你的代码去回应未知的发送者是多么酷的事情?作为一家痴迷于最新技术的公司,我想和大家一起分享我们的一些研究
如果你是 Android 用户,你可能会喜欢 Pushbullet 这个令人敬畏的 App,它是一个越过设备同步你所有的消息的 App,包括 Pc 和 Mac,自从二月的更新,它允许回应消息给应用程序,例如 Facebook Messenger,Hangouts,Telegram 等等 -难道不让人敬畏?可以确定!并没有易于实施的 API 接口去实现这些,基本上每一个平台都需要我们实现所有客户的连接,但这不是一个好的解决方案。
作为一个喜欢此功能的用户,我们的工程师 Michał Tajchert 开始专研这个主题后他的好奇心被 Reddit 上的一个“它是怎么运行的”问题而引发,这个功能一定已经与 Android Wear API 发生了连接,作者是这样说的。小提示下,Android Wear 是提供给智能手表的平台。更具体的讲,我们并不是谈论关于智能手表全新的数据 API 或消息 API,但除了一些已经存在的小通知。由于 Android Wear 的介绍,我们可以增添一个被叫做"WearableExtender"的小物件,增添一些针对智能手表的功能,像活动页,背景,语音输入等
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY).build();
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(...)
.addRemoteInput(remoteInput)
.build();
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender().addAction(action);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(MainActivity.this).extend(wearableExtender);
在上面的代码我们可以看到,我们已经新建 RemoteInput(一个收集语音应答的对象),RemoteInput 被作为一个 Action 添加到我们的 WearableExtender 对象,最终被添加带 Notification 他自己本身。如果你有一个显示通知并等待用户我文本响应的 App(任何类似的 App),这是一个完美的解决方案对你来说。大多流行 App 都已经实现了。仅有非常少数的 App 仍然缺乏这点,像 Skype 正在计划添加这个功能在接下来的几周。
我们使用这个技巧的事例应用程序,当用户点击程序按钮,他将回应 Messenger conversation
怎样捕获通知?
在 Android 上还是比较容易实现的,只要实现你自己的 service,继承 NotificationListenerService 并且实现 onNotificationPosted() and onNotificationRemoved() 方法,这样任何通知的出现或被取消都会被感知。安全访问通知的完成是通过提示屏幕列表中能访问你的通知和能够选中或取消任何的应用程序通知。对于 UX 来说这并不是一个好的应用,因为它是全屏幕(所以这样的互动应该离开我们的 App),一个更好的模式是有一个列表应程序替代询问我们特殊权限的应用程序窗口。
让我们看看如何可以访问 WearableExtender
第一种方式,这样做实际上是由一些用户在网上发布,从 pushbulle 源提取,随着一些代码的完善使它工作,这是什么样子的:
Bundle bundle = statusBarNotification.getNotification().extras;
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
if("android.wearable.EXTENSIONS".equals(key)){
Bundle wearBundle = ((Bundle) value);
for (String keyInner : wearBundle.keySet()) {
Object valueInner = wearBundle.get(keyInner);
if(keyInner != null && valueInner != null){
if("actions".equals(keyInner) && valueInner instanceof ArrayList){
ArrayList<Notification.Action> actions = new ArrayList<>();
actions.addAll((ArrayList) valueInner);
for(Notification.Action act : actions) {
if (act.getRemoteInputs() != null) {//API > 20 needed
android.app.RemoteInput[] remoteInputs = act.getRemoteInputs();
}
}
}
}
}
}
}
WearableExtenderGetter hosted with ❤ by GitHub
在这里刚刚发生了什么?首先,我们从 WearableExtender 中的参数分布提取出了 Bundle 通知,然后我们搜索一个能让我们访问所有 Android Wear 特殊的功能关键值“android.wearable.EXTENSIONS”,例如页面.....我们正在寻找 RemoteInput!为了找到它,我们需要遍历所有的功能。这就是实际的工作,但还是代码看起来很丑并且“hackish”。
我们能比这做的更好吗?
当然!一行代码获得 WearableExtender,会是多么的酷?
WearableExtender wearableExtender = new WearableExtender(statusBarNotification.getNotification());
WearableExtenderGetter hosted with ❤ by GitHub
好的,让我们现在直接提取 Actions 从 RemoteInput 中。
List< Action> actions = wearableExtender.getActions();
for(Action act : actions) {
if(act != null && act.getRemoteInputs() != null) {
RemoteInput[] remoteInputs = act.getRemoteInputs();
}
}
WearableExtenderGetter hosted with ❤ by GitHub
好的,但是我们还需要做些什么呢?我们需要 PendingIntent 发送我们的返回结果给应用程序,使它触发通知并且他也将很好的保持 Bundle,因为他可能包含一些额外的价值,例如 conversationId,让我们去实现它
notificationWear.pendingIntent = statusBarNotification.getNotification().contentIntent;
notificationWear.bundle = statusBarNotification.getNotification().extras;
WearableExtenderGetter hosted with ❤ by GitHub
现在我们准备返回它!探究一下我们的事例应用程序,加速 POC 的开发过程,通过的数据以 EventBus 为媒介转到 Activity 被保存在 Stack,当用户点击“Respond to last”直接就突出最后题目并且伪造响应文本给它
填充 RemoteInput
大概最有趣的部分是怎么能够填补一个对象,通常需要语音输入我们虚假的文字,它并不是很难。
RemoteInput[] remoteInputs = new RemoteInput[notificationWear.remoteInputs.size()];
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bundle localBundle = notificationWear.bundle;
int i = 0;
for(RemoteInput remoteIn : notificationWear.remoteInputs){
getDetailsOfNotification(remoteIn);
remoteInputs[i] = remoteIn;
localBundle.putCharSequence(remoteInputs[i].getResultKey(), "Our answer");//This work, apart from Hangouts as probably they need additional parameter (notification_tag?)
i++;
}
RemoteInput.addResultsToIntent(remoteInputs, localIntent, localBundle);
try {
notificationWear.pendingIntent.send(MainActivity.this, 0, localIntent);
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "replyToLastNotification error: " + e.getLocalizedMessage());
}
我们的 notificationWear 对象是一个暂时存放所有需要数据来响应特殊通知的容器,然后我们把每一个 RemoteInput 从我们保存的通知中填充到我们所期望的文本,通过使用 putCharSequence() 方法在 Bundle 返回。这里的关键是使用 getResultKey() 在每一个 RemoteInput,因为被调用的程序将在返回 Bundle 答复文本。最后一步我们需要使 Intent 移植于 RemoteInputs 对于那些虚假的回应,让我们使用 addResultsToIntent(inputs, intent, bundle) 这样做。
现在我们准备好所有的数据去触发 PendingIntent,触发叫做 pendingIntent.send(context, code,intent) 的方法。这是全部,我们只是回应在 Messenger,Telegraph,Line 或是任何其他使用 WearableExtender 与 RemoteInput 的应用程序!这种方法仍然缺少 Hangouts,可能需要一些额外的参数,我最好的猜测是在其他 Tag 中的 StatusBarNotification 如果其他的(工作)应用程序也是空的。
帅不?
是的,是这样。仔细看一看 Pushbullet 是他被允许做的非常好的事情。但是在我们的办公室,值得被的关注的是,用户被提示选择应用程序读取通知和使用这个技巧,我们可以应对任何通知。还有,我们不但可以响应也可以缓存需要被触发的数据,例如,一些用在 Messenger 中以后使用的对话,同时,潜在的,它允许你发送短信在没有正确权限,因为你“just responded to a notification”和偶然有短信应用程序。现在在 Android 默认文本消息的应用程序不包括 WearableExtender,但也许这只是时间问题,而且 Hangouts 允许你发送短信。
时刻保持安全
什么是单向的,可以做其他工作的,这就意味着如果你正在开发一个 Wear-enabled 用用程序,其他的开发人员可能会回复你的通知,后来出现 RemoteInput 黑客,让我们想想如何保持安全。最简单的方法是生成一些 ID 对于每个通知并且允许他们只能使用一次,这样任何应用程序使用这个技巧不会发送过多的消息到你的应用程序。另一个方法是在 Bundle 中不包括一些参数,但是作为一个例子 Tag 中的 Notification 比较后接受。
这可能是我们为什么不能让黑客工作在 Hangouts。从另一方面来讲,如果你是使用者你应该更加关注那些曾获取“reading notifications”应用程序,因为他现在也允许应用程序与他们互动。如果你想要一个测验或者帮助我们对于 Hangouts 的工作,这个样品在 Github 上
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 自定义 Custom Drawables
下一篇: 自动化截图-应用分发时的自动截图方案
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论