Android HOW TO: Jak wysłać powiadomienie?

Android HOWTO: Jak wysłać powiadomienie?

Powiadomienie to krótka wiadomość, która pozwala informować użytkownika o pewnych wydarzeniach pochodzących z aplikacji, sama będąc poza nią. Jest to bardzo potężne narzędzie. Wykorzystane w odpowiedni sposób może znacznie zwiększyć atrakcyjność naszej aplikacji.

Wszyscy przyzwyczailiśmy się już do tego mechanizmu. Wiele aplikacji na bieżąco zasypuje nas różnymi powiadomieniami. Trudno byłoby dziś wyobrazić sobie brak otrzymania informacji o nadesłanej wiadomości SMS/MMS, e-mail, czy powiadomienia o wydarzeniu z kalendarza.

W wersji Androida Jelly Bean bardzo rozbudowano system powiadomień udostępniając wiele ciekawych funkcjonalności takich jak:

  • ustawianie dodatkowych akcji dla powiadomienia,
  • nowy rozszerzony widok wiadomości mogący zawierać dodatkowe informacje, a nawet bitmapy,
  • ustawianie priorytetu powiadomienia,

Wszystkie te możliwości zaprezentuję w tym artykule na konkretnych przykładach. Jeśli jesteś zainteresowany włączeniem tego mechanizmu do swoich aplikacji, serdecznie zapraszam do lektury.

Zastosowanie

Najczęściej spotykane zastosowania powiadomień to informowanie użytkownika o:

  • nadejściu wiadomości tekstowej lub wiadomości e-mail,
  • nieodebranych połączeniach telefonicznych,
  • nadchodzącym wydarzeniu z kalendarza,
  • postępie pobierania danych z zewnętrznych źródeł,
  • aktualizacji aplikacji.

Typy powiadomień

Od wersji Androida 4.1 mamy już dwa typy prezentacji powiadomienia. Pierwszy to widok normalny, złożony z sześciu elementów.
noti_101

  1. tytuł powiadomienia
  2. duża ikony
  3. treść powiadomienia
  4. liczba zgrupowanych powiadomień
  5. mała ikona
  6. czas powiadomienia

Drugim typem powiadomienia jest tak zwany duży widok.
noti_102
Składa się on z tych samych elementów co widok normalny ale posiada dodatkowy obszar (7), który może zawierać szczegółowe informacje o wiadomości i może być wyświetlany, w zależności od zastosowania, w trzech trybach wizualnych:

  • Big picture style – prezentowanie bitmapy o wysokości do 256dp,
  • Big text style – wyświetlanie dużego bloku tekstu,
  • Inbox style – wyświetlanie tekstu w postaci oddzielnych linii.

Tworzenie prostego powiadomienia

W ramach prezentacji możliwości mechanizmu notyfikacji stworzymy jeden projekt, do którego będziemy kolejno dodawali nowe funkcjonalności.
Zaczynamy więc od stworzenia nowego projektu. Od razu po tej czynności przechodzimy do pliku layotu activity_main.xml, aby dodać w nim przycisk rozciągnięty na cały wyświetlacz urządzenia. Posłuży on nam jako wyzwalacz powiadomienia.

1
2
3
4
5
6
7
8
9
10
11
12
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >

    <Button
       android:id="@+id/btnCreateNotification"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:text="Create Notification" >
    </Button>
</LinearLayout>

Następnie w kodzie aktywności głównej MainActivity, implementujemy tworzenie powiadomienia. W zdarzeniu onCreate tej aktywności dodajemy obsługę przycisku tworzącego  i wysyłającego notyfikację.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnCreateNotification = (Button) findViewById(R.id.btnCreateNotification);
        btnCreateNotification.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                createNotification();
            }
        });
    }

Teraz implementujemy brakującą metodę wywoływaną zdarzeniem onClick powyższego przycisku.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    private void createNotification() {

        Intent intent = new Intent(this, ResultActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

        Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);

        Notification noti = new NotificationCompat.Builder(this)
        .setContentTitle("Nowa wiadomość")
        .setContentText("Temat wiadomości")
        .setTicker("Masz wiadomość")
        .setSmallIcon(android.R.drawable.ic_dialog_info)
        .setLargeIcon(icon)
        .setAutoCancel(true)
        .setContentIntent(pIntent)
        .build();

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        notificationManager.notify(0, noti);
    }

Zanim uruchomimy aplikację, przyjrzyjmy się utworzonej przed chwilą metodzie createNotification(). Zaczęliśmy od utworzenia intencji dla aktywności ResultActivity, której uruchomienie będzie reakcją na otrzymane powiadomienie. Następnie zapakowaliśmy tę intencję w intencję oczekującą. W takiej postaci przekażemy ją obiektowi notyfikacji.

