11. Přednáška Broadcast receiver Timer Alarmy Widgety 2
3 Broadcasty a Broadcast Receivery Doposud jsme Intenty používali ke spouštění nové Aplikace nebo Aktivity Intenty však mohou sloužit také jako posílání anonymních zpráv mezi komponentami Po odeslání broadcastu se pošle daný Intent všem BroadcastReceiverům zaregistrovaným na danou akci v Intentu
4 Broadcasty odeslání Broadcast můžeme odeslat dvěma způsoby Normální Broadcast Pošle se pomocí Context.sendBroadcast Je kompletně asynchronní všechny BroadcastReceivery obdrží tuto zprávu v nedefinovaném pořadí (často ve stejný čas) Seřazený Broadcast Posílá se pomocí Context.sendOrderedBroadcast V jeden okamžik zpracovává zprávu maximálně 1 BroadcastReceiver Pořadí se určí atributem android:priority v Intent filteru daného receiveru Po zpracování zprávy může každý receiver určit, zda-li předá výsledek dalšímu receiveru v pořadí nebo kompletně zruší daný broadcast a dál už se předávat nebude
5 Broadcasty odeslání 1. 5. 6. 7. 8. 9.... public static final String TEST_BROADCAST= "cz.cvut.example.test_broadcast";... Intent i = new Intent(TEST_BROADCAST); i.putextra("id", 3); sendbroadcast(i); Obvykle se používá jméno balíčku, aby... se zajistila jedinečnost Tímto řetězcem se musí zaregistrovat všechny BroadcastReceivery, které mají reagovat na tuto zprávu
6 BroadcastReceivery Startují se automaticky obdržením broadcastu onreceive musí skončit do 5s Pro delší operace je zde vhodné spustit službu BroadcastReceiver nedědí z Contextu, ten je předán metodě onreceive 1. public class MyReceiver extends BroadcastReceiver { @Override public void onreceive(context context, Intent intent) { 5. int id = intent.getintextra("id", 0); 6.... 7. } 8. }
7 Zaregistrování BroadcastReceiveru Staticky v AndroidManifest.xml 1. <receiver android:name=".myreceiver"> <intent-filter> <action 5. android:name="cz.cvut.example.test_broadcast" /> 6. </intent-filter> 7. </receiver> Nebo dynamicky v kódu (např. pokud je potřeba pouze když je aktivita viditelná) 1. 5. IntentFilter filter = new IntentFilter(TEST_BROADCAST); BroadcastReceiver myreceiver = new TestReceiver(); registerreceiver(myreceiver, filter); např. v onresume()... unregisterreceiver(myreceiver); např. v onpause()
8 Užitečné akce broadcastu Intent.ACTION_BOOT_COMPLETED Odešle se po nastartování systému Vyžaduje oprávnění RECEIVE_BOOT_COMPLETED Intent.ACTION_MEDIA_BUTTON Odešle se po stisknutí tlačítka pro ovládání přehrávače (např. na sluchátkách) V Intentu je v extra poli EXTRA_KEY_EVENT informace o daném tlačítku Intent.ACTION_CAMERA_BUTTON Odešle se po stisknutí tlačítka fotoaparátu Intent.ACTION_MEDIA_EJECT Pokud se uživatel rozhodne vyjmout externí úložiště, nejprve se rozešle tento broadcast Po obdržení této zprávy by aplikace měly ukončit IO z/do externího úložiště Intent.ACTION_BATTERY_LOW Odešle se spolu s hláškou o nízkém stavu baterky Intent.ACTION_SCREEN_ON a ACTION_SCREEN_OFF Odešle se při zapnutí a vypnutí obrazovky Intent.ACTION_MEDIA_MOUNTED a ACTION_MEDIA_UNMOUNTED Odešle se po úspěšném vložení a vyjmutí média
9 Oprávnění broadcastů Pro vyžádání oprávnění při odesílání broadcastu předáme nenullový parametr metodě sendbroadcast(intent, receiverpermission); případně sendorderedbroadcast(intent, receiverpermission); Daný broadcast přijmou pouze receivery, které mají toto oprávnění definované v AndroidManifest.xml pomocí <uses-permission> Pro vyžádání oprávnění při přijímání definujeme toto oprávnění při zaregistrování Receiveru Dynamicky registerreceiver(receiver, filter, broadcastpermission, scheduler); Staticky 1. <receiver android:name=".myreceiver" android:permission="android.permission.internet">... 5. </receiver> BroadcastReceiver přijme daný broadcast pouze z aplikací, které mají toto oprávnění definované v AndroidManifest.xml pomocí <uses-permission>
10 Timer Umožňuje spouštět úlohu TimerTask v daný čas/časový interval Úloha se spustí v nově vytvořeném vlákně 1. timertask = new TimerTask() { @Override public void run() {... 5. } 6. }; 7. timer = new Timer(); 8. //spousteni dane ulohy kazdou 1s 9. timer.schedule(timertask, 0, 1000); 10. 11... 1 1//zruseni timeru 1timer.cancel();
11 Alarmy Umožňují odesílat Intenty v předem stanovený čas Jsou odesílány mimo rámec aplikace => Na rozdíl od služeb umožňují spouštět události aplikace i když byla před tím ukončena Protože se aplikace spustí až v daný čas, jsou alarmy efektivní možností jak provádět jednorázové i pravidelné úkony např. Pravidelné stahování aktualizací na pozadí Nastavení upozornění v daný čas Spuštění časově náročných úkonů v době kdy se nepředpokládá používání telefonu (v noci) Všechny Alarmy jsou zrušeny při vypnutí telefonu Můžeme opět nastavit pomocí BroadcastReceiveru zaregistrovanému na Intent.ACTION_BOOT_COMPLETED
12 AlarmManager Alarmy jsou řízeny AlarmManagerem systémovou službou, k níž získáme přístup pomocí getsystemservice 1. AlarmManager am = (AlarmManager) context.getsystemservice(context.alarm_service); Pomocí AlarmManageru můžeme nastavit tyto alarmy: Jednorázový spustí se jednou v daný čas am.set(type, triggerattime, operation); Opakovaný spouští se opakovaně přesně v daných časech (např. každý den v 13:00) am.setrepeating(type, triggerattime, interval, operation); Nepřesný opakovaný spouští se opakovaně ale není zaručený přesný čas Méně náročný a baterku systém se snaží spustit co nejvíc alarmů najednou zabraňujíc probouzení zařízení častěji, než je potřeba am.setinexactrepeating(type, triggerattime, interval, operation);
13 AlarmManager U každého Alarmu musíme definovat tyto 3 hodnoty Typ určuje jestli zadaný čas určuje specifický nebo uběhlý čas AlarmManager.RTC; Čas znamená počet ms od 1.1.1970 - System.currentTimeMillis(); Neprobudí zařízení (pokud zařízení v daný čas spí, provede se daný PendingIntent po probuzení) AlarmManager.RTC_WAKEUP Probudí zařízení AlarmManager.ELAPSED_REALTIME; - Zadaný čas znamená počet ms od posledního bootu SystemClock.elapsedRealtime(); AlarmManager.ELAPSED_REALTIME_WAKEUP; Čas - určuje kdy se spustí Alarm(u opakovaných Alarmů je to čas prvního spuštění) Pokud je čas v minulost, spustí se Alarm hned po nastavení Operace PendingIntent, který se provede U opakovaných Alarmů je navíc potřeba parametr určující interval, v jakém se bude spouštět
14 AlarmManager Opakovaným nastavením stejného alarmu se zruší předchozí a nastaví nový (oldpendingintent.equals(newpendingintent) == true) Vrací true, pokud oba představují stejnou operaci ze stejného balíčku 1. 5. 6. 7. 8. 9. 10. 11. 1 1 AlarmManager am = (AlarmManager) context.getsystemservice(alarm_service); long time = System.currentTimeMillis() + 1000*3600*2; long interval = 1000*3600*2; Intent intent = new Intent(context, MyReceiver.class); PendingIntent pendingintent = PendingIntent.getBroadcast(context, 0, intent, 0); int type = AlarmManager.RTC_WAKEUP; //nastaveni opakovaneho alarmu am.setrepeating(type, time, interval, pendingintent);... //zruseni alarmu am.cancel(pendingintent);
Widgety Vizuální komponenty aplikace, které mohou být přidány do domovské obrazovky Běží v procesu domovské obrazovky Skládají se z těchto 3 částí: Layout XML definující parametry widgetu BroadcastReceiver, který ovládá widget 15
Widgety layout Lze použít pouze tyto layouty: FrameLayout LinearLayout RelativeLayout Lze použít pouze tyto Views: AnalogClock Button Chronometer ImageButton ImageView Progressbar TextView ViewFlipper 16
18 AppWidgetProviderInfo Widget je definován XML souborem uloženým v res/xml/ 1. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minwidth="72dp" android:minheight="72dp" 5. android:updateperiodmillis="1800000" 6. android:configure="cz.cvut.examplewidget.configure" 7. android:initiallayout="@layout/main" /> minwidth, minheight minimální šířka a výška, kterou zabírá widget Vypočítá se pomocí tohoto vzorce: (počet políček * 72)-2 updateperiodmillis Interval v ms, jak často bude systém widget aktualizovat zavolá se onupdate() (negarantován přesný čas) initiallayout odkaz na layout definován v res/layout/ configure Aktivita, která se spustí při přidání widgetu (volitelné) Musí být celá cesta i se jménem balíčku
19 Widgety IntentReceiver Widgety jsou aktualizovány pomocí BroadcastReceiverů s Intent filtry, které reagují na AppWidgetManager.ACTION_APPWIDGET_UPDATE, DELETED,ENABLED a DISABLED AppWidgetProvider usnadňuje zpracovávání těchto akcí (potomek BroadcastReceiveru) 1. 5. 6. 7. 8. public class ExampleWidget extends AppWidgetProvider { @Override public void onupdate(context context, AppWidgetManager appwidgetmanager, int[] appwidgetids) { //aktualizace widgetu } }
20 Widgety IntentReceiver Widgety se do AndroidManifest.xml přidávají jako běžné BroadcastReceivery, pouze se přidají navíc 2 tagy: Intent filtr pro android.appwidget.action.appwidget_update Odkaz na XML s parametry widgetu 1. <receiver android:name=".examplewidget" android:label="muj widget"> <intent-filter> 5. <action 6. android:name="android.appwidget.action.appwidget_update" /> 7. </intent-filter> 8. <meta-data 9. android:name="android.appwidget.provider" 10. android:resource="@xml/example_widget_provider" /> 11. </receiver>
21 Widgety AppWidgetProvider onupdate(context, appwidgetmanager, appwidgetids) Volá se v pravidelném intervalu definovaném pomocí updateperiodmillis v AppWidgetProviderInfo XML Pokud není deklarována konfigurační aktivita, zavolá se i po přidání widgetu na plochu ondeleted(context, appwidgetids) Zavolá se pokaždé, když je widget odebrán z plochy onenabled(context) Zavolá se po prvním přidání widgetu na plochu (pokud např. uživatel přidá 2 widgety, zavolá se pouze při přidání prvního) ondisabled(context) Zavolá se po odebrání posledního widgetu onreceive(context, intent) Volá se při každém broadcastu před výše zmíněnými metodami
22 RemoteViews Třída umožňující manipulaci s Views zobrazenými v jiné aplikaci (např. manipulace s UI widgetu, který běžně běží v procesu domovské obrazovky, z BroadcastReceiveru, který běží v procesu naší aplikace) 1. RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.main); // Aktualizace textu v TextView views.settextviewtext(r.id.textview, "Novy text"); 5. views.settextcolor(r.id.textview, Color.BLUE); 6. // Nastaveni obrazku v ImageView 7. views.setimageviewbitmap(r.id.imageview, mybitmap); 8. // Aktualizace ProgressBaru 9. views.setprogressbar(r.id.progressbar, 100, 50, false); 10. // Aktualizace Chronometru 11. views.setchronometer(r.id.chronometer, 1 SystemClock.elapsedRealtime(), null, true); 1 1 //Spusteni aktivity Configure po kliku na imagebutton 15. Intent i = new Intent(context, Configure.class); 16. PendingIntent pendingintent = PendingIntent.getBroadcast(context, 0, i, 0); 17. views.setonclickpendingintent(r.id.imagebutton, pendingintent);
23 AppWidgetProvider onupdate AppWidgetProvider předá do onupdate tyto 3 parametry Context context ve kterým běží BroadcastReceiver AppWidgetManager zavoláním appwidgetmanager. updateappwidget(appwidgetid, remoteviews); aktualizujeme UI widgetu Instanci lze získat z jiné metody pomocí AppWidgetManager.getInstance(context); appwidgetids pole intů s ID aktuálně zobrazovaných widgetů
24 AppWidgetProvider onupdate 1. @Override public void onupdate(context context, AppWidgetManager appwidgetmanager, int[] appwidgetids) { final int N = appwidgetids.length; 5. // Projdeme vsechny widgety 6. for (int i = 0; i < N; i++) { 7. //ID aktualne prochazeneho widgetu 8. int appwidgetid = appwidgetids[i]; 9. 10. // Nastavime nas layout do RemoteViews 11. RemoteViews views = new RemoteViews(context.getPackageName(), 1 R.layout.main); 1 // nastavime text 1 views.settextviewtext(r.id.text, appwidgetid + " text"); 15. 16. // Aktualizujeme UI widgetu 17. appwidgetmanager.updateappwidget(appwidgetid, views); 18. } 19. super.onupdate(context, appwidgetmanager, appwidgetids); 20. }
25 Widgety konfigurační aktivita Automaticky se spustí při vytvoření widgetu Umožňuje uživateli nakonfigurovat nastavení widgetu Definuje se v AndroidManifest.xml jako běžná aktivita s intent filtrem android.appwidget.action.appwidget_configure 1. <activity android:name=".configure"> <intent-filter> <action android:name= "android.appwidget.action.appwidget_configure" /> 5. </intent-filter> 6. </activity> Musí být také nastavena v AppWidgetProviderInfo XML 1. <appwidget-provider... android:configure="cz.cvut.examplewidget.configure"... />
26 Widgety konfigurační aktivita V Intentu, který spustil tuto aktivitu najdeme ID přidávaného widgetu pod extra polem AppWidgetManager.EXTRA_APPWIDGET_ID Pokud je nastavena konfigurační aktivita, při vytvoření widgetu se NEzavolá onupdate UI musíme nastavit zde Pokud konfigurační aktivita vrátí výsledek RESULT_CANCELED, widget se nevytvoří
27 Widgety konfigurační aktivita 1. 5. 6. 7. 8. 9. 10. 11. 1 1 1 15. 16. 17. 18. 19. 20. AppWidgetManager appwidgetmanager = AppWidgetManager.getInstance(this); //ziskani ID pridavaneho widgetu Intent intent = getintent(); int mappwidgetid = intent.getintextra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); //nastaveni UI RemoteViews views = new RemoteViews(getPackageName(), R.layout.main); views.settextviewtext(r.id.text, "text"); appwidgetmanager.updateappwidget(mappwidgetid, views); //nastaveni vysledku a ukonceni aktivity Intent resultvalue = new Intent(); resultvalue.putextra(appwidgetmanager.extra_appwidget_id, mappwidgetid); setresult(result_ok, resultvalue); finish();