piątek, 17 października 2014

Pojedynek z Androidem - odc. 14 jeszcze o intencjach

Intencja to Androida esencja. Zachęcony takowym hasłem zdobyłem się na zdobycie kolejnego levelu systemu z zielonym robotem. W sumie jakiegoś dużego zaskoczenia nie było, wiele zostało już powiedziane wcześniej, ale trochę nowych informacji się pojawiło - czy to porządkujących jakieś tam zagadnienie czy odsłaniające więcej szczegółów. A więc, podsumujmy:

Intencja - twór dość ogólny, pełni funkcję zarówno jakby tasków, kontraktów, funkcjonalności (czy czasami zadeklarowanych w manifeście wręcz GUID-ów) oraz zdarzeń i triggerów. Fajnie, że wymyślono coś w miarę uniwersalnego do różnych zastosowań, stosowanego zarówno przez system operacyjny, jak i każdego dostawcę oprogramowania, a każdy może definiować sobie nowe takie byty.  W Windows/Windows Phone mamy wielopostaciowość, narzucone rodzaje komunikacji, które czasami możemy dostosowywać do swoich potrzeb np. przesyłać dane w swoim formacie, obsłużyć swój typ pliku czy protokół.  Czy to oznacza, że to gorzej? Niekoniecznie, możliwości są w miarę podobne, Windows obsługuje wszystko co powinien, a w wielu przypadkach API dzięki choćby async czy zwykłym nawet zdarzeniom wydaje się łatwiejsze i bardziej intuicyjne. 

Aktywacja komponentów - pochwalić można kontrolę nad tworzeniem instancji.  API pokazujące sposób “myślenia systemu” przy wyszukiwaniu komponentów pod dane kryteria może i niezłe, ale czy aż tak potrzebne?  

PendingIntent - jak już wiemy służy do delegowania wykonania czegoś na jakimś komponencie. Jakby taka pewna forma callbacku, w którym wywoływana jest właściwa intencja. Może się sprawdzić w takiej sytuacji jak chociażby oprogramowanie własnych przycisków na notyfikacji czy widgecie, czego jeszcze nie mamy w rodzinie Windows. Natomiast domyślne zwracanie tej samej referencji dla równych intencji (ale np. różniących się danymi) jest pułapką, na którą trudno znaleźć proste wytłumaczenie. Trzeba o tym po prostu wiedzieć, a jeśli nie o to akurat nam chodzi, to używamy jawnie wybranej flagi.