1
2
Intent intent = new Intent(this, ResultActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

Dalej stworzyliśmy bitmapę, która będzie naszą dużą ikoną powiadomienia.

1
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);

Następnie przechodzimy do tworzenia obiektu powiadomienia

1
2
3
4
5
6
7
8
9
Notification noti = new NotificationCompat.Builder(this)
        .setContentTitle("Nowa wiadomość")
        .setContentText("Temat wiadomości")
        .setTicker("Masz wiadomość")
        .setSmallIcon(android.R.drawable.ic_dialog_info)
        .setLargeIcon(icon)
        .setAutoCancel(true)
        .setContentIntent(pIntent)
        .build();

Przyjrzyjmy się również poszczególnym parametrom, które ustawiliśmy:

  • setContentTitle() – tytuł wiadomości;
  • setContentText() – treść wiadomości;
  • setTicker() – krótki tekst wyświetlany przez krótką chwilę bezpośrednio po otrzymaniu wiadomości, jeszcze przed rozwinięciem panelu powiadomień;
  • setLargeIcon() – ustawienie dużej ikony;
  • setSmallIcon() – ustawienie małej ikony; jeśli nie będzie ustawiona duża ikona (setLargeIcon()), mała ikona będzie wyświetlana w polu dużej ikony, a pole małej ikony nie będzie wyświetlane; metoda jest obowiązkowa, bez niej wiadomość nie zostanie wysłana;
  • setAutoCancel() – jeśli ustawiona na true, po kliknięciu w powiadomienie zniknie ono automatycznie z listy, w przeciwnym razie pozostanie na liście, aż do ręcznego usunięcia;
  • setContentIntent() – podpięcie intencji oczekującej, której wywołanie zostanie uruchomione w wyniku kliknięcia w powiadomienie.

Utworzony w ten sposób obiekt wiadomości trzeba wysłać. Wykorzystujemy w tym celu NotificationManager.

1
2
3
4
NotificationManager notificationManager =
        (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(0, noti);

Pierwszy parametr metody notify(), to identyfikator powiadomienia. Jedno z jego zastosowań poznamy przy omawianiu grupowania powiadomień. Drugim parametrem jest obiekt wiadomości.

Skoro już trochę więcej wiemy, wreszcie możemy uruchomić aplikację i zobaczyć jej działanie w akcji.

noti_052

Kliknięcie w przycisk spowoduje wysłanie powiadomienia.

noti_002
noti_003

Najpierw, na górze ekranu urządzenia, zobaczymy krótki tekst, który ustawiliśmy poprzez metodę setTicker(). Po rozwinięciu panelu ujrzymy nasze powiadomienie. Kliknięcie w nie spowoduje, zgodnie z oczekiwaniami, przejście do aktywność ResultActivity naszej aplikacji.

noti_004

Tworzenie powiadomień z rozszerzonym widokiem

Dodajmy drugi przycisk, który wyzwoli notyfikację z rozszerzonym widokiem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >

    <Button
       android:id="@+id/btnCreateNotification"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_weight="1"      
       android:text="Create Notification" >
    </Button>
   
    <Button
       android:id="@+id/btnCreateBigNotification"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_weight="1"              
       android:text="Create Big Notification" >        
    </Button>  
</LinearLayout>

W metodzie onCreate uwzględnijmy obsługę nowego przycisku:

1
2
3
4
5
6
7
8
    Button btnCreateBigNotification = (Button) findViewById(R.id.btnCreateBigNotification);
    btnCreateBigNotification.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            createBigNotification();
        }
    });

