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
- aktywności lub serwisy
- 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
- http://developer.android.com/guide/components/intents-filters.html
- http://developer.android.com/reference/android/content/Intent.html
- http://developer.android.com/reference/android/content/IntentFilter.html
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
- lista pasujących komponentów dla intencji
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
- dokowanie urządzenia
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:
Prześlij komentarz