Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine...

25
Dozenten: Patrick Förster, Michael Hasseler Programmieren für mobile Endgeräte SS 2013/2014

Transcript of Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine...

Page 1: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

Dozenten: Patrick Förster, Michael Hasseler

Programmieren für mobile Endgeräte SS 2013/2014

Page 2: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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);

Page 3: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 4: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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>

Page 5: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 6: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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>

Page 7: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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>

Page 8: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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"/>

Page 9: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 10: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 11: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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);

Page 12: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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>

Page 13: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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)

Page 14: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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);

Page 15: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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 );

Page 16: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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>>

Page 17: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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 + "%"); }

Page 18: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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>

Page 19: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 20: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 21: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 22: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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()); } } }

Page 23: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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

Page 24: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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);

Page 25: Programmieren für mobile Endgeräte · android:name=".MyActivity„ android:label=„Eine Activity" > intent-filter>

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