Czas teraz zaimplementować metodę createBigNotification().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    protected void createBigNotification() {

        Intent intent = new Intent(this, ResultActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

        String[] msgPositions = new String[3];
        msgPositions[0] = "Pozycja 1";
        msgPositions[1] = "Pozycja 2";
        msgPositions[2] = "Pozycja 3";

        NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
        inboxStyle.setBigContentTitle("Pozycje wiadomości:");
        for (int i=0; i < msgPositions.length; i++) {
            inboxStyle.addLine(msgPositions[i]);
        }

        Notification noti = new NotificationCompat.Builder(this)
            .setContentTitle("Nowa wiadomość")
            .setContentText("Temat wiadomości")
            .setTicker("Masz wiadomość")
            .setAutoCancel(true)
            .setStyle(inboxStyle)
            .setSmallIcon(R.drawable.ic_launcher)
            .setContentIntent(pIntent)
            .build();

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        notificationManager.notify(1, noti);
    }

Uruchamiamy aplikację i tym razem wybieramy drugi przycisk. W efekcie otrzymamy powiadomienie z rozszerzonym widokiem.

noti_051
noti_012

Tworzenie powiadomień zawierających akcje

Wypróbujmy teraz kolejną funkcjonalność dodaną w wersji Jelly Bean, czyli akcje w powiadomieniach.

Zacznijmy od dodania nowej aktywność o nazwie ActionActivity, która będzie wywoływana poprzez akcję z powiadomienia. W jej pliku layoutu  zmieńmy tylko wyświetlany tekst dla elementu TextView.

1
2
3
4
    <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Aktywność uruchomiona z akcji powiadomienia" />

Następnie dodajmy trzeci przycisk do layoutu activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >

    <Button
       android:id="@+id/btnCreateNotification"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_weight="1"      
       android:text="Create Notification" >
    </Button>
   
    <Button
       android:id="@+id/btnCreateBigNotification"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_weight="1"              
       android:text="Create Big Notification" >        
    </Button>
   
    <Button
       android:id="@+id/btnCreateActionNotification"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_weight="1"          
       android:text="Create Notification with Action" >
    </Button>

</LinearLayout>

Analogicznie jak w poprzednich przykładach w metodzie onCreate aktywności MainActivity dodajmy obsługę nowego przycisku.

1
2
3
4
5
6
7
8
    Button btnCreateActionNotification = (Button) findViewById(R.id.btnCreateActionNotification);
        btnCreateActionNotification.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                createActionNotification();
            }
        });