Obsługa funkcji systemowych

  • zmiany w systemie - odpowiednik jakby nawet niektórych triggerów w Windows, zdarzenia śledzące szczegółowo co się dzieje z baterią (np. jej temperaturą) czy podłączenie / odłączenie słuchawek póki co wydają się być domeną Androida
  • przechwytywanie notyfikacji po czasie ich wystąpienia - rzecz dotyczy takich specyficznych przypadków jak dokowanie czy brak miejsca w storage, wydaje się przydatne, na razie w mobilnej platformie Windows czegoś takiego nie mamy (choć są dłużej trwające notyfikacje toast przydatne np. dla aplikacji typu VoIP jak choćby Skype), ale mam tu ciekawe skojarzenia z notyfikacjami Windows 10, w którym postanowiono połączyć w jedną całość notyfikacje z WinRT z notyfikacjami pokazywanymi do tej pory na task barze. W efekcie czego jak np. odłączam sobie USB pokazuje mi się “modernistyczny” prostokąt z informacją, że mogę go już bezpiecznie odłączyć. Może idąc tym tropem doczekamy się podobnych notyfikacji np. o braku miejsca na dysku ?  A wtedy zasadne byłoby odbieranie takich informacji po czasie? Równie dobrze jednak możnaby to załatwić odczytaniem jakiegoś stanu systemowego np. przy starcie aplikacji.
  • kolejkowanie w czasie akcji do wykonania, wyświetlanie strony Web, telefonowanie, robienie zdjęć, wybieranie kontaktu z listy kontaktów - wszystko to mamy także w Windows, przy czym kilka uwag. Kolejkowanie może nie jest aż tak konfigurowalne jak w Android. Chodzi mi o kontrolowanie precyzji co do odstępów czasowych. Jeśli chodzi o budzenie urządzenia, to w Windows również jest to możliwe (Connected Standby), taką ideę spotykamy w niektórych triggerach czy alarmach (w aplikacji alarmowej). Wyświetlanie strony Web jest tak samo proste (Launcher w WinRT API albo z dawniejszych czasów task w WP). Jeśli chodzi o telefonowanie, to w Windows Phone nie ma bezpośredniego dzwonienia. Robienie zdjęć z wykorzystaniem aplikacji systemowej w obu systemach jest dość proste (bardziej nowoczesną formę i prostotę ma WinRT API Windows, w WP możemy odnaleźć jeszcze taska z dawniejszych czasów, który również jest prosty w obsłudze, choć już mniej nowoczesny, bo oparty o zdarzenie, a nie o async).  Jeśli chodzi o wybór kontaktów to mamy analogię w postaci contact pickera w WinRT (możemy jeszcze w starszym API do WP odnaleźć contact choosera).  Co ciekawe Android używa słowa “contract” w klasie zwracającej dane do intencji, a sama akcja zawiera słowo “pick” !  Na tym jednak podobieństwa się kończą, bo wyciąganie danych o kontaktach pokazane przez autora nagrania jest o niebo trudniejsze niż w API Windows, gdzie mamy obiekty klas.  W Androidzie trzeba odpytywać w niskopoziomowym stylu bazodanowym tabelki content providera, bo wyciągnąć e-mail czy numer telefonu wybranego kontaktu!  Trochę słabo.

 

Mała powtórka

Intencja

  • struktura komunikatu specjalnego przeznaczenia
    • aktywacja komponentu
    • opisuje operację lub zdarzenie
  • używana w dwóch celach
    • opis operacji do wykonania
    • opis czegoś co się zdarzyło

Opis operacji bez używania jej typu

  • Action - opis akcji lub operacji do wykonania
  • Extras - dodatkowe informacje (słownik klucz-wartość)
  • Data - URI lub typ mime danych przetwarzanych przez akcję
  • Category - identyfikuje podgrupę typu komponentu, która powinna przechwycić intencję

Android routuje intencje w oparciu o typ docelowy

  • 3 typy docelowe
    • aktywności lub serwisy
      • żądanie wykonania operacji
      • wybierany jest dokładnie jeden serwis lub aktywność
      • użytkownik może być poproszony o dokonanie wyboru
    • broadcast receivery
      • zdarzenie, które zaszło
      • wszystkie pasujące broadcast receivery otrzymują intencję
    • celem każdej intencji jest tylko jeden rodzaj komponentu
  • komponent nie musi się wykonywać by otrzymać intencję
    • package manager sprawdza komponent intencji
    • system automatycznie tworzy nową instancję komponentu, jeśli jest to potrzebne

Komponenty używają filtrów intencji do ogłaszania swoich funkcjonalności i swoich “zainteresowań”

  • IntentFilter
    • wartości składowych opisują testy na podzbiór wartości składowych intencji
    • komponent może zdefiniować wiele instancji filtrów
    • normalnie jest opisywany w manifeście
    • niektóre filtry mogą być dynamicznie rejestrowane w kodzie
  • 2 zasadnicze cele
    • określenie, które komponenty powinny otrzymać daną intencję
    • opisanie możliwości komponentu lub pożądanych przez niego funkcjonalności

Testy filtrów intencji

  • akcja – jedna lub więcej akcji, które komponent może przechwycić
  • dane – jedno lub więcej Uri/rodzaj danych  - // -
  • kategoria – jedna lub więcej podgrup, do których należy komponent

