6

W naszej aplikacji OneBusAway z systemem Android (open-source on Github), musimy otrzymać powiadomienie, gdy użytkownik odrzuci powiadomienie o konkretnym przypomnieniu, więc nie wysyłamy kolejnego powiadomienia o przypomnieniu dla tego samego wydarzenia (jak długo do przybycia autobusu).Powiadomienie DeleteIntent jest uszkodzone w nowszych wersjach Androida

Robimy to słuchając Intent w naszej aplikacji, zarejestrowanej jako DeleteIntent z Notification. Gdy użytkownik odrzuci powiadomienie (albo przesuwając je, albo dotykając przycisku kasowania w oknie powiadomień), nasza aplikacja powinna otrzymać tę Intent.

Z badań wydaje się, że z the current version on Google Play (i current master branch on Github), to nigdy nie otrzymał DeleteIntent w naszej aplikacji w następujących wersjach Androida:

  • Android 4.4.3
  • Android 4.4.4

jednak dokładnie ten sam kod CZY pracę (tj Intent zarejestrowany jako DeleteIntent jest odbierany przez aPP) na:

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

Szukałem w następujących SO stanowisk, które zajmują się DeleteIntent, Żadne z wymienionych rozwiązań nie działają w systemie Android 4.4.3 i 4.4.4:

Obecny mistrz roboczego gałąź wykorzystuje usługę do słuchania dla intencyjny. Jednakże, na podstawie niektórych z powyższych postów, zmodyfikowałem trochę kodu tak, aby był bardziej zgodny z przykładami pracy, które wykorzystują BroadcastReceiver do słuchania intencji.

Kod pomocą BroadcastReceiver jest w następujący GitHub Branża:

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

Poniżej znajdują się fragmenty o co moja obecna wersja wygląda (który nadal działa na Androidzie 4.1.2 i niższe, ale nie 4,4 .3 lub 4.4.4), wraz z linkami do GitHub źródła:


Tworzenie powiadomienia

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

Tytuł i inne informacje dynamiczne powiadomienia są ustawione kilka linijek później (i przywrócić później , jeśli powiadomienie pozostanie nierozstrzygnięte):

@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 zawiera stałe do działania:

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> 

z powyższym, na Androidzie 4.4.3/4.4.4 The AlarmReceiver nie widzi Intent, gdy użytkownik odrzuca zgłoszenie.

Próbowałem też dodanie typu MIME, jak określono w Custom actions using implicit intents between applications, ale to nie działa na Androidzie 4.4.3/4.4.4 albo:

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

Oczywisty dla przy użyciu typu 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> 

próbowałem też nie z wykorzystaniem wsparcia l ibrary (to jest, używając Notification.Builder zamiast NotificationCompat.Builder), ale to też niczego nie zmieniło.

Jakieś pomysły, dlaczego to nie działa w systemie Android 4.4.3/4.4.4?

Więcej informacji jest pokazane w Github issue dla tego problemu.

EDIT

Ja również powielane ten problem w małym projekcie GitHub "DeleteIntentDemo":

https://github.com/barbeau/DeleteIntentDemo

Instrukcje do odtworzenia są w README dla tego projektu.

EDIT 2

Wydaje się to być spowodowane błędem w systemie Android w Notification.setLatestEventInfo() - ja już zgłaszane tutaj: https://code.google.com/p/android/issues/detail?id=73720

proszę zobaczyć @ odpowiedź CommonsWare za obejścia.

EDIT 3

Moja AOSP łata, aby rozwiązać ten problem został już połączyły więc ten problem nie pojawi się dla starszych aplikacji w przyszłych wydaniach Androida: https://code.google.com/p/android/issues/detail?id=73720#c4

Jednak w powyższym W wątku AOSP podkreślono, że nie należy już używać Notification.setLatestEventInfo() - zamiast tego należy użyć Notification.Builder, aby utworzyć nowe powiadomienie.

+1

Czy próbowałeś wyraźnego "zamiaru"? Jeśli te działania nie są częścią publicznego zestawu SDK, do którego mają się odwoływać osoby trzecie, nie powinieneś mieć '' on 'AlarmReceiver'. Nie mogę odtworzyć twojego problemu z wyraźnym 'Intent' -' setDeleteIntent() 'działa poprawnie na moim Nexusie 4 działającym 4.4.4. – CommonsWare

