Te warsztaty są częścią kursu Zaawansowany Android w Kotlinie. Najwięcej korzyści z tego kursu uzyskasz, jeśli przejdziesz wszystkie ćwiczenia w kolejności, ale nie jest to obowiązkowe. Wszystkie ćwiczenia z tego kursu znajdziesz na stronie docelowej ćwiczeń z zaawansowanego Androida w Kotlinie.
Wprowadzenie
Powiadomienia to wiadomości wyświetlane użytkownikowi poza interfejsem aplikacji. Powiadomienia są wyświetlane u góry ekranu, gdy urządzenie jest odblokowane, lub na ekranie blokady, gdy urządzenie jest zablokowane (w zależności od ustawień zabezpieczeń).
Typowe powiadomienie składa się z tytułu, opisu i ikony. Powiadomienie może też zawierać klikalne działania, szybką odpowiedź, rozwijane treści i obrazy.
Powiadomienia mogą dostarczać aktualne informacje i zawierać przyciski umożliwiające użytkownikowi szybkie wykonywanie działań, takich jak wysyłanie odpowiedzi czy wyłączanie alarmu. Kliknięcie powiadomienia przenosi użytkownika do widoku w aplikacji związanego z treścią powiadomienia.
Powiadomienia to przydatny sposób na przypominanie użytkownikom o ważnym zadaniu, informowanie ich o tym, co się wydarzyło, lub przekazywanie ważnych informacji, których potrzebują natychmiast, gdy aplikacja działa w tle. Oszczędnie korzystaj z powiadomień. W ten sposób nie tylko szanujesz użytkowników, ale też zwiększasz prawdopodobieństwo, że powiadomienie z Twojej aplikacji przyciągnie ich uwagę.
Z tego ćwiczenia w Codelabs dowiesz się, jak tworzyć i używać powiadomień w aplikacji na Androida.
Co warto wiedzieć
Musisz znać:
- Jak tworzyć aplikacje na Androida w Kotlinie. W szczególności pracuj z pakietem Android SDK.
- Jak projektować aplikacje przy użyciu komponentów architektury i powiązania danych.
- Podstawowa znajomość BroadcastReceiver
- Podstawowa znajomość AlarmManager
Czego się nauczysz
- Jak utworzyć, ostylować i wysłać powiadomienie.
- Jak anulować powiadomienia.
- Jak tworzyć kanały powiadomień.
- Jak dodawać szybkie działania do powiadomień.
- Jak wyświetlać plakietki powiadomień na ikonie aplikacji.
Jakie zadania wykonasz
- Dodaj powiadomienie do aplikacji początkowej.
- anulować 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 było interaktywne.
- Wyłącz plakietki powiadomień.
Gotowanie jajek jest proste, ale może być trudne, jeśli nie będziesz śledzić czasu. W tym module z kodem będziesz pracować nad aplikacją do odmierzania czasu gotowania jajek i dopracowywać ją, aby była tak idealna jak Twoje przyszłe jajka. Zaczniesz od działającej aplikacji do odmierzania czasu gotowania jajek, która umożliwia użytkownikowi ustawianie różnych czasów gotowania dla różnych rodzajów jajek. Timer odlicza czas od wybranego przedziału czasu i wyświetla komunikat, gdy jajka są gotowe.
Może się to wydawać funkcjonalne, ale jest dalekie od doskonałości i nie jest zbyt przyjazne dla użytkownika. Po pierwsze, komunikat toast jest wyświetlany tylko przez krótki czas, więc łatwo go przeoczyć. Po drugie, jeśli aplikacja nie jest na pierwszym planie lub urządzenie jest zablokowane, po zniknięciu komunikatu toast nie ma wizualnego wskaźnika stanu timera.
Najlepiej, aby minutnik używał powiadomień, aby informować użytkowników o upływie czasu. Użytkownik musi natychmiast wiedzieć, że jajka są gotowe, w przeciwnym razie będą rozgotowane. Powiadomienia są wizualne, mogą zawierać dźwięki i powodować wibracje urządzenia – wszystko to ma na celu przyciągnięcie uwagi użytkownika. W ten sposób uzyskasz idealne jajka i zadowolonych, najedzonych użytkowników.
Aby pobrać przykładową aplikację, możesz wykonać jedną z tych czynności:
Sklonuj repozytorium z GitHub 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.
- Otwórz i uruchom aplikację w Android Studio.
Zobaczysz obrazek jajka i menu z listą wstępnie zdefiniowanych przedziałów czasu gotowania jajka. Kliknij trójkąt w menu Jajko na miękko. Pierwsza opcja na liście służy do celów testowych i ustawia alarm na 10 sekund. Obok listy znajduje się przełącznik, który uruchamia minutnik. Możesz go używać do włączania i wyłączania minutnika w dowolnym momencie. Kod początkowy jest w pełni funkcjonalny, co oznacza, że możesz ustawić minutnik i obserwować, jak odlicza czas do 0. Po zakończeniu odliczania wyświetli się komunikat w formie wyskakującego okienka, jak pokazano poniżej.
- Sprawdź kod źródłowy. Aplikacja początkowa składa się z jednej aktywności o nazwie
MainActivity
. Istnieją 3 pakiety podrzędne o nazwachreceiver
,ui
iutil
.
- /receiver –
receiver
pakiet zawiera 2 odbiorniki transmisji o nazwachAlarmReceiver
iSnoozeReceiver
.AlarmReceiver
jest wywoływany przezAlarmManager
, aby wysłać powiadomienie po upływie czasu określonego przez użytkownika.SnoozeReceiver
obsługuje kliknięcie użytkownika, aby odłożyć powiadomienie. - /ui – zawiera
EggTimerFragment
, który jest częścią interfejsu aplikacji.EggTimerViewModel
odpowiada za uruchamianie i anulowanie timera oraz inne zadania aplikacji związane z cyklem życia. - /util – w tym pakiecie znajdują się 2 pliki.
BindingUtils.kt
ma adaptery powiązań, które umożliwiają powiązanie danych między interfejsem aplikacji aViewModel
.NotificationUtils.kt
ma metody rozszerzeń wNotificationManager
.
Powiadomienia to świetny sposób na zwrócenie uwagi użytkowników na Twoją aplikację. Niezależnie od tego, czy aplikacja jest uruchomiona, czy nie, powiadomienie wyświetli wyskakujące okienko u góry ekranu i może zawierać dźwięk lub wibracje. Aby utworzyć powiadomienie, musisz użyć narzędzia do tworzenia powiadomień i podać tekst tytułu, tekst treści oraz ikonę. Gdy kreator będzie miał wszystkie niezbędne pola, NotificationManager
, czyli usługa systemowa, pomoże Ci wyświetlić te treści w formie powiadomienia. NotificationManager
odpowiada za wysyłanie powiadomień, aktualizowanie ich treści i anulowanie powiadomień. W kolejnych krokach dodasz do NotificationManager
metody rozszerzające. Dzięki temu za każdym razem, gdy będziesz potrzebować NotificationManager
, możesz użyć tych funkcji rozszerzających, aby uzyskać potrzebną funkcjonalność.
Krok 1. Utwórz podstawowe powiadomienie
W tym zadaniu utworzysz nowe powiadomienie, ustawisz wiadomość dla użytkownika i wyślesz powiadomienie.
- Otwórz zajęcia
NotificationUtils.kt
i znajdźTODO: Step 1.1
. W tym ćwiczeniu i w kodzie aplikacji znajdziesz pasujące zadania do wykonania. - Sprawdź podaną funkcję
sendNotification()
. Rozszerzasz tę funkcję rozszerzenia naNotificationManager
, 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) {
- Uzyskaj instancję narzędzia do tworzenia powiadomień, przekaż kontekst aplikacji i identyfikator kanału. Identyfikator kanału to wartość ciągu znaków dla kanału.
Kanały powiadomień to sposób grupowania powiadomień. Grupując podobne typy powiadomień, deweloperzy i użytkownicy mogą zarządzać wszystkimi powiadomieniami na kanale. Po utworzeniu kanału można go używać do dostarczania 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)
)
- Ustaw ikonę powiadomienia, która będzie reprezentować Twoją aplikację, tytuł i tekst treści wiadomości, którą chcesz przekazać użytkownikowi. W tym samouczku znajdziesz więcej opcji dostosowywania powiadomień, ale 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)
- Następnie musisz wywołać funkcję
notify()
z unikalnym identyfikatorem powiadomienia i obiektemNotification
z konstruktora.
Ten identyfikator reprezentuje bieżącą instancję powiadomienia i jest potrzebny do jego zaktualizowania lub anulowania. Aplikacja będzie mieć w danym momencie tylko jedno aktywne powiadomienie, więc możesz używać tego samego identyfikatora dla wszystkich powiadomień. W tym celu w pliku NotificationUtils.kt
masz już stałą o nazwie NOTIFICATION_ID
. Zwróć uwagę, że możesz bezpośrednio wywołać funkcję notify()
, ponieważ wywołujesz ją 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())
- Otwórz
ui/EggTimerViewModel.kt
i znajdź funkcjęstartTimer()
. Ta funkcja tworzy alarm z wybranym interwałem czasowym, gdy użytkownik włączy minutnik. - W tej funkcji wywołasz powiadomienie, gdy użytkownik uruchomi stoper. Aby wywołać zaimplementowaną wcześniej funkcję
sendNotification()
, potrzebujesz instancjiNotificationManager
.NotificationManager
to usługa systemowa, która udostępnia wszystkie funkcje interfejsu API powiadomień, w tym dodaną przez Ciebie funkcję rozszerzenia. Za każdym razem, gdy chcesz wysłać, anulować lub zaktualizować powiadomienie, musisz poprosić system o instancjęNotificationManager
. Wywołaj funkcjęsendNotification()|
z wiadomością powiadomienia 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)
Już prawie gotowe. Jeśli jednak uruchomisz teraz aplikację i ustawisz minutnik, nie otrzymasz powiadomienia.
- Otwórz
logcat
i wyszukaj"No Channel found"
. Powinien pojawić się komunikat o błędzie informujący, żeegg_channel
nie istnieje. W kolejnych krokach dowiesz się więcej o kanałach powiadomień i rozwiążesz ten problem.
Krok 2. Kanały powiadomień
Począwszy od interfejsu API na poziomie 26, wszystkie powiadomienia muszą być przypisane do kanału. Jeśli naciśniesz i przytrzymasz ikonę programu uruchamiającego aplikacje, 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 jeszcze żadnych kanałów.
Kanały reprezentują „rodzaj” powiadomienia – na przykład minutnik może wysłać powiadomienie, gdy jajko będzie gotowe, a także użyć innego kanału, aby wysyłać codzienne powiadomienia przypominające o zjedzeniu jajek na śniadanie. Wszystkie powiadomienia w kanale są zgrupowane, a użytkownicy mogą konfigurować ustawienia powiadomień dla całego kanału. Dzięki temu użytkownicy mogą dostosowywać ustawienia powiadomień do rodzaju powiadomień, które ich interesują. Użytkownicy mogą na przykład wyłączyć powiadomienia o śniadaniu, ale nadal otrzymywać powiadomienia z timera.
Deweloperzy ustawiają początkowe ustawienia, ważność i zachowanie, które mają być stosowane do wszystkich powiadomień na kanale. Po skonfigurowaniu ustawień początkowych użytkownicy mogą je zastąpić.
W kroku 1.1 jako kanału powiadomień użyto egg_notification_channel_id
, więc teraz musisz utworzyć i dostosować ustawienia powiadomień oraz działanie tego kanału.
- Otwórz
EggTimerFragment.kt
i znajdź funkcjęcreateChannel()
. - Przekaż unikalny identyfikator kanału do konstruktora
NotificationChannel
. - Przekaż nazwę kanału powiadomień, która będzie widoczna dla użytkowników na ekranie Ustawienia.
- Jako ostatni parametr przekaż poziom ważności kanału powiadomień. Poziomy ważności omówimy w dalszej części tego laboratorium, więc na razie możesz użyć wartości
NotificationManag
er.IMPORTANCE_LOW
. - W obiekcie
notificationChannel
ustaw wartośćenableLights
na true. To ustawienie włącza światła, gdy wyświetlane jest powiadomienie. - W obiekcie
notificationChannel
ustaw kolorlightColor
na czerwony, aby wyświetlać czerwone światło, gdy pojawi się powiadomienie. - W obiekcie
notificationChannel
ustaw wartośćenableVibration
na true, aby włączyć wibracje. - W obiekcie
notificationChannel
ustaw opis kanału na‘Time for breakf
ast'. - Uzyskaj instancję
NotificationManager
, dzwoniąc pod numergetSystemService()
. - Wywołaj funkcję
createNotificationChannel()
w obiekcieNotificationManager
i przekaż obiektnotificationChannel
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
}
- Następnie, aby utworzyć kanał, musisz wywołać napisaną wcześniej funkcję
createChannel()
(krok 1.7). Ta funkcja przyjmuje 2 parametry: identyfikator kanału i nazwę kanału. Musisz wyszukać identyfikator i nazwę kanału w zasobach ciągów tekstowych, które są już dostępne 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)
)
- Musisz przekazać identyfikator kanału do narzędzia do tworzenia powiadomień. Zostało to już zrobione w kroku 1.2. Ustawienie nieprawidłowej wartości jako identyfikatora kanału spowoduje niepowodzenie powiadomienia. 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)
)
- Uruchom aplikację. Zobaczysz, że za każdym razem, gdy włączysz minutnik, aplikacja wysyła powiadomienie.
- Pociągnij pasek stanu i sprawdź, czy tytuł, treść i ikona powiadomienia są takie, jak ustawiono w poprzednich krokach.
- Aby sprawdzić nowo utworzony kanał, zamknij aplikację i znajdź jej ikonę. Przytrzymaj ikonę aplikacji i wybierz Informacje o aplikacji.
- Na liście ustawień wybierz Powiadomienia. Pod ustawieniem Wyświetlaj powiadomienia powinien pojawić się nowy kanał o nazwie Egg.
Gdy uruchomisz aplikację, powiadomienie zostanie wyświetlone. Zarówno Ty jako deweloper aplikacji, jak i użytkownicy możecie dostosowywać ustawienia i zachowanie wszystkich powiadomień wysyłanych na tym kanale. Gratulujemy, udało Ci się utworzyć powiadomienie.
Krok 3. Dodaj powiadomienia do aplikacji
To pokazuje podstawowe użycie interfejsu Notifications API, ale wysyłanie powiadomienia zaraz po uruchomieniu timera nie ma większego sensu. Użytkownicy prawdopodobnie wolą otrzymywać powiadomienia, gdy jajko jest gotowe. W dalszej części tego kursu naprawisz ten problem i zmienisz komunikat toast na powiadomienie.
Powiadomienie zostało już wysłane i możesz obserwować, jak jest wyświetlane użytkownikom. To jednak dopiero pierwszy krok do tworzenia świetnych powiadomień. W tym kroku zmienisz godzinę wysyłania powiadomienia na bardziej odpowiednią.
Twoja aplikacja używa AlarmManager
do ustawiania alarmu. Kod związany z AlarmManager
jest już podany w kodzie początkowym i używany do wyświetlania wiadomości toast. AlarmManager
śledzi wybrany czas i po jego upływie uruchamia funkcję onReceive()
elementu AlarmReceiver.kt
. Jeśli otworzysz AlarmReceiver.kt
i przejdziesz do onReceive()
, zobaczysz komunikat, który wyświetla się za każdym razem, gdy ustawiasz minutnik.
- Otwórz
AlarmReceiver.kt
, instancjęNotificationManager
, i wywołaj funkcjęsendNotification()
z tekstem wiadomości i parametrami kontekstu.
// 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
)
- Opcjonalnie możesz usunąć wyskakujące okienko, ponieważ aplikacja będzie wysyłać powiadomienie po upływie czasu.
// AlarmReceiver.kt
// TODO: Step 1.10 [Optional] remove toast
// Toast.makeText(
// context,
// context.getText(R.string.eggs_ready),
// Toast.LENGTH_SHORT
// ).show()
- Uruchom aplikację . Powiadomienie powinno pojawiać się za każdym razem, gdy włączysz minutnik, i za każdym razem, gdy upłynie czas.
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 stoper.
- Otwórz
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)
- Ponownie uruchom aplikację.
- Ustaw minutnik, włącz go w tle i poczekaj, aż czas się skończy. Zobaczysz powiadomienie. To znacznie bardziej przydatne powiadomienie.
Krok 4. Dodaj intencję dotyczącą treści
- Uruchom ponownie aplikację, jeśli nie jest jeszcze uruchomiona.
- Kliknij powiadomienie. Nic się nie dzieje.
Wyświetlanie powiadomienia i informowanie użytkownika to świetna sprawa, ale gdy użytkownik kliknie powiadomienie, oczekuje, że wróci do odpowiedniej aplikacji. W tej części laboratorium dodasz do powiadomienia intencję, aby użytkownik mógł wrócić do ekranu timera.
Intent
to obiekt wiadomości, którego możesz użyć, aby poprosić inny komponent aplikacji o wykonanie działania. Intencje mogą służyć do uruchamiania aktywności lub usługi albo do dostarczania transmisji. W takim przypadku używasz tego zamiaru, aby poinformować system, że po kliknięciu powiadomienia przez użytkownika ma się otworzyć aplikacja MainActivity
. Twoja aplikacja składa się tylko z jednego widoku, więc nie masz tu wielu opcji. W przypadku większej aplikacji powiadomienie powinno zapewniać spójne wrażenia, przenosząc użytkownika na ekran, który ma sens w momencie interakcji z powiadomieniem.
- Otwórz
NotificationUtils.kt
i znajdź funkcję rozszerzeniasendNotification()
. - Utwórz
Intent
z użyciemapplicationContext
i aktywności, która ma zostać uruchomiona,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ł utworzony zamiar, ale powiadomienie jest wyświetlane poza aplikacją. Aby zamiar działał poza aplikacją, musisz utworzyć nowy PendingIntent
.
PendingIntent
przyznaje innej aplikacji lub systemowi uprawnienia do wykonywania operacji w imieniu Twojej aplikacji. PendingIntent
to po prostu odwołanie do tokena utrzymywanego przez system, który opisuje oryginalne dane użyte do jego pobrania. Oznacza to, że nawet jeśli proces aplikacji, do której należy PendingIntent
, zostanie zakończony, będzie można go używać w innych procesach, w których został udostępniony. W takim przypadku system użyje oczekującego zamiaru, aby otworzyć aplikację w Twoim imieniu, niezależnie od tego, czy aplikacja minutnika jest uruchomiona.
- Utwórz
PendingIntent
zapplicationContext
,NOTIFICATION_ID
,contentIntent
utworzonym w poprzednim kroku i flagąPendingIntent
. FlagaPendingIntent
określa opcję utworzenia nowegoPendingIntent
lub użycia istniejącego. Musisz ustawić flagęPendingIntent.FLAG_UPDATE_CURRENT
, ponieważ nie chcesz tworzyć nowego powiadomienia, jeśli istnieje już powiadomienie. W ten sposób zmodyfikujesz bieżącyPendingIntent
, który jest powiązany z dostarczanym przez Ciebie sygnałem o zamiarze.
// NotificationUtils.kt
// TODO: Step 1.12 create PendingIntent
val contentPendingIntent = PendingIntent.getActivity(
applicationContext,
NOTIFICATION_ID,
contentIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
- Przekaż
PendingIntent
do powiadomienia. Aby to zrobić, zadzwoń pod numersetContentIntent()
naNotificationBuilder
. Teraz, gdy klikniesz powiadomienie, uruchomi sięPendingIntent
i otworzy sięMainActivity
. - Ustaw też wartość
setAutoCancel()
natrue
, aby po kliknięciu powiadomienia przez użytkownika zostało ono zamknięte, a użytkownik został przekierowany do aplikacji.
// NotificationUtils.kt
// TODO: Step 1.13 set content intent
.setContentIntent(contentPendingIntent)
.setAutoCancel(true)
- Uruchom ponownie aplikację.
- Ustaw licznik czasu, umieść aplikację w tle i poczekaj na wyświetlenie powiadomienia.
- Gdy zobaczysz powiadomienie, kliknij je, przeciągając w dół pasek stanu, i zobacz, jak aplikacja przechodzi na pierwszy plan.
Krok 5. Anuluj powiadomienie
Masz działający minutnik z powiadomieniami, ale występuje drobny problem. Jeśli ustawisz minutnik, otrzymasz powiadomienie i ustawisz go ponownie, poprzednie powiadomienie pozostanie na pasku stanu, a nowy minutnik będzie działać. Może to wprowadzać zamieszanie, jeśli aplikacja działa w tle, i skutkować niedogotowaniem jajek.
Aby to naprawić, musisz wyczyścić poprzednie powiadomienie, gdy uruchamiasz nowy minutnik. Zacznij od utworzenia kolejnej funkcji rozszerzenia w pliku NotificationUtils.kt
. NotificationManager
ma interfejs API do anulowania wszystkich aktywnych powiadomień o nazwie cancelAll
()
.
- Otwórz pokój
NotificationsUtil.kt
. - Dodaj funkcję rozszerzenia w
NotificationManager
, która wywołujecancelAll()
.
// NotificationUtils.kt
// TODO: Step 1.14 Cancel all notifications
/**
* Cancels all notifications.
*
*/
fun NotificationManager.cancelNotifications() {
cancelAll()
}
- Otwórz
EggTimerViewModel.kt
i przejdź do funkcjistartTimer()
. - W
startTimer()
pobierz instancjęNotificationManager
z systemu i wywołajcancelNotifications()
.
// EggTimerViewModel.kt
//TODO Step 1.15 call cancel notification
val notificationManager =
ContextCompat.getSystemService(
app,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelNotifications()
- Uruchom aplikację i włącz timer.
- Po wyświetleniu powiadomienia ponownie uruchom stoper i obserwuj, jak nasza aplikacja automatycznie usuwa poprzednie powiadomienie z paska stanu.
Platforma powiadomień udostępnia deweloperom różne opcje dostosowywania, dzięki którym mogą oni ustawiać niestandardowe działania i w razie potrzeby zmieniać styl powiadomień. Z tego zadania dowiesz się, jak dostosować powiadomienia minutnika.
Krok 1. Nadaj powiadomieniu styl
Dostosowanie stylu powiadomienia do Twoich potrzeb i jego treści sprawi, że będzie się ono wyróżniać i wyglądać bardziej jak rozszerzenie aplikacji. Platforma powiadomień ma kilka wbudowanych stylów, które mogą Ci pomóc, ale zawsze możesz utworzyć własne.
NotificationCompat
oferuje wbudowane style dla:
BigTextStyle
, który może wyświetlać duży blok tekstu, np. zawartość e-maila po rozwinięciu.BigPictureStyle
, który wyświetla powiadomienia w dużym formacie z dużym załącznikiem w postaci obrazu.InboxStyle
, który zawiera tekst w formie rozmowy.MediaStyle
, w którym znajdują się elementy sterujące odtwarzaniem multimediów.MessagingStyle
, w którym wyświetlane są powiadomienia w dużym formacie zawierające wiele wiadomości od dowolnej liczby osób.
Więcej informacji o innych stylach znajdziesz w dokumentacji tworzenia rozwijanego powiadomienia. W tym kroku użyjesz NotificationCompat.BigPictureStyle
, aby utworzyć rozwijane powiadomienie, które po rozwinięciu wyświetla duży obraz jajka.
- Otwórz
NotificationUtils.kt
i znajdź funkcjęsendNotification()
. - Zacznij od wczytania obrazu z
resources
za pomocąBitmapFactory
.
// NotificationUtils.kt
// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.cooked_egg
)
- Utwórz nowy
BigPictureStyle
i ustaw obraz. - Ustaw wartość
bigLargeIcon()
nanull
, aby duża ikona znikała po rozwinięciu powiadomienia.
// 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)
- Ustaw styl za pomocą
setStyle()
nabigPicStyle
. - Ustaw dużą ikonę z symbolem
setLargeIcon()
naeggImage
, aby obraz był wyświetlany jako mniejsza ikona po zwinięciu powiadomienia.
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
- Uruchom aplikację i ustaw minutnik. Gdy powiadomienie pojawi się po raz pierwszy, będzie zwinięte w panelu powiadomień. Jeśli rozwiniesz powiadomienie, w obszarze rozwiniętego powiadomienia pojawi się duży obraz.
Krok 2. Działania powiadomień
Działania związane z powiadomieniami to kolejne dostosowanie, które możesz dodać do powiadomień. Obecnie powiadomienia przekierowują użytkowników do Twojej aplikacji, gdy klikną oni powiadomienie. Oprócz tego domyślnego działania powiadomienia możesz dodać przyciski poleceń, które wykonują zadanie związane z aplikacją z poziomu powiadomienia.
Powiadomienie może zawierać maksymalnie 3 przyciski działań, które umożliwiają użytkownikowi szybką reakcję, np. odłożenie przypomnienia lub odpowiedź na wiadomość tekstową. Te przyciski nie powinny powielać działania wykonywanego po kliknięciu powiadomienia przez użytkownika.
Aby dodać przycisk działania, przekaż PendingIntent
do funkcji addAction()
w narzędziu do tworzenia. Działa to podobnie jak ustawianie domyślnego działania po kliknięciu powiadomienia przez wywołanie metody setContentIntent()
, z tym że zamiast uruchamiać aktywność, możesz wykonać różne inne czynności, np. uruchomić BroadcastReceiver
, która wykonuje zadanie w tle, dzięki czemu działanie nie przerywa pracy już otwartej aplikacji.
W tym laboratorium masz już BoadcastReceiver
o nazwie SnoozeReceiver
. Aby otrzymać kliknięcie działania powiadomienia przez użytkownika, użyj SnoozeReceiver
. W kolejnych krokach dodasz kod, który spowoduje odłożenie powiadomienia o zakończeniu odliczania czasu przez minutnik na 60 sekund, gdy użytkownik kliknie przycisk odkładania. Gdy klikniesz opcję odłożenia, SnoozeReceiver
otrzyma intencję i utworzy nowy alarm, aby po 60 sekundach wysłać nowe powiadomienie.
- Otwórz pokój
SnoozeReceiver.kt
. Ta klasa jest podobna do klasyAlarmReceiver
, której używasz. W kolejnych krokach dodasz kod, który wywoła funkcjęonReceive()
wSnoozeReceiver
. Krótko mówiąc, kod wSnoozeReceiver
utworzy nowy alarm, który za minutę wyśle nowe powiadomienie. Przewiń w dół do funkcji onReceive, pobierz z systemu instancję notificationManager i wywołaj cancelAll.
// SnoozeReceiver.kt
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelAll()
- Aby użyć
SnoozeReceiver
, otwórzNotificationUtils.kt
. - Utwórz nowy element
Intent
snoozeIntent
dla elementuSnoozeReceiver
tuż po stylu w funkcjisendNotification()
. - Utwórz oczekujący zamiar, wywołując metodę
getBroadcast()
w obiekciePendingIntent
, która oczekuje parametrów opisanych w kolejnych krokach. TenPendingIntent
będzie używany przez system do ustawiania nowego alarmu, który po 60 sekundach od naciśnięcia przez użytkownika przycisku drzemki wyśle nowe powiadomienie. - Pierwszy parametr to kontekst aplikacji, w którym
PendingIntent
ma rozpocząć działanie. - Drugi parametr to kod żądania, czyli kod żądania dla tego oczekującego zamiaru. Jeśli chcesz zaktualizować lub anulować tę oczekującą intencję, musisz użyć tego kodu, aby uzyskać do niej dostęp.
- Następnie dodaj obiekt
snoozeIntent
, który jest intencją działania, które ma zostać uruchomione. - Na koniec dodaj wartość flagi
#FLAG_ONE_SHOT
, ponieważ intencja będzie używana tylko raz. Szybka czynność i powiadomienie znikną po pierwszym kliknięciu, dlatego 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
)
- Następnie wywołaj funkcję
addAction()
na obiekcienotificationBuilder
. Ta funkcja oczekuje ikony i tekstu opisującego działanie dla użytkownika. Musisz też dodaćsnoozeIntent
. Ta intencja będzie używana do wywoływania odpowiedniegoboadcastReceiver
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
)
- Uruchom aplikację z minutnikiem, aby przetestować funkcję drzemki.
- Uruchom stoper i przełącz aplikację w tryb tła. Gdy czasomierz się wyłączy, rozwiń powiadomienie. Zobaczysz, że zawiera ono teraz przycisk odkładania, który odłoży czasomierz na kolejną minutę.
Krok 3. Ważność powiadomień
Ważność określa, w jakim stopniu powiadomienie powinno wizualnie i dźwiękowo przeszkadzać użytkownikowi. Powiadomienia o większym znaczeniu będą bardziej przeszkadzać użytkownikom.
Poziom ważności musisz określić w konstruktorze NotificationChannel
. Dla aplikacji minutnika ustawiono pierwotnie niski poziom ważności. Możesz użyć jednego z 5 poziomów ważności w zakresie od IMPORTANCE_NONE(0)
do IMPORTANCE_HIGH(4)
. Poziom ważności przypisany do kanału dotyczy wszystkich wiadomości z powiadomieniami, które na nim publikujesz.
Poziomy ważności kanału
Poziom ważności widoczny dla użytkownika | Ważność (Android 8.0 lub nowszy) | Priorytet (Android 7.1 i starsze) |
Sygnalizacja dźwiękiem i wyświetlenie powiadomienia w formie wyskakującego okienka u góry ekranu | ||
Wydaje dźwięk | ||
Brak dźwięku | ||
Brak dźwięku i nie pojawia się na pasku stanu |
Informacje o wybieraniu odpowiedniego poziomu priorytetu znajdziesz w sekcji „Poziomy priorytetu” w przewodniku po projektowaniu powiadomień. Przy wyborze poziomu ważności powiadomień w aplikacji należy zachować ostrożność. Ważność kanału powinna być wybierana z uwzględnieniem czasu i uwagi użytkownika. Gdy nieistotne powiadomienie jest maskowane jako pilne, może wywoływać niepotrzebny alarm i rozpraszać uwagę. Użytkownicy mają pełną kontrolę nad poziomem ważności powiadomień, więc jeśli utworzysz irytujące powiadomienie, mogą całkowicie wyłączyć Twój kanał powiadomień.
Gdy w kroku 1.6 utworzono powiadomienie, minutnik został ustawiony tak, aby wysyłać powiadomienia o niskim priorytecie, ponieważ miał nie przeszkadzać użytkownikowi. Warto jednak zwrócić uwagę użytkownika, zanim jajko się przegotuje. Aby zmienić poziom ważności powiadomienia, zacznij od ustawień kanału. Ważność kanału wpływa na poziom przerwania wszystkich powiadomień publikowanych w kanale i musi być określona w konstruktorze NotificationChannel
.
- Aby zmienić poziom ważności kanału powiadomień aplikacji, otwórz
EggTimerFragment.kt
i przejdź docreateChannel()
. Zmień poziom ważności zIMPORTANCE_LOW
naIMPORTANCE_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 interfejsu API 25) lub starszym, musisz też wywołać setPriority()
dla każdego powiadomienia, używając stałej priorytetu z klasy NotificationCompat
.
- Otwórz
NotificationUtils.kt
i dodaj do obiektu kreatora powiadomień te elementy:
// 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)
- Przed uruchomieniem aplikacji przytrzymaj jej ikonę na urządzeniu lub emulatorze i kliknij Odinstaluj, aby wyczyścić poprzednie ustawienia kanału. Jeśli nie uda Ci się odinstalować aplikacji, ustawienia priorytetu kanału nie ulegną zmianie, co spowoduje, że po opublikowaniu powiadomienia nie nastąpi żadna zmiana w działaniu.
- Uruchom ponownie aplikację i włącz minutnik. Gdy powiadomienie zostanie dostarczone, u góry ekranu powinno pojawić się wyskakujące okienko, niezależnie od tego, czy aplikacja działa na pierwszym planie, czy w tle.
Krok 4. Kropki powiadomień
Kropki powiadomień to małe kropki, które pojawiają się na ikonie aplikacji w launcherze, gdy aplikacja ma aktywne powiadomienie. Użytkownicy mogą nacisnąć i przytrzymać ikonę aplikacji, aby wyświetlić powiadomienia.
Te kropki, zwane plakietkami, pojawiają się domyślnie i nie wymagają żadnych działań ze strony aplikacji. Mogą jednak wystąpić sytuacje, w których plakietki nie będą odpowiednie dla powiadomień. Możesz je wyłączyć dla poszczególnych kanałów, wywołując metodę setShowBadge(false)
na obiekcie NotificationChannel
. Ponieważ minutnik może mieć tylko jedno aktywne powiadomienie w danym momencie, plakietka na ikonie aplikacji nie przynosi użytkownikom wielu korzyści. W kolejnych krokach wyłączysz plakietkę i będziesz wyświetlać tylko powiadomienie o minutniku.
- Dodaj znak
setShowBadge(false)
do kodu tworzenia kanału dla minutnika, aby wyłączyć plakietki.
// EggTimerFragment.kt
).apply {
// TODO: Step 2.6 disable badges for this channel
setShowBadge(false)
}
- Uruchom ponownie aplikację, włącz minutnik i obserwuj ikonę aplikacji. Na ikonie aplikacji nie powinny być widoczne żadne plakietki.
Kod rozwiązania znajduje się w gałęzi głównej pobranego kodu.
- Użyj klasy NotificationManager, aby tworzyć, wysyłać, aktualizować i anulować powiadomienia.
- Użyj obiektu NotificationChannel z metodą createNotificationChannel, aby ustawić kanał powiadomień.
- Użyj metody addAction(), aby dodać skróty do powiadomienia.
- Użyj funkcji setShowBadge(), aby włączyć lub wyłączyć plakietki.
- Określ styl powiadomień za pomocą stylów, które rozszerzają Notification.Style.
- Ustaw poziom ważności za pomocą metody NotificationChannel.setImportance().
Kurs Udacity:
Dokumentacja dla deweloperów aplikacji na Androida:
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z zaawansowanego Androida w Kotlinie.