Nasza metoda createActionNotification() będzie wyglądała następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    private void createActionNotification() {

        Intent intent = new Intent(this, ResultActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

        Intent actionIntent = new Intent(this, ActionActivity.class);
        PendingIntent pendingActionIntent = PendingIntent.getActivity(this, 0, actionIntent, 0);

        String url = "tel:123456789";
        Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
        PendingIntent pendingCallIntent = PendingIntent.getActivity(this, 0, callIntent, 0);

        Notification noti = new NotificationCompat.Builder(this)
        .setContentTitle("Nowa wiadomość")
        .setContentText("Temat wiadomości")
        .setTicker("Masz wiadomość")
        .setSmallIcon(R.drawable.ic_launcher)
        .setAutoCancel(true)
        .setContentIntent(pIntent)
        .addAction(android.R.drawable.ic_menu_call, "Call", pendingCallIntent)
        .addAction(R.drawable.ic_launcher, "Action", pendingActionIntent)
        .build();

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        notificationManager.notify(2, noti);
    }

Pamiętajmy, by dodać do manifestu uprawnienie do wykonywania połączeń telefonicznych.

1
<uses-permission android:name="android.permission.CALL_PHONE" />

Kolejne etapy działania dodanej przed chwilą funkcjonalności przedstawiają screeny poniżej.

noti_050
noti_022
noti_023
noti_025

Grupowanie powiadomień

Powiadomienia pochodzące z tego samego źródła powinny być grupowane, a nie wyświetlane każde osobno. Należy unikać sytuacji takiej, jak zaprezentowana poniżej.

noti_034

W celu rozpoznania, które wiadomości powinny być połączone, Android wykorzystuje identyfikator powiadomienia. Wspomniałem o nim krótko przy okazji wysyłania powiadomień (notificationManager.notify(0, noti);). W naszych przykładach każdy typ powiadomienia dostaje zawsze ten sam identyfikator, więc będą one automatycznie grupowane. Zobaczmy jednak, jak możemy zliczyć wszystkie połączone powiadomienia. Zachęcam również do własnych eksperymentów.

Potrzebny będzie nam licznik, który dodamy jako pole klasy MainActivity:

1
private int numMessages = 0;

Dodajmy teraz implementację działania tego licznika oraz wyświetlanie go w powiadomieniu. Służy do tego metoda setNumber().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    private void createNotification() {

        Intent intent = new Intent(this, ResultActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

        Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);

        int messageNumber = ++numMessages;

        Notification noti = new NotificationCompat.Builder(this)
        .setContentTitle("Nowa wiadomość")
        .setContentText("Wiadomość numer " + messageNumber)
        .setTicker("Masz wiadomość")
        .setSmallIcon(android.R.drawable.ic_dialog_info)
        .setLargeIcon(icon)
        .setNumber(messageNumber)
        .setAutoCancel(true)
        .setContentIntent(pIntent)
        .build();

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        notificationManager.notify(0, noti);
    }

Przetestujmy to działanie na emulatorze.

noti_041
noti_031

Kolejne wysłania powiadomień powodują zwiększenie licznika, ale nie powodują ich rozmnożenia na liście. Zauważ, że w stylu normalnym powiadomienia wyświetlana jest ostatnia wiadomość, która nadeszła.

noti_032
noti_033

Priorytety powiadomień

Domyślnie notyfikacje wyświetlane są według czasu nadejścia, od najnowszego do najstarszego. Od wersji Jelly Bean możemy jednak nadawać priorytet naszemu powiadomieniu. Oznacza to, że możemy na przykład wymusić, by znajdowało się ono na samej górze listy lub jej dole, niezależnie od jego czasu nadejścia wiadomości.

Mamy do wyboru pięć poziomów ważności dla naszego powiadomienia. Przedstawia je poniższa tabela.

Poziom Zastosowanie
MAX Najwyższy priorytet. Stosowany jest dla pilnych powiadomień, które mogą być bardzo istotne dla użytkownika. Przydatny w sytuacji, gdy jego reakcja na otrzymaną wiadomość powinna być jak najszybsza.
HIGH Wysoki priorytet. Stosuje się go dla powiadomień, które mogą być ważne dla użytkownika, na przykład informacja o nieodebranym połączeniu lub nieprzeczytanej, a nadal ważnej wiadomości email.
DEFAULT Domyślny poziom powiadomienia.
LOW Niski poziom powiadomienia. Może mieć zastosowanie dla informacji, które nie są dla użytkownika tak pilne.
MIN Ten priorytet powinny dostać powiadomienia o znikomym znaczeniu dla użytkownika.

 
W naszych dotychczasowych przykładach nigdzie nie ustawialiśmy priorytetów powiadomieniom, zatem wszystkie otrzymały poziom DEFAULT. Sprawdźmy więc, jak zachowują się one z taką konfiguracją.

W tym celu wybierzmy najpierw pierwszy przycisk, a potem drugi. Jak można było się spodziewać, wiadomość rozszerzona jest powyżej zwykłej, ponieważ nadeszła później.

noti_040_a
noti_043

Wprowadźmy teraz dobną modyfikację w metodzie createNotification() i zobaczmy, jak wpłynie ona na identyczny scenariusz. Dodajmy ustawienie parametru .setPriority(Notification.PRIORITY_MAX) dla tworzonego obiektu Notifiaction.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    private void createNotification() {

        Intent intent = new Intent(this, ResultActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

        int messageNumber = ++numMessages;

        Notification noti = new NotificationCompat.Builder(this)
        .setContentTitle("Nowa wiadomość")
        .setContentText("Wiadomość numer " + messageNumber)
        .setTicker("Masz wiadomość")
        .setSmallIcon(R.drawable.ic_launcher)
        .setNumber(messageNumber)
        .setAutoCancel(true)
        .setContentIntent(pIntent)
        .setPriority(Notification.PRIORITY_MAX)
        .build();

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        notificationManager.notify(0, noti);
    }

Uruchamiamy aplikację i sprawdzamy, jak zachowają się nasze powiadomienia. Ponownie najpierw klikamy pierwszy przycisk, a po odczekaniu chwili wybieramy drugi. Tym razem to wiadomość zwykła jest wyżej niż wiadomość rozszerzona, mimo że ta pierwsza nadeszła wcześniej, co widać po czasach dostarczenia tych powiadomień.

noti_040_a
noti_044

Nadaliśmy naszej zwykłej wiadomości najwyższy priorytet, zatem będzie wyświetlana wyżej niż powiadomienia o niższym priorytecie niezależnie od czasu ich nadesłania.

Co z wcześniejszymi wersjami Androida?

Dzięki zastosowaniu klasy NotificationCompat nie musimy się martwić o wsteczną kompatybilność. Należy jednak pamiętać, że w aplikacjach z wersją Androida niższą niż 4.1 wszystkie powiadomienia będą wyświetlane w trybie normalnym, bez możliwości ich rozszerzania. Nie będziemy mieli też dodatkowego obszaru na uzupełniające informacje notyfikacji. Nie skorzystamy także z akcji oraz z priorytetowania powiadomień. Wszystko to należy uwzględnić przy projektowaniu mechanizmu powiadomień we własnej; aplikacji.

Podsumowanie

System powiadomień Androida jest bardzo ciekawą funkcjonalnością, która, jak wspomniałem we wstępie, może znacząco zwiększyć użyteczność oraz komfort pracy z Twoją aplikacją. Jednak, jak zawsze nie należy przesadzać. Nadużycie tego narzędzia może powodować frustrację użytkownika, który w końcowym przypadku odinstaluje sobie irytującą go aplikację.

Gorąco zachęcam Cię do dalszego zgłębiania wiedzy o powiadomieniach w Androidzie.

Źródła

http://developer.android.com/guide/topics/ui/notifiers/notifications.html
http://developer.android.com/design/patterns/notifications.html


Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Komentarz do “Android HOW TO: Jak wysłać powiadomienie?

  • Kamil

    Szkoda, że nie ma dodawania wibracji, dźwięku, koloru LED, czasu powtarzania powiadomienia, daty i godziny powiadomienia plus ustawianie ich z pozycji użytkownika. Ale i tak jest super 🙂