6

Nella nostra app OneBusAway Android (open-source on Github), è necessario essere avvisati quando l'utente chiude una particolare notifica di sollecito, quindi non pubblichiamo un'altra notifica di promemoria per lo stesso evento (per quanto tempo fino a quando arriva il loro autobus).Notifica DeleteIntent interrotta nelle versioni successive di Android

Lo stiamo ascoltando per un Intent nella nostra app, registrato come DeleteIntent con il Notification. Quando l'utente rifiuta la notifica (sia scorrendo via, o toccando il pulsante Cancella nella finestra di notifica), la nostra app dovrebbe ricevere tale Intent.

Da test, sembra che con the current version on Google Play (e la current master branch on Github), il DeleteIntent non viene mai ricevuto nella nostra applicazione nelle seguenti versioni di Android:

  • Android 4.4.3
  • Android 4.4.4

Tuttavia, lo stesso identico codice di DOES lavoro (vale a dire, l'intento registrato come DeleteIntent viene ricevuto dal app) su:

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

Ho guardato i seguenti SO messaggi che si occupano di DeleteIntent, e nessuna delle soluzioni elencate lavoro su Android 4.4.3 e 4.4.4:

Il branch master di lavoro attuale utilizza un servizio per ascoltare l'intento. Tuttavia, basandomi su alcuni dei post di cui sopra, ho modificato alcuni dei codici per essere più in linea con gli esempi di lavoro che utilizzano un BroadcastReceiver per ascoltare l'Intent.

Il codice utilizzando il BroadcastReceiver è nel seguente ramo Github:

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

Qui di seguito sono estratti di ciò che la mia versione attuale sembra (che funziona ancora su Android 4.1.2 e inferiori, ma non 4.4 .3 o 4.4.4), insieme con i link alle Github fonte:


Creare la notifica

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(); 
} 

Titolo e altre informazioni di notifica dinamica sono impostate poche righe dopo (e ripristinati in seguito , se la notifica rimane non modificata):

@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 contiene le costanti per l'azione:

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> 

Con quanto sopra, su Android 4.4.3/4.4.4, l'AlarmReceiver non vede mai l'Intento quando l'utente chiude la notifica.

Ho anche provato ad aggiungere un tipo MIME, come specificato nella Custom actions using implicit intents between applications, ma che non ha funzionato su Android 4.4.3/4.4.4 o:

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_TYPE è application/vnd.com.joulespersecond.seattlebusbot.reminder

Manifesto per utilizzando il tipo 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> 

ho anche provato non utilizzando il supporto l ibrary (cioè, usando Notification.Builder invece di NotificationCompat.Builder), ma questo non ha cambiato nulla.

Qualche idea sul perché questo non funziona su Android 4.4.3/4.4.4?

Ulteriori informazioni sono visualizzate nello Github issue per questo problema.

EDIT

Ho anche replicato questo problema in un piccolo progetto GitHub "DeleteIntentDemo":

https://github.com/barbeau/DeleteIntentDemo

Istruzioni di riprodurre sono nel README per questo progetto.

EDIT 2

Questo sembra essere dovuto a un bug in Android in Notification.setLatestEventInfo() - ho riportato qui: https://code.google.com/p/android/issues/detail?id=73720

Si prega di vedere @ risposta di CommonsWare per la soluzione.

EDIT 3

mia patch AOSP per risolvere questo problema è stato ora fusa quindi questo problema non verrà visualizzato per le applicazioni legacy in future versioni di Android: https://code.google.com/p/android/issues/detail?id=73720#c4

Tuttavia, in quanto sopra Il thread AOSP è sottolineato dal fatto che non si dovrebbe più usare Notification.setLatestEventInfo() - invece, usare Notification.Builder per creare una nuova Notifica.

+1

Hai provato un esplicito 'Intento'? A meno che tali azioni non facciano parte di un SDK pubblico che le terze parti dovrebbero invocare, non si dovrebbe avere il su 'AlarmReceiver'. Non riesco a riprodurre il tuo problema con un esplicito 'Intento' -' setDeleteIntent() 'funziona bene sul mio Nexus 4 con 4.4.4. – CommonsWare