+0

'Intent deleteIntent = new Intent (mContext, AlarmReceiver.class) ;, który jest obecnie używany, jest wyraźnym intencją, prawda? Niektóre z tych projektów są wcześniejsze niż moje zaangażowanie w projekt, ale uważam, że pomysł polegał na tym, aby (ostatecznie) zezwalać zewnętrznym aplikacjom na planowanie/kasowanie alarmów tranzytowych, a także wywoływanie tych samych działań z wewnętrznego kodu. Próbowałem usunąć '' s na AlarmReceiver, ale to niczego nie zmieniło. –

+0

@CommonsWare U dołu mojej odpowiedzi dodałem mały przykładowy projekt na Github, który odtwarza problem - https://github.com/barbeau/DeleteIntentDemo. Wszelkie opinie są mile widziane.Jeśli mógłbyś dostarczyć próbkę, która działa poprawnie, to też by pomogło. –

Odpowiedz

4

W projekcie próbki, jeśli usunąć następujące linii, deleteIntent prace nad Nexus 4 działa 4.4.4:

setLatestInfo(getActivity(), notification, routeId); 

Podejrzewam, że ta rozmowa jest wycierając swoje deleteIntent. Może to być praca polegająca na ponownym zastosowaniu Twojego deleteIntent do Notification jako część przetwarzania setLatestInfo().

+1

Masz rację! Jeśli usuniemy tę linię z projektu demonstracyjnego, działa ona również na moim Galaxy S3 z 4.4.4. Patrząc na źródło Android 'Notification.setLatestEventInfo()' (http://goo.gl/lqrdwy), wygląda na to, że 'Notification.Builder()' jest używane wewnętrznie, aby nadpisać bieżące powiadomienie z nowymi wartościami, ale bieżący 'deleteIntent' nigdy nie jest kopiowany - więc jest to w zasadzie błąd. Dziwne, ponieważ eliminacja 'Notification.setLatestEventInfo()' nie ma żadnego efektu w oryginalnym projekcie (po prostu przetestowany ponownie). Musi to być coś innego poza tym ... –

+0

@SeanBarbeau: Zauważ, że twoja próbka używa 'NotificationCompat.Builder', a nie' Notification.Builder', jak wspomniano w twoim komentarzu. Jeśli wymyślisz inną próbkę, która może odtworzyć Twój problem w wersji 4.4.4, daj mi znać. – CommonsWare

+0

W porządku - w projekcie demonstracyjnym tworzę "powiadomienie" za pomocą 'NotificationCompat.Builder'. Ale, gdy 'notification.setLatestEventInfo()' jest wywoływana później, 'Notification.setLatestEventInfo()' używa 'Notification.Builder' wewnętrznie. Tak więc, chociaż początkowo tworzyłem 'Notification' z biblioteką kompatybilności, platforma kończy się używając normalnego' Notification.Builder' w procesie 'Notification.setLatestEventInfo()'. Brakuje 'builder.setDeleteIntent (this.deleteIntent)' w 'Notification.setLatestEventInfo()'. –

1

Musisz mieć inny problem, ponieważ jestem w stanie odebrać deleteintent na kilku emulatorach 4.3 i 4.4.

Chciałem przetestować twój "prosty" projekt, ale używa Androida Studio, więc zrobiłem mój własny prostszy test.

Kroki do odtworzenia:

-Tworzenie działalność i ustawić tryb uruchamiania na singleInstance w manifeście.

-W obsługi przycisku lub menu pozycji, uruchom powiadomienia:

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 pokazać tosty lub zalogować wiadomość, gdy zgłoszenie jest anulowane:

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

Aby odrzucić powiadomienie, przesuń palcem lub naciśnij przycisk usuwania. Nie będzie nad nim naciskać, ponieważ autocancel nie jest uważana za jawną akcję użytkownika, a zatem zamiar usunięcia nie zostanie dostarczony.

+0

Problem wydaje się być (przynajmniej częściowo) przy użyciu 'Notification.setLatestEventInfo()'. Zobacz odpowiedź @CommonsWare powyżej. –

Powiązane problemy