PendingIntent  opakowuje intencję w akcję, która ma być z nią wykonana

  • enkapsuluje tożsamość nadawcy i dane uwierzytelniające
  • używany do delegowania akcji na inny komponent
  • pozwala wykonywać odbiorcy akcję, jakby była pochodziła od nadawcy
  • pozwala przywrócić kontrolę nad danym zadaniem
    • odpala aktywność, kiedy użytkownik zaznacza notyfikację
    • zwraca sterowanie z powrotem do aplikacji, która przekazała sterowanie reużytkowalnemu widgetowi
    • otrzymuje wyniki z serwisu

Linki

 

Aktywacja komponentów przez intencje

Komponenty są aktywowane przez wysyłanie intencji

  • aktywności aktywowane przez startActivity
  • serwisy  - // -  przez startService
  • broadcast receivery  - // -  przez sendBroadcast
  • intencje mogą być wysyłane przez aplikacje lub system

Jawne intencje  - najprostsze to stworzenia

  • zawierają informacje o komponencie
  • zawsze ukierunkowane na instancję typu
  • cała pozostała zawartość intencji ignorowana, kiedy komponent jest wybierany

Intencje oferują dużo więcej niż tylko proste tworzenie obiektów

  • dostarczają kontrolę nad zarządzaniem komponentem
  • wysyłanie intencji może tworzyć nową instancję komponentu
  • -  // - przekierowywać intencję na istniejącą instancję komponentu
  • flagi kontrolujące zachowanie podczas aktywacji komponentu

Każdy rodzaj komponentu ma inny dostęp do intencji

  • serwisy
    • Intent automatycznie jest przekazywany do metody onStartCommand
  • broadcast receivery
    • Intent automatycznie jest przekazywany do metody onReceive
  • aktywności
    • intencja, która utworzyła aktywność jest dostępna przez metodę getIntent
    • każda inna intencja wysłana do aktywności jest przekazywana do metody onNewIntent

Najprostsze wywołania

Intent intent1 = new Intent(this, YActivity.class);

startActivity(intent1);

 

Intent intent2 = new Intent(this, ZService.class);

startActivity(intent2);

Flagi

intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

Niejawne intencje

  • znoszą ograniczenia na znajomość nazwy komponentu (wywołania między różnymi aplikacjami i procesami)
  • dopasowywane do komponentów przy użyciu akcji, kategorii i/lub danych
  • znalezienie komponentu oparte jest na filtrach intencji dostarczanych przez komponenty
    • aktywacja działa tak samo jak przy intencjach jawnych
    • jedyna różnica jest w sposobie wybierania komponentu
  • nie zawierają nazwy komponentu
    • jeśli występuje, intencja nie jest porównywana z żadnymi filtrami intencji

Filtry intencji dostarczane przez komponent

  • definiowane w manifeście aplikacji
  • pasuje, jeśli wszystko w nim pasuje (akcja, kategoria, dane)
  • komponent pasuje, jeśli jeden lub więcej filtrów pasuje
  • komponent bez filtra może być aktywowany tylko przez jawną intencję

Akcja

  • nazwa w stylu pakietowym (często “action” na końcu nazwy pakietu)  np. com.xxx.action.DO_WORK
  • dla wielu standardowych akcji w kodzie używamy stałych Intent.ACTION_xxx (w manifeście używamy zawsze jawnej wartości string)
  • intencja może posiadać najwyżej jedną akcję
  • filtr intencji zawiera zero lub więcej akcji (w praktyce jedna lub więcej)

Intent intent = new Intent(“com.xxx.action.LOG_TIME”);

startService(intent);

 

<intent-filter>

          <action android:name=”com.xxx.action.LOG_TIME” />

          <action android:name=”com.xxx.action.LOG_DATE” />

</intent-filter>

