Korzystanie z powiadomień z Androida

To ćwiczenie programowania jest częścią kursu „Android dla zaawansowanych w Kotlinie”. Korzyści z tego kursu będą dla Ciebie najbardziej wartościowe, jeśli wykonasz je w sekwencjach ćwiczeń z programowania, ale nie jest to obowiązkowe. Wszystkie ćwiczenia z kursu są wymienione na stronie Zaawansowane ćwiczenia z programowania na Androida w Kotlin.

Wstęp

Powiadomienia to wiadomości pokazywane użytkownikowi poza interfejsem aplikacji. Powiadomienia są wyświetlane u góry ekranu, jeśli urządzenie jest odblokowane lub, w zależności od ustawień zabezpieczeń, na ekranie blokady, gdy urządzenie jest zablokowane.

Typowe powiadomienie składa się z tytułu, opisu i ikony. Powiadomienie może też zawierać działania, które można kliknąć, krótką odpowiedź, treść, którą można rozwinąć, i obrazy.

Powiadomienia mogą szybko dostarczać materiały, a klienci mogą używać przycisków umożliwiających użytkownikowi wykonywanie szybkich działań, takich jak wysłanie odpowiedzi czy odłożenie alarmu. Kliknięcie powiadomienia powoduje przejście do widoku w aplikacji powiązanego z treścią powiadomienia.

Dzięki powiadomieniom możesz powiadomić użytkowników o ważnym zadaniu, poinformować ich o wystąpieniu lub poinformować ich o ważnych zmianach, gdy aplikacja działa w tle. Nie nadużywaj powiadomień. Nie tylko szanuje to użytkowników, ale również zwiększa prawdopodobieństwo, że Twoja aplikacja zasługuje na to, na co zasługuje.

Z tego modułu dowiesz się, jak tworzyć i używać powiadomienia w aplikacji na Androida.

Co musisz wiedzieć

Pamiętaj:

  • Jak tworzyć aplikacje na Androida w Kotlinie. Szczególnie dotyczy to pakietu Android SDK.
  • Jak łączyć architekturę aplikacji za pomocą komponentów architektury i wiązania danych.
  • Podstawowe informacje o BroadcastBroadcastReceive
  • Podstawowe informacje o usłudze AlarmManager

Czego się nauczysz

  • jak tworzyć i wysyłać powiadomienia oraz określać ich styl;
  • Jak anulować powiadomienia.
  • Tworzenie kanałów powiadomień
  • Jak dodawać szybkie działania do powiadomień
  • Wyświetlanie plakietek z powiadomieniem na ikonie aplikacji

Jakie zadania wykonasz:

  • Dodaj powiadomienie do aplikacji startowej.
  • Anuluj wysłane wcześniej powiadomienie.
  • Twórz kanały dla różnych typów powiadomień.
  • Dostosuj powiadomienia w aplikacji startowej.
  • Dodaj szybkie działania, aby powiadomienie stało się interaktywne.
  • Wyłącz plakietki z powiadomieniami.

Przygotowywanie jaj jest proste, ale może się okazać wyzwaniem, jeśli nie potrafisz go kontrolować. W tym ćwiczeniu będziesz pracować nad aplikacją jajka z licznikiem czasu, która pomoże sprawić, by jajka wyglądały jak prawdziwe. Uruchomiona zostanie aplikacja działającego minutnika jaj, która daje użytkownikowi możliwość ustawienia różnych ustawień czasu gotowania dla różnych stylów jaj. Minutnik odlicza od wybranego przedziału czasu i wyświetla komunikat, gdy jajka będą gotowe.

Może się wydawać, że działa, ale wcale nie jest idealna i niezbyt przyjazna użytkownikowi. Po pierwsze, komunikat wyświetla się tylko przez krótki czas i dlatego można go łatwo pominąć. Ponadto, jeśli aplikacji nie ma na pierwszym planie lub urządzenie jest zablokowane, nie pojawi się wskaźnik wizualny stanu minutnika po zniknięciu komunikatu.

Najlepiej, gdyby minutnik jajka pokazywał się użytkownikom, kiedy czas dobiega końca. Użytkownik musi wiedzieć, że jajka są od razu gotowe, w przeciwnym razie będą się gotować. Powiadomienia mają charakter wizualny, mogą zawierać dźwięki i sprawić, że urządzenie zacznie wibrować, a przyciągnie ono uwagę użytkownika. W ten sposób przygotujesz doskonałe jajka, które będą uspokoić użytkowników.

Możesz pobrać aplikację próbną na dwa sposoby:

Skopiuj repozytorium z GitHuba i przejdź do gałęzi starter.

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications


Możesz też pobrać repozytorium jako plik ZIP, rozpakować go i otworzyć w Android Studio.

Pobierz aplikację Zip

  1. Otwórz i uruchom aplikację w Android Studio.