+0

'Intent deleteIntent = new Intent (mContext, AlarmReceiver.class);' che è attualmente in uso è un Intent esplicito, giusto? Alcuni di questi design sono precedenti al mio coinvolgimento nel progetto, ma credo che l'idea fosse di (eventualmente) consentire alle app esterne di programmare/annullare gli allarmi di transito e di attivare la stessa azione dal codice interno. Ho provato a rimuovere '' intent-filter> su AlarmReceiver, ma questo non ha cambiato nulla. –

+0

@CommonsWare Alla fine della mia risposta ho aggiunto un piccolo progetto di esempio su Github che riproduce il problema - https://github.com/barbeau/DeleteIntentDemo. Qualsiasi feedback è apprezzato.Se potessi fornire l'esempio che funziona correttamente, sarebbe anche d'aiuto. –

risposta

4

Nel progetto di esempio, se si rimuove la seguente riga, le deleteIntent opere su un Nexus 4 in esecuzione 4.4.4:

setLatestInfo(getActivity(), notification, routeId); 

ho il sospetto che questa chiamata sta spazzando via la vostra deleteIntent. Potrebbe funzionare per riapplicare lo deleteIntent allo Notification come parte dell'elaborazione di setLatestInfo().

+1

Hai ragione! Se rimuovo quella linea nel progetto demo funziona anche sul mio Galaxy S3 con 4.4.4. Guardando il sorgente Android per 'Notification.setLatestEventInfo()' (http://goo.gl/lqrdwy), sembra che 'Notification.Builder()' sia usato internamente per sovrascrivere la Notifica corrente con i nuovi valori, ma il corrente 'deleteIntent' non viene mai copiato - quindi questo è essenzialmente un bug lì. Stranamente, poiché l'eliminazione di 'Notification.setLatestEventInfo()' non ha alcun effetto nel progetto originale (appena testato di nuovo). Deve esserci qualcos'altro in corso in aggiunta a questo ... –

+0

@SeanBarbeau: Nota che il tuo campione usa 'NotificationCompat.Builder', non' Notification.Builder' come menzionato nel tuo commento. Se trovi un altro esempio in grado di riprodurre il tuo problema in 4.4.4, fammi sapere. – CommonsWare

+0

Right - nel progetto demo sto originariamente creando il 'notification' usando' NotificationCompat.Builder'. Tuttavia, quando 'notification.setLatestEventInfo()' viene chiamato in seguito, 'Notification.setLatestEventInfo()' utilizza internamente 'Notification.Builder'. Quindi, anche se inizialmente sto creando il 'Notification' con la libreria di compatibilità, la piattaforma finisce usando il normale' Notification.Builder' nel processo di 'Notification.setLatestEventInfo()'. Manca 'builder.setDeleteIntent (this.deleteIntent)' in 'Notification.setLatestEventInfo()'. –

1

È necessario un altro problema perché sono in grado di ricevere l'eliminazione in diversi emulatori 4.3 e 4.4.

Volevo testare il tuo progetto "semplice" ma utilizza Android Studio, quindi ho fatto il mio test più semplice.

Procedura per riprodurre:

-Creare un'attività e impostare la modalità di lancio per SingleInstance nel manifesto.

-In il gestore di una voce di menu pulsante o, lanciare una notifica:

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 per mostrare un toast o registrare un messaggio quando la notifica viene annullata:

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

Per eliminare la notifica, fai scorrere il dito o premi il pulsante di cancellazione. Non funzionerà su di esso perché autocancel non è considerato un'azione esplicita dell'utente e quindi l'intenzione di eliminazione non verrà consegnata.

+0

Il problema sembra essere (almeno parzialmente) con l'uso di 'Notification.setLatestEventInfo()'. Vedi la risposta di @CommonsWare sopra. –