Dopasowanie akcji

  • wielkość liter ma znaczenie
  • dopasowanie, jeśli akcja intencji zawiera się w liście akcji filtra
  • filtr z pustą listą akcji nie pasuje do żadnych intencji
  • intencja bez akcji pasuje do wszystkich filtrów, które mają co najmniej jedną akcję

Extras

  • informacje mogą być silnie typowane
    • string i większość typów prymitywnych
    • tablice typów
    • typy Serializable (Java) i Parceable (Android)
  • nie biorą udziału przy dopasowywaniu komponentów (nie ma ich w filtrach)

Kategorie

  • nazwa w stylu pakietowym (często na końcu “category”)
  • dla wielu standardowych kategorii w kodzie używamy stałych Intent.CATEGORY_xxx (w manifeście używamy zawsze jawnej wartości string)
  • intencja może zawierać 0 lub więcej kategorii (kategorie najczęściej pojawiają się w generowanych przez system intencjach)
  • filtr intencji zawiera 0 lub więcej kategorii

Dopasowywanie kategorii

  • wielkość liter ma znaczenie
  • dopasowanie, jeśli wszystkie kategorie w intencji są obecne w filtrze
  • intencja z pustą listą kategorii przechodzi test kategorii dla wszystkich filtrów (także filtry z pustą listą kategorii)
  • czasami kategorie są wymagane w filtrze, nawet jeśli ich nie ma w intencji
    • CATEGORY_DEFAULT - wymagana dla aktywności, która chce być dostępna przez startActivity przez intencję niejawną
    • CATEGORY_LAUNCHER - wymagane dla aktywności, która ma się pojawiać na ekranie Android Launcher

Dane

  • URI / mime (np. audio/mp4)
  • intencja zawiera zero lub więcej elementów określających dane
  • filtr zawiera zero lub więcej testów na dane
  • typy mime / uri mogą zawierać *
  • test URI składa się z kilku części: schemat(protokokół), host, port, ścieżka

Dopasowywanie danych

  • wielkość liter ma znaczenie
  • dopasowanie, jeśli dane intencji przechodzą przynajmniej jeden test danych z filtra
  • komplikacje
  • scenariusze dopasowania
    • intencja bez typu lub URI + filtr bez typu i URI
    • intencja z typem bez URI + filtr z pasującym typem bez URI
    • intencja z URI bez typu + filtr z pasującym URI bez typu
    • intencja z URI i typem + filtr z pasującym typem bez URI lub z pasującymi typem i URI

Konieczność

<activity …>

         <intent-filter>

                   <action android:name=”com.xxx.yyy.ABC_DEF” />

                   <category android:name=”android.intent.category.DEFAULT”/>

         </intent-filter>

</activity>

Intent intent = new Intent(”com.xxx.yyy.ABC_DEF”);

startActivity(intent);

Jeśli przygotujemy dwie aktywności z takimi filtrami jak powyżej, to przy takim samym wywołaniu Android wyświetli okno dialogowe pozwalające wybrać użytkownikowi aktywność (z opcją zapamiętania domyślnego wyboru).

Proces dopasowywania intencji do filtrów jest łatwo dostępny

  • możliwość logowania i uzyskania wyników dopasowania
  • logowanie
    • dodajemy do intencji Intent.FLAG_DEBUG_LOG_RESOLUTION
    • zapisywanie do Logcat
      • zawartość intencji
      • wszystkie pasujące komponenty
      • wybrany komponent
  • PackageManager
    • lista pasujących komponentów dla intencji
      • queryIntentActivities, queryIntentServices, queryBroadcastReceivers
    • komponent, który będzie wybrany dla intencji
      • resolveActivity, resolveService
    • wyniki zawierają opis komponentów
      • obiekty klasy ResolveInfo
      • flaga GET_RESOLVED_FILTER w zapytaniu pozwala uzyskać filtr, który został dopasowany do intencji
    • aktywności: dla naśladowania zachowania startActivity w wywołaniu należy użyć flagi MATCH_DEFAULT_ONLY

 

