在更高版本的Android上,通知DeleteIntent已损坏

时间:2014-07-15 22:28:02

标签: java android android-intent android-notifications android-pendingintent

在我们的应用程序OneBusAway Android(open-source on Github)中,我们需要在用户驳回特定提醒通知时收到通知,因此我们不会针对同一事件发布另一个提醒通知(他们的公共汽车到达多长时间) )。

我们通过在我们的应用中收听Intent,并使用DeleteIntent注册为Notification来实现此目的。当用户取消通知时(通过轻扫通知或点击通知窗口中的清除按钮),我们的应用应该会收到Intent

从测试开始,似乎使用the current version on Google Play(以及current master branch on Github),我们的应用程序永远不会在以下Android版本中收到DeleteIntent:

  • Android 4.4.3
  • Android 4.4.4

然而,完全相同的代码 DOES 工作(即应用程序收到注册为DeleteIntent的Intent):

  • Android 2.3.3
  • Android 2.3.6
  • Android 4.1.1
  • Android 4.1.2

我查看了以下涉及DeleteIntent的SO帖子,并且列出的所有解决方案都不适用于Android 4.4.3和4.4.4:

当前工作的主分支使用服务来侦听Intent。但是,基于上面的一些帖子,我确实调整了一些代码,使其更符合使用BroadcastReceiver监听Intent的工作示例。

使用BroadcastReceiver的代码位于以下Github分支中:

https://github.com/CUTR-at-USF/onebusaway-android/tree/issue104-RepeatingReminders

以下是我当前版本的内容摘录(仍适用于Android 4.1.2及更低版本,但不适用于4.4.3或4.4.4),以及指向Github源代码的链接:


制作通知

https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/tripservice/NotifierTask.java#L131

private Notification createNotification(Uri alertUri) {
    //Log.d(TAG, "Creating notification for alert: " + alertUri);
    Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
    deleteIntent.setAction(TripService.ACTION_CANCEL);
    deleteIntent.setData(alertUri);

    return new NotificationCompat.Builder(mContext)
            .setSmallIcon(R.drawable.ic_stat_notification)
            .setDefaults(Notification.DEFAULT_ALL)
            .setOnlyAlertOnce(true)
            .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
                    deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT))
            .setAutoCancel(true)
            .build();
}

标题和其他动态通知信息会在几行之后设置(如果通知仍未确定,则稍后重置):

@SuppressWarnings("deprecation")
private void setLatestInfo(Notification notification,
        String stopId,
        String routeId,
        long timeDiff) {
    final String title = mContext.getString(R.string.app_name);

    final PendingIntent intent = PendingIntent.getActivity(mContext, 0,
            new ArrivalsListActivity.Builder(mContext, stopId).getIntent(),
            PendingIntent.FLAG_UPDATE_CURRENT);

    notification.setLatestEventInfo(mContext,
            title,
            getNotifyText(routeId, timeDiff),
            intent);
}

TripService包含操作的常量:

public static final String ACTION_CANCEL =
        "com.joulespersecond.seattlebusbot.action.CANCEL";

AlarmReceiver

https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/AlarmReceiver.java

public class AlarmReceiver extends BroadcastReceiver {

    private static final String TAG = "AlarmReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "In onReceive with intent action " + intent.getAction());
        ...
    }
}

AndroidManifest

https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/AndroidManifest.xml

<receiver android:name=".AlarmReceiver">
     <!-- These action names must match the constants in TripService -->
      <intent-filter>
         <action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
         <action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
         <action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />
     </intent-filter>
 </receiver>

通过上述操作,在Android 4.4.3 / 4.4.4上,当用户解除通知时,AlarmReceiver永远不会看到Intent。

我还尝试添加Custom actions using implicit intents between applications中指定的MIME类型,但这在Android 4.4.3 / 4.4.4上也不起作用:

Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
    deleteIntent.setAction(TripService.ACTION_CANCEL);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        deleteIntent.setDataAndTypeAndNormalize(alertUri, TripService.REMINDER_MIME_TYPE);
    } else {
        deleteIntent.setDataAndType(alertUri, TripService.REMINDER_MIME_TYPE);
    }

    return new NotificationCompat.Builder(mContext)
            .setSmallIcon(R.drawable.ic_stat_notification)
            .setDefaults(Notification.DEFAULT_ALL)
            .setOnlyAlertOnce(true)
            .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
                    deleteIntent, 0))
                    //.setLights(0xFF00FF00, 1000, 1000)
                    //.setVibrate(VIBRATE_PATTERN)
            .build();

REMINDER_MIME_TYPEapplication/vnd.com.joulespersecond.seattlebusbot.reminder

使用MIME类型的清单:

<receiver android:name=".AlarmReceiver">
        <!-- These action names must match the constants in TripService -->
        <intent-filter>
            <action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
            <action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
            <action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />

            <data android:mimeType="application/vnd.com.joulespersecond.seattlebusbot.reminder" />
        </intent-filter>
    </receiver>

我还尝试使用支持库(即使用Notification.Builder代替NotificationCompat.Builder),但这也没有改变任何内容。

为什么这不适用于Android 4.4.3 / 4.4.4?

此问题在Github issue中显示了更多信息。

修改

我还在一个小的Github项目“DeleteIntentDemo”中复制了这个问题:

https://github.com/barbeau/DeleteIntentDemo

重现的说明在此项目的自述文件中。

编辑2

这似乎是由于Notification.setLatestEventInfo()中的Android中的错误 - 我在此处报告过: https://code.google.com/p/android/issues/detail?id=73720

请参阅@ CommonsWare的解决方案答案。

编辑3

我的AOSP补丁修复此问题现已合并,因此在Android的未来版本中,旧版应用不会出现此问题: https://code.google.com/p/android/issues/detail?id=73720#c4

但是,在上述AOSP主题中强调一个人不应再使用Notification.setLatestEventInfo() - 而是使用Notification.Builder来创建新的通知。

2 个答案:

答案 0 :(得分:4)

在您的示例项目中,如果您删除以下行,则deleteIntent适用于运行4.4.4的Nexus 4:

setLatestInfo(getActivity(), notification, routeId);

我怀疑这个电话会消灭你的deleteIntent。作为deleteIntent处理的一部分,将Notification重新应用于setLatestInfo()可能会有效。

答案 1 :(得分:1)

您必须遇到其他问题,因为我能够在多个4.3和4.4模拟器中收到deleteintent。

我想测试你的简单&#34;项目,但它使用Android Studio,所以我做了我自己更简单的测试。

重现的步骤:

创建一个活动并在清单中将启动模式设置为singleInstance。

- 在按钮或菜单项的处理程序中,启动通知:

    Intent deleteIntent = new Intent(this, MainActivity.class);

     Notification notification = new NotificationCompat.Builder(this)
    .setSmallIcon(android.R.drawable.ic_dialog_alert)
    .setOnlyAlertOnce(true)
    .setContentTitle("Notification delete intent test")
    .setContentText("Please dismiss this notification by swipping or deleting it. A Toast will be shown if the deletion intent works.")
    .setDeleteIntent(PendingIntent.getActivity(this, 0, deleteIntent, 0))
    .setAutoCancel(true)
    .build();

     NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
     nm.notify((int)System.currentTimeMillis(), notification);

-Override onNewIntent在通知取消时显示祝酒词或记录消息:

    @Override
    public void onNewIntent(Intent intent){
        Toast.makeText(this, "Notification deleted!", Toast.LENGTH_LONG).show();
    }

要解除通知,请滑动或按清除按钮。它无法按下它,因为autocancel不被视为显式用户操作,因此删除意图不会被传递。