programmieren für mobile endgeräte · android:name=".myactivity„ android:label=„eine...
TRANSCRIPT
Dozenten: Patrick Förster, Michael Hasseler
Programmieren für mobile Endgeräte SS 2013/2014
Dozenten: Patrick Förster, Michael Hasseler
Intents (Wiederholung I)
• Ein Intent erklärt die Absicht eine bestimmte Activity auszuführen
• Explizit durch Angabe einer Activity-Klasse
• Implizit durch Beschreibung der gewünschten Activity
• Intents kapseln zudem die Rückgabedaten einer Activity
Programmieren für mobile Endgeräte 2
Intent intent = new Intent(this, MyActivity.class); startActivity(intent);
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("http://www.uni-muenster.de/ZIV")); startActivity(intent);
Intent result = new Intent(); result.putExtra("url", "http://www.uni-muenster.de/ZIV"); setResult(Activity.RESULT_OK, result);
Dozenten: Patrick Förster, Michael Hasseler
Intents (Wiederholung II)
Programmieren für mobile Endgeräte 3
Activity
Activity
Intent
PackageManager
Activity
// explizit Intent intent = new Intent(context, MyActivity.class); // implizit Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(…); intent.addCategory(…);
… Activity
create lookup
Intent
Intent result = new Intent(); result.putExtra(…,…); result.putExtra(…,…);
setResult
finish
query
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents
• Wie wird bei einem impliziten Intent entschieden?
• Implizit nur die Activities, die im Manifest beschrieben sind
• Per <intent-filter> wird die Aufgabe einer Activity beschrieben
• Sobald eine implizite Absicht erklärt wird, werden alle Activities
hinsichtlich der Anforderungen gefiltert
• Nur eine wird am Ende ausgeführt
Programmieren für mobile Endgeräte 4
<activity android:name=".MyActivity„ android:label=„Eine Activity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Dozenten: Patrick Förster, Michael Hasseler
Late Runtime Binding
• Das Filtern zur Laufzeit wird „Late Runtime Binding“ genannt
• Filter werden definiert durch:
• <action>: Name der Aktionen die eine Activity ausführen kann
• <category>: Kategorie zur Einschränkung der Aktion
• <data>: Beschreibt die Daten auf denen die Activity arbeitet im
URI-Format und/oder als MIME-Type
• Alle Angaben können mehrfach vorkommen
• Zur Laufzeit:
• Mindestens eine der Aktionen muss der geforderten übereinstimmen
• Alle Kategorien müssen von eine Activity erfüllt werden
• Mindestens eine Datenbeschreibung muss stimmen
• Liefert das Filtern mehrere Ergebnisse, so muss der User entscheiden, welche Activity gestartet werden soll
Programmieren für mobile Endgeräte 5
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents (Beispiel)
• Default Activity im Manifest:
• <action> android.intent.action.MAIN:
• Activity ist Einstiegspunkt für die Applikation
• <category> android.intent.category.LAUNCHER:
• Activity soll im Luncher angezeigt werden
• Achtung: LAUNCHER sowie MAIN müssen definiert sein, damit die Activity im Launcher erscheint!
Programmieren für mobile Endgeräte 6
<activity android:name=".MyActivity" android:label="Eine Activity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents (Browser)
• Alle Activities sind gleichgestellt
• Keine „Bevorzugung“ von nativen Activities wie bspw. der Browser
• Aufruf des Browsers (aus Vorlesung 4):
• Eine eigene Activity mit folgender Konfiguration:
• <action> android.intent.action.VIEW: Die Activity stellt Daten
des angebenen Formats dar
• <data> http: Die Daten müssen das Scheme „http“ erfüllen (Achtung:
wirklich nur http, https würde das Datenformat nicht erfüllen!)
Programmieren für mobile Endgeräte 7
Uri webpage = Uri.parse("http://www.uni-muenster.de/ZIV/"); Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage); startActivity(webIntent);
<activity android:name=".URLActivity" android:label="URL Activity"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <data android:scheme="http"/> </intent-filter> </activity>
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents (Browser II)
• Eine weitere Activity sei definiert mit:
• einer EditText-Komponente für die
Eingabe einer URI
• einem Button, der bei einem Klick den VIEW-Intent einfordert
Programmieren für mobile Endgeräte 8
• Die URLActivity soll die übergebene URL per WebView anzeigen:
• Achtung: Für den Zugriff auf Internet-Ressourcen benötigt die Applikation eine „Erlaubnis“ im Manifest
public class URLActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { … Uri uri = getIntent().getData(); if (uri != null) { WebView view = new WebView(this); view.loadUrl(uri.toString()); LinearLayout layout = (LinearLayout) findViewById(R.id.base); layout.addView(view, 0); } }
<uses-permission android:name="android.permission.INTERNET"/>
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents (Browser III)
• Ein erster Test zeigt, dass der Default-Browser aufgerufen wird
• Die <intent-filter> Spezifikationen scheinen zu „schwach“
• Die Activity muss die Default-Kategorie erfüllen, um direkt per
implizitem Intent aufgerufen werden zu können
Programmieren für mobile Endgeräte 9
<activity android:name=".URLActivity" android:label="URL Activity"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="http"/> </intent-filter> </activity>
• Bei einem Klick auf den Goto-Button, wird nun auch die eigene Activity erkannt
• Der Nutzer muss nun die Entscheidung treffen, welche Activity zu starten ist
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents (Alternativen)
• Statt als Default kann man eine Activity auch als Alternative anbieten
• Alternative bedeutet, dass diese Activity nicht direkt aufgerufen, sondern von anderen Activities als Alternative angeboten werden kann
• SELECTED_ALTERNATIVE deutet dabei an, dass die Activity aus einer
Reihe von Activites ausgewählt werden sol
Programmieren für mobile Endgeräte 10
<category android:name="android.intent.category.ALTERNATIVE"/> <category android:name="android.intent.category.SELECTED_ALTERNATIVE"/>
• Die Beispiel-Activity soll nun um
eine Spinner-Komponente erweitert werden, die eine Auswahl aus allen „http“-Activities anbietet
• Je nach Auswahl soll die entsprechende Activity per Button
gestartet werden
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents (PackageManager)
• Über den sogenannten PackageManager (siehe Folie 2) wird nachgefragt, welche Activities einem bestimmten Intent genügen
• Hier sollen alle http-Activities erfragt werden:
• Die Rückgabe ist eine Liste von ResolveInfos
• ResolveInfo liefert alle zur Verfügung stehenden Informationen über eine bestimmte Activity
• Über diese Infos wird der Spinner gefüllt:
Programmieren für mobile Endgeräte 11
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("http:").normalizeScheme()); List<ResolveInfo> infos = getPackageManager().queryIntentActivities(intent, 0);
ArrayList<String> names = new ArrayList<String>(); for (ResolveInfo info : this.infos) { names.add(info.loadLabel(getPackageManager()).toString()); } ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, names); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); Spinner spinner = (Spinner) findViewById(R.id.choose_browser); spinner.setAdapter(adapter);
Dozenten: Patrick Förster, Michael Hasseler
Implizite Intents (PackageManager II)
• Anhand der Auswahl wird bei einem Klick ein expliziter Intent durchgeführt:
• Zuletzt wird die eigene Activity nun als SELECTED_ALTERNATVIE angeboten:
Programmieren für mobile Endgeräte 12
Spinner spinner = (Spinner)findViewById(R.id.choose_browser); int pos = spinner.getSelectedItemPosition(); ResolveInfo info = this.infos.get(pos); Intent intent = new Intent(); intent.setData(getUriFor(R.id.url_1)); intent.setClassName(info.activityInfo.applicationInfo.packageName, info.activityInfo.name); startActivity(intent);
<activity android:name=".URLActivity" android:label="URL Activity"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.SELECTED_ALTERNATIVE"/> <data android:scheme="http"/> </intent-filter> </activity>
Dozenten: Patrick Förster, Michael Hasseler
Broadcasts
• Broadcasts sind neben dem Aufruf von Activities und deren Rückgabewerte das dritte Einsatzgebiet von Intents
• Broadcasts werden genutzt um Nachrichten zwischen Applikationen auszutauschen
• Nachrichten können applikationsübergreifen verteilt werden
• Nachrichten werden von BroadcastReceivern entgegen genommen
Programmieren für mobile Endgeräte 13
Activity
Application
… Receiver Receiver
Application
…
Application
…
sendBroadcast(intent)
Dozenten: Patrick Förster, Michael Hasseler
Broadcasts (Definition)
• Analog zu den Activities werden Receiver im Manifest definiert
• Ebenso analog werden Intent-Filter genutzt, um festzulegen auf
welche Nachrichten ein Receiver wartet
• Hier wartet der Receiver vom Typ MyReceiver auf Nachrichten zur Aktion android.intent.action.AIRPLANE_MODE
• Manifest-Receiver werden mit dem Aufkommen einer Nachricht erzeugt
• Handling einer Nachricht durch:
• Nach der Methode wird der Receiver zerstört
• Keine asynchronen Prozesse innerhalb des Receivers
• Keine aufwendigen Prozesse innerhalb des Receivers (10sec Threshold)
Programmieren für mobile Endgeräte 14
<receiver android:name=".MyReceiver"> <intent-filter> <action android:name="android.intent.action.AIRPLANE_MODE"/> </intent-filter> </receiver>
public void onReceive(Context context, Intent intent);
Dozenten: Patrick Förster, Michael Hasseler
Broadcasts (Management)
• Was wenn ein Receiver keine Nachrichten mehr entgegen nehmen soll?
• Der PackageManager kann genutzt werden um einen Manifest-Receiver
zu deaktivieren:
• Oder der Receiver wird komplett durch eine Activity verwaltet
Programmieren für mobile Endgeräte 15
private MyReceiver receiver = new MyReceiver (); protected void onResume() { super.onResume(); registerReceiver(receiver, new IntentFilter(…)); … } protected void onPause() { super.onPause(); … this.unregisterReceiver(receiver); }
getPackageManager().setComponentEnabledSetting( new ComponentName(this, MyReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP );
Dozenten: Patrick Förster, Michael Hasseler
Broadcasts (Beispiel)
• Als Beispiel soll eine Activity entwickelt werden, die den aktuellen Batteriestatus des Gerätes anzeigt
• Entsprechende Systemnachricht: Intent.ACTION_BATTERY_CHANGED
Programmieren für mobile Endgeräte 16
• Als Architektur soll das Listener-Prinzip genutzt werden:
• Wie gehabt wird die Activity gleichzeitig der Listener sein
BatteryStateReceiver OnStatusChangesListener
View BatteryActivity
onReceive
register update <<implements>>
Dozenten: Patrick Förster, Michael Hasseler
Broadcasts (Beispiel II)
• Receiver + Listener
Programmieren für mobile Endgeräte 17
public class BatteryStateReceiver extends BroadcastReceiver { public interface OnStatusChangesListener { public void onStatusChanged(int status); } private OnStatusChangesListener listener; public void onReceive(Context context, Intent intent) { if (listener == null) return ; int status = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); listener.onStatusChanged(status); } public void setOnStatusChangedListener( OnStatusChangesListener listener) { this.listener = listener; } }
public class BatteryActivity extends Activity implements BatteryStateReceiver.OnStatusChangesListener { private BatteryStateReceiver receiver = new BatteryStateReceiver(); protected void onResume() { super.onResume(); Intent sticky = registerReceiver(receiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED) ); int status = sticky.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); Log.i(getClass().getName(), "Current battery status: " + status); receiver.setOnStatusChangedListener(this); } protected void onPause() { super.onPause(); this.unregisterReceiver(receiver); } public void onStatusChanged(int status) { ProgressBar bar = (ProgressBar) findViewById(…); bar.setProgress(status); TextView textView = (TextView) findViewById(…); textView.setText(status + "%"); }
Dozenten: Patrick Förster, Michael Hasseler
Batteriestatus im Emulator
• Batteriestatus im Emulator bleibt konstant
• Um ihn zu ändern, muss man sich per Telnet mit der Emulator-Console verbinden
• Beispielsweise über die Command-Zeile (Windows)
• Oder über einen anderen Client (z.B. Putty)
• Der Port wird in der Titelleiste des Emulator angezeigt
• Einmal verbunden kann man sich per help Hilfe zu den einzelnen
Befehlen holen
• Zum Ändern des Batteriestatus wird folgender Befehl benötigt:
Programmieren für mobile Endgeräte 18
telnet localhost <console-port>
power capacity <0…100>
Dozenten: Patrick Förster, Michael Hasseler
Eigene Intents
• Bisher lediglich System-Nachrichtigen abgefangen
• <action>, <category> und <data> sind frei konfigurierbar
• Als Beispiel soll eine Applikation zum Verwalten von „Todos“ erstellt werden
• Ein Receiver wird auf Nachrichten warten, die Todo enthalten
• Eine Activity auf Intents, die das Darstellen aller Todos anfordern
• Die Aktionsnamen sind:
• de.wwu.ziv.lehre.android.broadcast.TODO_VIEW
• de.wwu.ziv.lehre.android.broadcast.TODO_ADD
• Zudem sollen nur Daten vom Data-Scheme „todo“ mit dem Mime-Typ „text/plain“ verarbeitet werden
Programmieren für mobile Endgeräte 19
Dozenten: Patrick Förster, Michael Hasseler
Eigene Intents (Manifest)
• Manifest.xml
Programmieren für mobile Endgeräte 20
<activity android:name=".UtilizeActivity„ android:label="Utilize Todo"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="de.wwu.ziv.lehre.android.broadcast.TodoActivity„ android:label="@string/app_name" > <intent-filter> <action android:name="de.wwu.ziv.lehre.android.broadcast.TODO_VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="todo" android:mimeType="text/plain"/> </intent-filter> </activity> <receiver android:name=".TodoReceiver"> <intent-filter> <action android:name="de.wwu.ziv.lehre.android.broadcast.TODO_ADD"/> <data android:scheme="todo" android:mimeType="text/plain"/> </intent-filter> </receiver>
• Test-Activity
• MAIN
• LAUNCHER
• View-Activity
• TODO_VIEW
• DEFAULT
• text/plain, todo
• Receiver
• TODO_ADD
• text/plan, todo
Dozenten: Patrick Förster, Michael Hasseler
Eigene Intents (Persistenz)
• Der Receiver wird zum Auswerten der Todo-Daten erzeugt
• Nach der Auswertung allerdings umgehend „zerstört“
• Die View-Activity wird ebenfalls erst auf Anforderung gestartet und evtl. vom System zerstört
• Damit Daten den Aufruf des Receivers oder der Activity überleben, müssen sie persistent gespeichert werden
• Persistenz im Android-Umfeld ist Thema einer späteren Vorlesung
• Für das Beispiel sei angenommen, dass es „magische“ Klasse Persistence gibt, die das Speichern der Todo-Daten in eine Datei
übernimmt
• Im großen und ganzen unterscheidet sich das Lesen/Schreiben in eine Datei nicht groß vom „normalen“ Java
Programmieren für mobile Endgeräte 21
Dozenten: Patrick Förster, Michael Hasseler
Eigene Intents (TodoItem, TodoReceiver)
Programmieren für mobile Endgeräte 22
public class TodoItem { private String text; public TodoItem() {} public void setText(String text) {this.text = text;} public String getText() {return text;} public String toString() { return this.text == null ? "N/A" : this.text; } }
• TodoItem kapselt die Todo-Daten
• Extra-Daten werden über putExtra an ein Intent angehangen
• Diese Daten werden in einem Bundle (Schlüssel-Werte-Tabelle)
mitgeliefert
• Das Bundle wird über Persistence gespeichert
public class TodoReceiver extends BoadcastReceiver { public void onReceive(Context context, Intent intent) { try { Bundle bundle = intent.getExtras(); Persistence.saveBundle(context, bundle, "todo.temp"); Log.i(getClass().getName(), "New todo bundle received."); } catch (Exception e) { Log.e(getClass().getName(), e.getMessage()); } } }
Dozenten: Patrick Förster, Michael Hasseler
Eigene Intents (Todo-Activity)
Programmieren für mobile Endgeräte 23
public class TodoActivity extends ListActivity { private List<TodoItem> items; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_todo); try {items = loadItems();} catch (Exception e) items = new ArrayList<TodoItem>(); setListAdapter(new ArrayAdapter<TodoItem>(…)); } public List<TodoItem> loadItems() throws IOException { List<TodoItem> items = new ArrayList<TodoItem>(); List<Bundle> bundles = Persistence.loadBundles( this, "todo.temp„) for (Bundle bundle : bundles) items.add(createItemFromBundle(bundle)); return items; } public TodoItem createItemFromBundle(Bundle bundle) { TodoItem item = new TodoItem(); item.setText(bundle.getString("todo.text")); //… return item; } }
• erweitert ListActivity
• onCreate alle Todo-Daten laden
• Dazu die Daten als Liste von Bundles über Persistence anfragen
• Anhand der Daten die Todo-Objekte erzeugten
• Bundle-Daten auf die Todo-Klasse mappen
Dozenten: Patrick Förster, Michael Hasseler
Eigene Intents (Persistenz)
• Es fehlt noch eine Test-Activity, die Daten zum erstellen eines Todo-Objektes aufnimmt
• Diese Daten werden dann per Broadcast verteilt
Programmieren für mobile Endgeräte 24
Intent intent = new Intent("de.wwu.ziv.lehre.android.broadcast.TODO_ADD";); intent.setDataAndType(Uri.parse("todo:"), "text/plain"); EditText textView = (EditText) findViewById(…); intent.putExtra("todo.text", textView.getText().toString()); // … this.sendBroadcast(intent);
Dozenten: Patrick Förster, Michael Hasseler
Aufgabe
Binden Sie das Beispiel-Projekt „Broadcast“ in Ihren Workspace ein
1.Erweitern Sie das Datenmodel „Todo“ um ein weitere Eigenschaft „Priorität“, welche die Werte NIEDRIG, MITTEL und HOCH annehmen kann
2.Erweitern Sie die Test-Activity und View-Activity so, dass eine Priorität in der Test-Activity angegeben werden kann und diese in der View-Activity mit ausgegeben wird
3.Schreiben Sie einen eigenen Receiver für die folgende Aktion
• de.wwu.ziv.lehre.android.broadcast.TODO_REMOVE
• Wird ein Intent zu dieser Aktion entgegengenommen, so soll das
erste Elemente der ToDo-Liste gelöscht werden (Persistence.removeBundleAtIndex(…))
• Die Test-Activity sollte um einen Button erweitert, der einen Intent
mit für die obige Aktion per Broadcast absendet
Programmieren für mobile Endgeräte 25