Delegacje i callbacki w intencjach PendingIntent

Bezpieczny sposób delegacji akcji na komponentach

Często stosowane w notyfikacjach, widgetach, serwisach, aktywnościach.

notification.setLatestEventInfo(this, “aaa”, “aaa”, pi);

PendingIntent enkapsuluje

  • Intent
  • akcję powiązaną z Intent
  • dane uwierzytelniające do komponentu
  • tworzenie pakietu komponentu

Zawartość PendingIntent  nie jest widzialna dla innych aplikacji

  • nie jest wystawiana poza tworzący ją komponent
  • przechowywana jako wpis w systemie Android

Współdzielenie PendingIntent jako referencji

Zduplikowane requesty o PendingIntent otrzymują referencję na tą samą pozycję

  • requesty muszą pochodzić z tej samej aplikacji
  • przekazane intencje muszą być równe jak w definicji Intent.filterEquals

Flagi PendingIntent

Flagi do metod getXXX pozwalają kontrolować zachowanie referencji

  • FLAG_UPDATE_CURRENT - jeśli pozycja już istnieje, zaktualizowanie jej extras z nowego Intent
  • FLAG_NO_CREATE - nowa pozycja nie jest tworzona, zwracana jest referencja do istniejącej pozycji, w przeciwnym razie null
  • FLAG_CANCEL_CURRENT - tworzona jest nowa pozycja, jeśli istnieje zgodna z nią pozycja oznaczona zostanie jako anulowana
  • FLAG_ONE_SHOT - pozycja może zostać wysłana tylko raz (możliwe jest wiele referencji do pozycji), jeśli istnieje zgodna z nią pozycja i nie zawiera tej flagi, tworzona jest nowa pozycja (obie pozycje są ważne)

W każdym przypadku ten sam PendingIntent bez extras:

Intent intent1 = new Intent(“com.xxx.ABC_DEF”);

PendingIntent pi1 = PendingIntent.getActivity(this, 0, intent1, 0);

 

Intent intent2 = new Intent(“com.xxx.ABC_DEF”);

intent2.putExtra(“KLM”, “xyz”);

PendingIntent pi2 = PendingIntent.getActivity(this, 0, intent2, 0);

Ta sama pozycja z extras:

PendingIntent pi2 = PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT);

Jak naprawić (pierwsza bez extras, druga z extras) ?

PendingIntent pi2 = PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_ONE_SHOT);

Wysyłanie PendingIntent

Wykonywanie zawartości

  • wywołanie metody send wysyła Intent  (kilka przeładowań)
  • możliwość robienia ograniczonych modyfikacji z Intent
    • możliwość wysyłania wartości Intent nie ustawionych już w Intent w PendingIntent
    • Intent.fillIn - wykonanie modyfikacji
  • możliwość wysłania kodu rezultatu do celu PendingIntent
    • ograniczone scenariusze, gdzie PendingIntent jest dostarczycielem wyniku
    • często używane w połączeniu z Activity.createPendingResult

class XActivity extends Activity {

        …

        private ComponentName getWallPaperServiceComponentName()  {

                final String serviceClassName = “com.android.internal.service.wallpaper.ImageWallPaper”;

                ComponentName wallpaperService = null;

               

                ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

                List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(Integer.MAX_VALUE);

               

                for(ActivityManager.RunningServiceInfo theService: services)  {

                       if  (serviceClassName.equalsIgnoreCase(theService.service.getClassName())) {

                               wallpaperService = theService.service;

                               break;

                       }

                }

 

                return wallpaperService;

        }

        public void showWallpaperControlPanel() {

                ComponentName wallpaperComponentName = getWallPaperServiceComponentName();

 

                ActivityManager am = (ActivityManager)  getSystemService(Context.ACTIVITY_SERVICE);

                PendingIntent pi = am.getRunningServiceControlPanel(wallpaperComponentName);

 

                try {

                       pi.send(); 

                }  catch (PendingIntent.CanceledException e)  {

                }             

        }

}

 