Pojawi się menu jajka i menu z gotową listą gotowych przedziałów czasowych, w których można ugotować jajko. Kliknij trójkąt w menu Miękkie gotowane. Pierwsza opcja na liście służy do testowania i ustawia alarm na 10 sekund. Obok listy znajduje się przełącznik, który uruchamia minutnik jaj. W każdej chwili możesz użyć tego przełącznika, aby włączać i wyłączać minutnik jaj. Kod początkowy jest w pełni funkcjonalny, co oznacza, że możesz skonfigurować licznik czasu jajka i odliczać do 0. Po zakończeniu odliczania zostanie wyświetlony komunikat.

  1. Sprawdź kod źródłowy. Aplikacja startowa składa się z pojedynczej aktywności o nazwie MainActivity. Dostępne są 3 podpakiety o nazwach receiver, ui i util.

  • /odbiornik – pakiet receiver zawiera 2 odbiorniki o nazwie AlarmReceiver i SnoozeReceiver. AlarmReceiver uruchamia się, gdy AlarmManager wysyła powiadomienie, gdy licznik czasu zdefiniowany przez użytkownika minie. Aby odłożyć powiadomienie, użytkownik musi kliknąć SnoozeReceiver.
  • /ui – zawiera EggTimerFragment, który jest częścią interfejsu aplikacji. EggTimerViewModel odpowiada za uruchamianie i anulowanie minutnika oraz za inne zadania związane z cyklem życia aplikacji.
  • /util – w tym pakiecie znajdują się dwa pliki. BindingUtils.kt ma adaptery wiązania, które umożliwiają wiązanie danych między interfejsem aplikacji a ViewModel. NotificationUtils.kt udostępnia metody rozszerzeń na NotificationManager.

Użycie powiadomień to świetny sposób na przyciągnięcie uwagi użytkowników aplikacji. Bez względu na to, czy aplikacja działa czy nie działa na pierwszym planie, powiadomienie wyświetli wyskakujące okienko, które może zawierać dźwięk lub wibracje. Aby utworzyć powiadomienie, musisz użyć kreatora powiadomień oraz podać tekst tytułu, treść i ikonę. Gdy konstruktor ma wszystkie wymagane pola, NotificationManager, czyli usługa systemowa, umożliwia wyświetlanie tych treści jako powiadomień. NotificationManager odpowiada za wysłanie powiadomienia, zaktualizowanie jego treści i anulowanie powiadomienia. W kolejnych krokach dodasz metody rozszerzeń do NotificationManager. Dzięki temu za każdym razem, gdy zechcesz skorzystać z NotificationManager, będziesz mieć możliwość skorzystania z tych funkcji rozszerzeń, aby uzyskać niezbędne funkcje.

Krok 1. Utwórz podstawowe powiadomienie

W tym zadaniu tworzysz nowe powiadomienie, ustawiasz wiadomość dla użytkownika i wysyłasz powiadomienie.

  1. Otwórz klasę NotificationUtils.kt i znajdź opcję TODO: Step 1.1. Zadania do wykonania znajdziesz w tym ćwiczeniu z programowania i w kodzie aplikacji.
  2. Sprawdź daną funkcję sendNotification(). Rozszerzysz tę funkcję rozszerzenia o NotificationManager, aby wysyłać powiadomienia.
//NotificationUtils.kt
// TODO: Step 1.1 extension function to send messages (GIVEN)
/**
 * Builds and delivers a notification.
 *
 * @param messageBody, notification text.
 * @param context, activity context.
 */
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
  1. Pobierz wystąpienie kreatora powiadomień, przekaż kontekst aplikacji i identyfikator kanału. Identyfikator kanału jest ciągiem znaków.

Kanały powiadomień umożliwiają grupowanie powiadomień. Łącząc podobne typy powiadomień, deweloperzy i użytkownicy mogą kontrolować wszystkie powiadomienia na kanale. Po utworzeniu kanału możesz używać go do wysyłania dowolnej liczby powiadomień.