Testy danych w filtrze intencji

Uri

  • w intencjach jako pojedyncza wartość
  • testy w filtrze intencji jako części Uri

Testy danych

  • Uri - nie wszystkie części są wymagane
  • mimeType
  • case sensitive

<data android:scheme=”http”/>

<data android:host=”www.xyz.com”/>

<data android:port=”80”/>

<data android:path=”/xxx/yyy/zzz”/>

<data android:mimeType=”audio/mp4”/>

Test Uri

  • musi zawierać przynajmniej schemat (inne części testu są ignorowane, jeśli nie ma schematu)
  • przy podaniu portu trzeba podać hosta
  • przy podaniu path, pathPrefix, pathPattern należy podać hosta

Relacje między atrybutami danych

  • wiele atrybutów może być umieszczonych na jednym elemencie data
  • każdy atrybut może być umieszczony na osobnym elemencie data
  • ten sam atrybut dla data może pojawiać się kilka razy w filtrze -warunek “OR”
    • jeden atrybut na osobnych elementach data - prosta alternatywa
    • gdy chcemy mieć alternatywne kombinacje np. protokołów z hostami robimy to w osobnych filtrach

public class XActivity extends Activity {

        …

        private List<String> getMatchingActivityNames(Intent intent)  {

                List<String> activityNames = new LinkedList<~>();

 

                PackageManager pm = getPackageManager();

                List<ResolveInfo> resolveInfoList = pm.queryIntentActivities(intent, PackageManager.GET_INTENT_FILTERS | …);

 

                for(ResolveInfo resolveInfo : resolveInfoList)

                        activityNames.add(resolveInfo.activityInfo.name);

 

                return activityNames;

        }

}

pathPattern

  • x* - 0 lub więcej wystąpień znaku x
  • .* - 0 lub więcej dowolnego znaku