//NotificationUtils.kt
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
val builder = NotificationCompat.Builder(
        applicationContext,
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. Ustaw ikonę powiadomienia, aby reprezentować aplikację, tytuł oraz tekst treści wiadomości, którą chcesz przekazać użytkownikowi. W ćwiczeniach z programowania zobaczysz więcej opcji dostosowywania powiadomienia, ale jest to minimalna ilość danych, które musisz ustawić, aby wysłać powiadomienie.
//NotificationUtils.kt
   // TODO: Step 1.3 set title, text and icon to builder
   .setSmallIcon(R.drawable.cooked_egg)
   .setContentTitle(applicationContext.getString(R.string.notification_title))
   .setContentText(messageBody)
  1. Następnie musisz wywołać notify() za pomocą unikalnego identyfikatora powiadomienia i obiektu Notification z konstruktora.

Ten identyfikator reprezentuje bieżącą instancję powiadomienia i jest potrzebny do zaktualizowania lub anulowania tego powiadomienia. Twoja aplikacja będzie miała tylko jedno aktywne powiadomienie, więc możesz używać tego samego identyfikatora dla wszystkich powiadomień. Masz już stałą wartość o nazwie NOTIFICATION_ID w NotificationUtils.kt. Zauważ, że możesz wywołać funkcję notify() bezpośrednio, ponieważ wykonujesz połączenie z funkcji rozszerzenia w tej samej klasie.

//NotificationUtils.kt
   // TODO: Step 1.4 call notify to send the notification
    // Deliver the notification
    notify(NOTIFICATION_ID, builder.build())
  1. Otwórz ui/EggTimerViewModel.kt i znajdź funkcję startTimer(). Ta funkcja tworzy alarm z wybranym przedziałem czasu, gdy użytkownik włączy minutnik.
  2. Gdy użytkownik uruchomi minutnik, wyświetli się powiadomienie w tej funkcji. Aby wywoływać funkcję sendNotification(), która została wcześniej zaimplementowana, potrzebujesz wystąpienia NotificationManager. NotificationManager to usługa systemowa, która udostępnia wszystkie funkcje interfejsu API powiadomień, w tym funkcję dodanego rozszerzenia. Jeśli chcesz wysłać, anulować lub zaktualizować powiadomienie, musisz poprosić o wystąpienie elementu NotificationManager w systemie. Wywołaj funkcję sendNotification()| z powiadomieniem i kontekstem.
// EggTimerViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification

val notificationManager = ContextCompat.getSystemService(
    app, 
    NotificationManager::class.java
) as NotificationManager
                notificationManager.sendNotification(app.getString(R.string.timer_running), app)

Prawie Ci się udało! Jeśli jednak uruchomisz aplikację teraz i ustawisz minutnik, nie otrzymasz powiadomienia.

  1. Otwórz aplikację logcat i wyszukaj: "No Channel found". Powinien wyświetlić się komunikat o błędzie informujący, że egg_channel nie istnieje. Aby rozwiązać ten problem, wykonaj czynności opisane poniżej.

Krok 2. Kanały powiadomień

Począwszy od poziomu API 26, wszystkie powiadomienia muszą być przypisane do kanału. Jeśli naciśniesz i przytrzymasz ikonę programu uruchamiającego, wybierzesz informacje o aplikacji i klikniesz powiadomienia, zobaczysz listę kanałów powiadomień powiązanych z aplikacją. Obecnie lista jest pusta, ponieważ aplikacja nie utworzyła żadnych kanałów.

Kanały oznaczają &typ powiadomienia – na przykład stoper z jajkiem może wysłać Ci powiadomienie, gdy jajko się zagotuje, a codziennie wysyłać kolejne powiadomienia, by przypominać Ci o śniadaniu. Wszystkie powiadomienia na kanale są grupowane, a użytkownicy mogą konfigurować ustawienia powiadomień dla całego kanału. Dzięki temu użytkownicy mogą spersonalizować ustawienia powiadomień, biorąc pod uwagę rodzaj powiadomień, które ich interesują. Użytkownicy mogą na przykład wyłączyć powiadomienia o śniadaniu, ale nadal chcą otrzymywać powiadomienia z minutnika.

Deweloperzy określają wstępne ustawienia, znaczenie i zachowanie stosowane do wszystkich powiadomień w kanale. Gdy skonfigurujesz ustawienia początkowe, użytkownicy mogą je zastąpić.

W kroku 1.1 jako kanał powiadomień wybrano egg_notification_channel_id, więc teraz musisz samodzielnie utworzyć i dostosować ustawienia powiadomień oraz sposób działania tego kanału.

  1. Otwórz EggTimerFragment.kt i znajdź funkcję createChannel().
  2. Przekaż unikalny identyfikator kanału konstruktorowi NotificationChannel.
  3. Przekaż nazwę kanału powiadomień, którą użytkownicy zobaczą też na ekranie Ustawienia.
  4. Jako ostatni parametr, którego poziom ważności jest wymagany w przypadku kanału powiadomień. Poziomy ważności zostaną omówione w dalszej części tego ćwiczenia, więc na razie możesz użyć NotificationManager.IMPORTANCE_LOW.
  5. W obiekcie notificationChannel ustaw wartość „enableLights” na „true”. To ustawienie włączy diody po wyświetleniu powiadomienia.
  6. Na notificationChannel obiekcie lightColor ustawionym na czerwono, aby pojawiło się czerwone światło po wyświetleniu powiadomienia.
  7. Aby włączyć wibracje, w obiekcie notificationChannel ustaw wartość „enableVibration” na „true”.
  8. W obiekcie notificationChannel ustaw opis kanału na ‘Time for breakfast'.
  9. Uzyskaj instancję NotificationManager przez wywołanie metody getSystemService().
  10. Wywołaj createNotificationChannel() w obiekcie NotificationManager i przekaż obiekt notificationChannel utworzony w poprzednim kroku.
//EggTimerFragment.kt
private fun createChannel(channelId: String, channelName: String) {
    // TODO: Step 1.6 START create a channel
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val notificationChannel = NotificationChannel(
            channelId,
            channelName,
            // TODO: Step 2.4 change importance
            NotificationManager.IMPORTANCE_LOW
        )
        // TODO: Step 2.6 disable badges for this channel

        notificationChannel.enableLights(true)
        notificationChannel.lightColor = Color.RED
        notificationChannel.enableVibration(true)
        notificationChannel.description = "Time for breakfast"

        val notificationManager = requireActivity().getSystemService(
            NotificationManager::class.java
        )
        notificationManager.createNotificationChannel(notificationChannel)
    }
    // TODO: Step 1.6 END create channel
}
  1. Aby utworzyć kanał, musisz wywołać utworzoną funkcję createChannel() (krok 1.7). Funkcja ta wykorzystuje 2 parametry: identyfikator i nazwę kanału. Identyfikator i nazwę kanału musisz wyszukać w zasobach ciągu znaków podanych już w Twoim projekcie.
// EggTimerFragment.kt
    // TODO: Step 1.7 call createChannel
    createChannel(
          getString(R.string.egg_notification_channel_id),
          getString(R.string.egg_notification_channel_name)
    )
  1. Musisz przekazać identyfikator kanału do kreatora powiadomień. Ta czynność została już wykonana w kroku 1.2. Ustawienie niewłaściwej wartości spowoduje, że powiadomienie nie powiedzie się. Otwórz NotificationUtils.kt, aby sprawdzić, czy wcześniej ustawiony identyfikator kanału jest prawidłowy.
// NotificationUtils.kt
val builder = NotificationCompat.Builder(
        applicationContext,
       // TODO: Step 1.8 verify the notification channel name
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. Uruchom aplikację. Zobaczysz powiadomienie przy każdym uruchomieniu minutnika.
  2. Pociągnij pasek stanu i sprawdź, czy tytuł, treść i ikona powiadomienia są ustawione tak jak w poprzednich krokach.
  3. Aby sprawdzić informacje o nowo utworzonym kanale, zamknij aplikację i znajdź ikonę aplikacji. Przytrzymaj ikonę aplikacji i wybierz Informacje o aplikacji.

  1. Z listy ustawień wybierz Powiadomienia. Pod ustawieniem Pokaż powiadomienia powinien być widoczny nowy kanał o nazwie Jajko.

Powiadomienie pojawi się po uruchomieniu aplikacji. Zarówno Ty, jak i deweloper aplikacji, możecie dostosować ustawienia i zachowanie wszystkich powiadomień wysyłanych na ten kanał. Gratulacje, powiadomienie zostało utworzone!

Krok 3. Dodaj powiadomienia do aplikacji

Na razie pokazano podstawy korzystania z interfejsu API powiadomień, ale wysyłanie powiadomienia zaraz po uruchomieniu minutnika nie ma sensu. Użytkownicy wolą otrzymywać powiadomienia, gdy jajko będzie gotowe. W następnej części ćwiczeń z programowania dowiesz się, jak to naprawić, i wymienisz toast na powiadomienie.

Powiadomienie zostało już wysłane i zgłoszone użytkownikom zostało wyświetlone, ale to był tylko pierwszy krok do utworzenia świetnych powiadomień. W tym kroku zmienisz ustawienia powiadomień, by wysyłane w odpowiednim momencie.

Twoja aplikacja używa AlarmManager do konfigurowania alarmu. Kod związany z AlarmManager jest już zawarty w kodzie początkowym i jest używany do wyświetlania komunikatu. AlarmManager śledzi wybraną godzinę i aktywuje funkcję onReceive() AlarmReceiver.kt po zakończeniu. Jeśli otworzysz aplikację AlarmReceiver.kt i przejdziesz do sekcji onReceive(), powinien się wyświetlić komunikat, który pojawia się za każdym razem, gdy ustawisz minutnik.

  1. Otwórz AlarmReceiver.kt (wystąpienie NotificationManager) i wywołaj funkcję sendNotification() z tekstem wiadomości oraz parametrami kontekstowymi.
// AlarmReceiver.kt
   // TODO: Step 1.9 add call to sendNotification
   val notificationManager = ContextCompat.getSystemService(
       context, 
       NotificationManager::class.java
   ) as NotificationManager
             
   notificationManager.sendNotification(
       context.getText(R.string.eggs_ready).toString(), 
       context
   )
  1. Możesz też usunąć to powiadomienie, ponieważ po zakończeniu minutnika aplikacja zacznie wysyłać powiadomienia.
// AlarmReceiver.kt
     // TODO: Step 1.10 [Optional] remove toast
//   Toast.makeText(
//       context, 
//       context.getText(R.string.eggs_ready),
//       Toast.LENGTH_SHORT
//   ).show()
  1. Uruchom aplikację . Takie powiadomienie jest wyświetlane za każdym razem, gdy uruchomisz minutnik lub po jego upływie.

Nie jest to idealne rozwiązanie. Nie chcesz wysyłać zbyt wielu powiadomień do użytkowników. Możesz usunąć pierwsze powiadomienie, które jest wysyłane, gdy użytkownik uruchomi minutnik.

  1. Otwórz aplikację EggTimerFragment.kt i usuń kod powiadomienia z kroku 1.5.
// EggTimeViewModel.kt

// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification
// val notificationManager = ContextCompat.getSystemService(
//      app,
//      NotificationManager::class.java
// ) as NotificationManager
// notificationManager.sendNotification(app.getString(R.string.eggs_ready), app)
  1. Uruchom ponownie aplikację.
  2. Ustaw minutnik, umieść go w tle i zaczekaj, aż się zakończy. Zobaczysz powiadomienie. To powiadomienie jest dużo bardziej przydatne.

Krok 4. Dodaj intencję treści

  1. Uruchom aplikację ponownie, jeśli jeszcze nie jest uruchomiona.
  2. Kliknij powiadomienie. Nic się nie dzieje.

Wyświetlanie powiadomienia i informowanie go jest świetne, ale gdy użytkownik kliknie powiadomienie, spodziewa się, że wróci do odpowiedniej aplikacji. W ramach ćwiczenia z programowaniem dodasz do powiadomienia zamiar, by wyświetlić go na ekranie licznika.

Element Intent to obiekt wiadomości, którego możesz użyć, aby zażądać działania z innego komponentu aplikacji. Intencje mogą być używane do uruchamiania działania, usługi lub dostarczania wiadomości. W takim przypadku ten system służy do informowania systemu, że gdy użytkownik kliknie powiadomienie, ma otwierać MainActivity. Aplikacja składa się tylko z jednego widoku, więc nie masz tu zbyt wielu opcji. Jednak w większej aplikacji powiadomienie powinno zapewnić użytkownikowi płynny obraz i skierować go na ekran odpowiedni do interakcji z powiadomieniem.

  1. Otwórz NotificationUtils.kt i znajdź funkcję rozszerzenia sendNotification().
  2. Utwórz Intent z applicationContext i rodzajem aktywności, którą chcesz uruchomić, MainActivity::class.java.
// NotificationUtils.kt

fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
    // Create the content intent for the notification, which launches
    // this activity
   // TODO: Step 1.11 create intent
    val contentIntent = Intent(applicationContext, MainActivity::class.java)

Została przez Ciebie utworzona intencja, ale w aplikacji pojawiło się powiadomienie. Jeśli chcesz, aby intencje działały poza aplikacją, musisz utworzyć nową grupę PendingIntent.

PendingIntent przyznaje prawo do innej aplikacji lub systemu do wykonywania operacji w imieniu Twojej aplikacji. Element PendingIntent jest tylko odwołaniem do tokena przechowywanego przez system opisujący pierwotne dane użyte do jego pobrania. Oznacza to, że nawet jeśli proces tworzenia aplikacji zostanie zakończony, PendingIntent nadal będzie można go używać na potrzeby innych procesów, którym go przydzielono. W takim przypadku system będzie uruchamiać aplikację w Twoim imieniu, niezależnie od tego, czy działa minutnik.

  1. Utwórz PendingIntent z applicationContext, NOTIFICATION_ID, contentIntent utworzonym w poprzednim kroku i flagą PendingIntent. Flaga PendingIntent określa opcję tworzenia nowego obiektu PendingIntent lub używania istniejącego. Musisz ustawić PendingIntent.FLAG_UPDATE_CURRENT jako flagę, ponieważ nie chcesz tworzyć nowego powiadomienia, jeśli już istnieje. W ten sposób zmienisz obecny element PendingIntent powiązany z intencją intencji.
// NotificationUtils.kt
   // TODO: Step 1.12 create PendingIntent
    val contentPendingIntent = PendingIntent.getActivity(
        applicationContext, 
        NOTIFICATION_ID,
        contentIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
  1. Przekaż PendingIntent w powiadomieniu. Aby to zrobić, zadzwoń pod numer setContentIntent() w aplikacji NotificationBuilder. Teraz gdy klikniesz powiadomienie, wyświetli się PendingIntentMainActivity otworzy się.
  2. Możesz też ustawić setAutoCancel() na true – gdy użytkownik kliknie powiadomienie, otworzy się samo przejście do aplikacji.
// NotificationUtils.kt
    // TODO: Step 1.13 set content intent
    .setContentIntent(contentPendingIntent)
    .setAutoCancel(true)
  1. Ponownie uruchom aplikację.
  2. Ustaw minutnik, umieść aplikację w tle i zaczekaj, aż pojawi się powiadomienie.
  3. Gdy pojawi się powiadomienie, kliknij je, przesuwając w dół pasek stanu. Zobaczysz, jak aplikacja działa na pierwszym planie.

Krok 5. Anuluj powiadomienie

Masz ustawiony funkcjonalny minutnik jajka z powiadomieniami. Wystąpił mały problem. Jeśli ustawisz minutnik, otrzymasz powiadomienie i ponownie ustawisz minutnik, poprzednie powiadomienie pozostanie na pasku stanu podczas działania nowego minutnika. Może to dezorientować użytkowników, jeśli aplikacja działa w tle, co może doprowadzić do niedogotowania jaj.

Aby rozwiązać ten problem, musisz wyczyścić poprzednie powiadomienie po uruchomieniu nowego minutnika. Zacznij od utworzenia innej funkcji rozszerzenia w NotificationUtils.kt. NotificationManager ma interfejs API do anulowania wszystkich aktywnych powiadomień o nazwie cancelAll().

  1. Otwórz aplikację NotificationsUtil.kt.
  2. Dodaj do NotificationManager funkcję rozszerzenia, która wywołuje cancelAll().
// NotificationUtils.kt

// TODO: Step 1.14 Cancel all notifications
/**
 * Cancels all notifications.
 *
 */
fun NotificationManager.cancelNotifications() {
    cancelAll()
}
  1. Otwórz aplikację EggTimerViewModel.kt i przejdź do funkcji startTimer().
  2. W ramach startTimer() pobierz z systemu wystąpienie NotificationManager i wywołaj cancelNotifications().
//  EggTimerViewModel.kt
   //TODO Step 1.15 call cancel notification
    val notificationManager =
       ContextCompat.getSystemService(
            app,
            NotificationManager::class.java
        ) as NotificationManager
    notificationManager.cancelNotifications()       
  1. Uruchom aplikację i włącz minutnik.
  2. Gdy pojawi się powiadomienie, ponownie włącz licznik czasu i sprawdź, jak nasza aplikacja automatycznie usuwa poprzednie powiadomienie z paska stanu.

Platforma powiadomień daje programistom różne opcje dostosowywania działań niestandardowych i stylu ich powiadomień. Z tego zadania dowiesz się, jak dostosować powiadomienia minutnika jaj.

Krok 1. Dostosuj styl powiadomienia

Wygląd powiadomienia możesz dostosować do swoich potrzeb, a jego treść sprawi, że powiadomienia będą się wyróżniać i wyglądają jak rozszerzenie aplikacji. Platforma powiadomień zawiera kilka wbudowanych stylów, które mogą pomóc w tworzeniu kreacji.

NotificationCompat ma wbudowane style dla:

  • BigTextStyle, która może wyświetlać duży blok tekstu, na przykład po rozwinięciu treści e-maila.
  • BigPictureStyle, który wyświetla powiadomienia o dużym formacie zawierające duży załącznik.
  • InboxStyle, która zawiera tekst rozmowy.
  • MediaStyle, który zawiera elementy sterujące odtwarzaniem multimediów.
  • MessagingStyle, gdzie wyświetlane są powiadomienia w dużym formacie zawierające wiele wiadomości wysyłanych przez dowolną liczbę osób.

Więcej informacji o innych stylach znajdziesz w dokumentacji dotyczącej tworzenia powiadomień rozwijanych. W tym kroku użyjesz NotificationCompat.BigPictureStyle, aby utworzyć rozwijane powiadomienie, które po rozwinięciu będzie wyświetlać duże zdjęcie jajka.

  1. Otwórz NotificationUtils.kt i znajdź funkcję sendNotification().
  2. Zacznij od wczytania obrazu ze strony resources za pomocą BitmapFactory.
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
  1. Utwórz nowy plik BigPictureStyle i ustaw zdjęcie.
  2. Ustaw bigLargeIcon() jako null, aby po rozwinięciu powiadomienia zniknęła duża ikona.
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
        .bigPicture(eggImage)
        .bigLargeIcon(null)
  1. Ustaw styl z setStyle() na bigPicStyle.
  2. Ustaw dużą ikonę setLargeIcon() na eggImage, aby po zwinięciu powiadomienia było ono wyświetlane jako mniejsza ikona.
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
  1. Uruchom aplikację i ustaw minutnik. Gdy powiadomienie jest wyświetlane po raz pierwszy, jest wyświetlane w szufladzie powiadomień w stanie zwiniętym. Gdy rozwiniesz powiadomienie, w obszarze rozwiniętym pojawi się duży obraz.

Krok 2. Powiadomienie

Działania związane z powiadomieniami to kolejne dostosowanie, które możesz dostosować do swoich powiadomień. Powiadomienia są obecnie przekierowywane do aplikacji po kliknięciu jej przez użytkownika. Oprócz tego domyślnego działania powiadomień możesz dodać przyciski, które pozwalają wykonywać zadania związane z powiadomieniami z poziomu powiadomienia.

Powiadomienia mogą zawierać maksymalnie 3 przyciski czynności, które umożliwiają użytkownikowi szybką reakcję, na przykład odłożenie przypomnienia lub wysłanie SMS-a. Te przyciski działań nie powinny powielać czynności wykonywanych, gdy użytkownik kliknie powiadomienie.

Aby dodać przycisk działania, przekaż PendingIntent do funkcji addAction() w kreatorze. To działanie jest podobne do konfigurowania domyślnego działania powiadomienia przez wywołanie funkcji setContentIntent(). Różnica polega na tym, że zamiast uruchamiać aktywność, możesz wykonać inne czynności, na przykład uruchomić BroadcastReceiver, który wykonuje zadanie w tle, aby nie przerywać działania aplikacji, która jest już otwarta.

W ramach tych ćwiczeń masz już BoadcastReceiver o nazwie SnoozeReceiver. Wykorzystasz SnoozeReceiver, aby poprosić użytkownika o kliknięcie działania powiadomienia. W kolejnych krokach dodasz kod, który spowoduje odłożenie powiadomienia minutnika na 60 sekund po kliknięciu przycisku drzemki. Po kliknięciu działania drzemki SnoozeReceiver otrzyma intencję i utwórz nowy alarm, aby po 60 sekundach wysłać nowe powiadomienie.

  1. Otwórz aplikację SnoozeReceiver.kt. Te zajęcia są podobne do tych, których używałeś wcześniej w ramach: AlarmReceiver. W kolejnych krokach dodasz kod, który wywoła funkcję onReceive() elementu SnoozeReceiver. Krótko mówiąc, kod w usłudze SnoozeReceiver utworzy nowy alarm, który pozwoli wysłać nowe powiadomienie minutę później. Przewiń widok w dół funkcji onReceive, pobierz wystąpienie powiadomienia z Menedżera systemu i wywołaj funkcję anulowaćAll.
// SnoozeReceiver.kt
        val notificationManager = ContextCompat.getSystemService(
            context,
            NotificationManager::class.java
        ) as NotificationManager
        notificationManager.cancelAll()
  1. Aby użyć aplikacji SnoozeReceiver, otwórz aplikację NotificationUtils.kt.
  2. Utwórz nowy Intent snoozeIntent dla elementu SnoozeReceiver tuż za stylem w funkcji sendNotification().
  3. Utwórz oczekującą intencję, wywołując metodę getBroadcast() w obiekcie PendingIntent, która wymaga parametrów wymienionych w kolejnych krokach. System PendingIntent będzie używać tego systemu do konfigurowania nowego alarmu w celu opublikowania nowego powiadomienia po 60 sekundach po kliknięciu przez użytkownika przycisku drzemki.
  4. Pierwszy parametr to kontekst aplikacji, w którym PendingIntent ma rozpoczynać działanie.
  5. Drugi parametr to kod żądania, czyli kod żądania dla tej intencji. Jeśli chcesz zaktualizować lub anulować oczekującą intencję, musisz użyć tego kodu, aby uzyskać dostęp do oczekującej intencji.
  6. Następnie dodaj obiekt snoozeIntent będący celem uruchamiania aktywności.
  7. Na koniec dodaj wartość flagi #FLAG_ONE_SHOT, ponieważ intencja zostanie użyta tylko raz. Szybkie działanie i powiadomienie znikną po pierwszym kliknięciu. Z tego powodu intencji można użyć tylko raz.
// NotificationUtils.kt

// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
    applicationContext, 
    REQUEST_CODE, 
    snoozeIntent, 
    FLAGS
)
  1. Następnie wywołaj funkcję addAction() w notificationBuilder. Ta funkcja oczekuje, że ikona i tekst opisują działanie użytkownika. Musisz też dodać snoozeIntent. Ta intencja zostanie użyta do wywołania odpowiedniego elementu boadcastReceiver po kliknięciu działania.
// NotificationUtils.kt
// TODO: Step 2.3 add snooze action
.addAction(
    R.drawable.egg_icon, 
    applicationContext.getString(R.string.snooze),
    snoozePendingIntent
)
  1. Uruchom aplikację minutnika, aby włączyć drzemkę.
  2. Uruchom minutnik i umieść aplikację w tle. Po upływie tego czasu rozwiń powiadomienie, a zobaczysz, że zawiera on przycisk drzemki, który włączy drzemkę na następną minutę.

Krok 3. Znaczenie powiadomienia

Znaczenie określa, w jakim stopniu powiadomienie powinno zakłócić wzrok i dźwięk. Powiadomienia o większym znaczeniu będą bardziej uciążliwe dla użytkowników.

Musisz określić poziom ważności w konstruktorze NotificationChannel. Ta aplikacja była bardzo mała. Możesz użyć jednego z pięciu poziomów ważności: od IMPORTANCE_NONE(0) do IMPORTANCE_HIGH(4). Poziom ważności przypisany kanałowi ma zastosowanie do wszystkich publikowanych na nim wiadomości z powiadomieniami.

Poziomy ważności kanału

Poziom ważności widoczny dla użytkowników

Ważność (Android 8.0 i nowsze)

Priorytet (Android 7.1 i starsze)

Wydaje dźwięk i wyświetla się jako powiadomienie z ostrzeżeniem (pojawia się w górnej części ekranu)

IMPORTANCE_HIGH

PRIORITY_HIGH/PRIORITY_MAX

Wydaje dźwięk

IMPORTANCE_DEFAULT

PRIORITY_DEFAULT

Brak dźwięku

IMPORTANCE_LOW

PRIORITY_LOW

Brak dźwięku i nie pojawia się na pasku stanu

IMPORTANCE_MIN

PRIORITY_MIN

Więcej informacji o wybieraniu odpowiedniego poziomu priorytetu znajdziesz w Przewodniku po projektowania powiadomień. Zachowaj ostrożność, wybierając poziom ważności powiadomień w aplikacji. Ważne jest, aby wybrane kanały były uwzględniane pod kątem czasu i uwagi użytkownika. Nieistotne powiadomienie udające się jako pilne, może wywołać niepotrzebne alarmy i rozproszyć uwagę. Użytkownicy mają pełną kontrolę nad poziomem ważności powiadomień, więc jeśli tworzysz irytujące powiadomienie, mogą oni całkowicie wyłączyć Twój kanał powiadomień.

Gdy po raz pierwszy udało Ci się utworzyć powiadomienie w kroku 1.6, licznik jaj został ustawiony, by wysyłać powiadomienia o niskim priorytecie, bo nie ma ono wpływu na powiadomienia użytkownika. Dobrym pomysłem może być jednak przyciągnięcie uwagi użytkownika, zanim jajko się nie zsunie. Aby zmienić poziom ważności powiadomienia, zacznij od ustawień kanału. Znaczenie kanału ma wpływ na poziom wszystkich powiadomień publikowanych na kanale i musi być określony w konstruktorze NotificationChannel.

  1. Aby zmienić poziom ważności kanału powiadomień aplikacji, otwórz EggTimerFragment.kt i przejdź na stronę createChannel(). Zmień poziom ważności z IMPORTANCE_LOW na IMPORTANCE_HIGH.
// EggTimerFragment.kt
    val notificationChannel = NotificationChannel(
        channelId,
        channelName,
        // TODO: Step 2.4 change importance
        NotificationManager.IMPORTANCE_HIGH
    )

Aby obsługiwać urządzenia z Androidem 7.1 (poziom API 25) lub starszym, musisz też wywołać setPriority() dla każdego powiadomienia, używając stałej priorytetu klasy NotificationCompat.

  1. Otwórz NotificationUtils.kt i dodaj do obiektu narzędzia do tworzenia powiadomień:
// NotificationUtils.kt
   .addAction(
       R.drawable.common_google_signin_btn_icon_dark,
       applicationContext.getString(R.string.snooze),
       snoozePendingIntent
    )
   // TODO: Step 2.5 set priority
    .setPriority(NotificationCompat.PRIORITY_HIGH)
  1. Zanim uruchomisz aplikację, przytrzymaj jej ikonę na urządzeniu lub w emulatorze i wybierz Odinstaluj, by wyczyścić poprzednie ustawienia kanału. Jeśli nie odinstalujesz aplikacji, ustawienia priorytetu kanału nie ulegną zmianie i nie spowoduje to zmiany działania po opublikowaniu powiadomienia.
  2. Ponownie uruchom aplikację i włącz minutnik. Tym razem po wysłaniu powiadomienia u góry ekranu powinno pojawić się wyskakujące okienko, niezależnie od tego, czy działa ono na pierwszym planie czy w tle.

Krok 4. Plakietki powiadomień

Plakietki powiadomień to małe kropki wyświetlane na ikonie programu uruchamiającego powiązanej aplikacji, gdy aplikacja ma aktywne powiadomienie. Użytkownicy mogą przycisnąć i przytrzymać ikonę aplikacji, aby wyświetlić powiadomienia.

Te plakietki, zwane plakietkami, są wyświetlane domyślnie. Nie musisz nic robić w aplikacji. Może się jednak zdarzyć, że plakietki nie mają sensu dla powiadomień. Możesz je wyłączyć w poszczególnych kanałach, wywołując setShowBadge(false) na obiekcie NotificationChannel. Minutnik jajka ma tylko jedno aktywne powiadomienie w danym momencie, dlatego plakietka na ikonie aplikacji nie przynosi żadnych korzyści użytkownikom. W kolejnych krokach wyłączymy plakietkę i wyświetlimy tylko powiadomienie dotyczące minutnika jaj.

  1. Dodaj setShowBadge(false) do kodu tworzenia kanału, aby wyłączyć plakietki w przypadku licznika czasu.
// EggTimerFragment.kt

    ).apply {
        // TODO: Step 2.6 disable badges for this channel
        setShowBadge(false)
    }
  1. Ponownie uruchom aplikację, włącz minutnik i obejrzyj jej ikonę. Na ikonie aplikacji nie powinny być widoczne żadne odznaki.

Kod rozwiązania znajduje się w gałęzi głównej pobranego kodu.

Kurs Udacity:

Dokumentacja dla programistów Androida:

Linki do innych ćwiczeń z programowania znajdziesz w kursie dotyczącym programowania na Androida dla zaawansowanych w Kotlin.