typy mime

  • każdy standardowy
  • własne wartości (często w połączeniu z własnym content providerem)
  • * - pasuje do wszystkich podtypów np. image/*
  • test zawierający tylko typ mime zakłada komunikację lokalną
    • pliki lokalne (schemat “file”)
    • dane trzymane w content providerach (schemat “content”)

 

Systemowe funkcje dostępne przez intencje

Wykrywanie zmian w stanie systemu

  • bootowanie / zamykanie systemu
    • Intent.ACTION_BOOT_COMPLETED
    • Intent.ACTION_SHUTDOWN
  • podłączenie / odłączenie słuchawek
    • Intent.ACTION_HEADSET_PLUG
    • extras: stan, typ, mic
  • zmiany w zasilaniu
    • Intent.ACTION_POWER_CONNECTED
    • Intent.ACTION_POWER_DISCONNECTED
    • Intent.ACTION_BATTERY_LOW
    • Intent.ACTION_BATTERY_OKAY

Przechwytywanie przegapionych notyfikacji

  • do odebrania większości notyfikacji systemowych potrzebujemy wcześniej zarejestrować broadcast receivery
  • niektóre notyfikacje mają trwałą ważność, nowi odbiorcy mogą je otrzymywać po fakcie
    • dokowanie urządzenia
      • Intent.ACTION_DOCK_EVENT
    • mało miejsca w storage
      • Intent.ACTION_DEVICE_STORAGE_LOW

Rejestrowanie się na systemowe zdarzenia

  • większość wspiera rejestrację w manifeście
  • wszystkie wspierają rejestrację w run-time za pomocą context.registerReceiver
  • niektóre mogą być rejestrowane tylko w run-time
    • Intent.ACTION_BATTERY_CHANGED - szczegółowe zmiany w stanie zasilania systemu (także zmiana temperatury, podłączenie, odłączenie), spore różnice pomiędzy urządzeniami (np. co ile % zmiany w naładowaniu odpalane jest zdarzenie)
    • Intent.ACTION_TIME_TICK - upływ każdej minuty czasu systemowego

AlarmManager

  • kolejkowanie akcji w przyszłości
  • używamy intencji PendingIntent
  • akcje mogą być wykonywane nawet jeśli program nie wykonuje się
  • opcjonalnie może budzić urządzenie
  • nie jest przewidziany do zdarzeń czasowych w działającej aplikacji (zbyt duży narzut)
  • trzy opcje do odpalania PendingIntent
    • AlarmManager.set - jednorazowo o podanym czasie
    • AlarmManager.setRepeating - o podanym czasie i dokładnie równych odstępach
    • AlarmManager.setInexactRepeating - mniej więcej o podanym czasie i aproksymowanych odstępach (większa oszczędność energii)
  • specyfikacja czasu
    • relatywny do czasu systemowego (SystemClock.elapsedRealtime)
    • relatywny do czasu UTC (System.currentTimeMillis)
    • opcjonalna możliwość budzenia urządzenia
    • flagi
      • ELAPSED_REALTIME
      • ELAPSED_REALTIME_WAKEUP
      • RTC
      • RTC_WAKEUP

long alarmTime = SystemClock.elapsedRealtime() + 5000;

AlarmManager alarmManager = (AlarmManager)  getSystemService(Context.ALARM_SERVICE);

alarmManager.set(AlarmManager.ELAPSED_REALTIME, alarmTime, pendingIntent);

Wiele funkcjonalności platformy Android jest zwykłymi aktywnościami

  • w większości przypadków używamy startActivity  np. przeglądarka web i telefon
  • dla otrzymywania rezultatów odbieramy startActivityForResult  np. kamera & kontakty

Wyświetlanie zawartości Web

  • data intencji: “http”/”https”
  • akcja intencji:  Intent.ACTION_VIEW

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(“http://kult.art.pl”));

startActivity(intent);

Telefon

  • dane intencji: “tel” uri
  • Intent.ACTION_DIAL - wyświetlenie ekranu do zadzwonienia
  • Intent.ACTION_CALL - od razu zadzwonienie (wymagane pozwolenie w manifeście permission.CALL_PHONE)

Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(“tel:0123456789”));

startActivity(intent);

Robienie zdjęć (już było)

Lista kontaktów

  • dane intencji:  ContactsContract.Contacts.CONTENT_URI
  • akcja intencji:  Intent.ACTION_PICK
  • po wyborze użytkownika wywoływane jest onActivityResult
    • dane intencji zawierają uri do kontakty w content providerze
    • Intent.EXTRA_SHORTCUT_NAME zawiera wyświetlaną nazwę kontaktu

Intent intent = new Intent(Intent.ACTION_PICK,  ContactsContract.Contacts.CONTENT_URI);

startActivityForResult(intent, 200);

 

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data)  {

         if (resultCode == Activity.RESULT_OK)  {

                 switch (requestCode)  {

                         case 200:

                                 String displayName = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);

                                 Uri contactUri = data.getData();

                                 String email = getContactEmail(contactUri);

                                 String phoneNumber = getContractPhoneNumber(contactUri);

                                 break;

                 }

         }

}

 

private String getContactEmail(Uri contactUri)  {

          return getContactCommonDataItem(contactUri, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,

          ContactsContract.CommonDataKinds.Email.CONTACT_ID, ContactsContract.CommonDataKinds.Email.DATA);

}

 

private String getContactPhoneNumber(Uri contactUri)  {

          return getContactCommonDataItem(contactUri, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,

          ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.DATA);

}

 

private String getContactCommonDataItem(Uri contactUri,  Uri dataStoreUri,  String idColumnName)  {

          String contactId = contactUri.getLastPathSegment();

          String resultValue = null;

 

          Cursor cursor = managedQuery(dataStoreUri,  null, idColumnName + “=?”, new String[] { contactId }, null);

          if (cursor.moveToFirst()) {

                  …

          }

}

Brak komentarzy: