Version | Anpassungen |
---|---|
1.0 (2021-05-05) | Initiale Version |
1.1 (2021-07-11) |
|
1.2 (2021-09-12) |
Wird die Extension in mehreren Apps verwandt, löst ein Antippen einer Aktionsschaltfläche das zugehörige Ereignis in allen Apps gleichzeitig aus. Das liegt an der Globalität der verwendeten Broadcast-Receiver. Abhilfe: Die Intents für die Aktionsschaltflächen sind mit dem App-Namen individualisiert. Beim Empfang einer Broadcast wird der Name überprüft und nur die passenden Nachrichten weiter verarbeitet. Kodular stellt nun auch viele Androidx-Funktionen bereit. Es muss nur noch "androidx.media.jar" eingebunden werden. Der Foreground-Service wird nun per UsesServices-Annotaion in der Quelle und manuell in die Rubrik broadcastReceivers in der generierten .aix-Datei eingetragen. Durch diese Änderungen ist nicht mehr notwendig zwei verschiedene Version der Extension für App Inventor und Kodular zu haben. Anpassung bestehender Kodular-Projekte: Es besteht interne Namensgleichheit. Deshalb kann die alte Version durch die neue ersetzt werden. Dies geht jedoch nur über einen Export des Projekts und einem Reimport.
|
1.3 (2021-09-15) | Wie oben. Nun auch für das Anklicken der Notification und das Löschen individualisierte Intents um Überschneidungen bei gleichzeitiger Benutzung der Extension in mehreren Apps zu verhindern. |
1.4 (2022-10-10) | Angepasst für SDK31 (Android 12): Alle PendingIntent erhalten das Flag FLAG_IMMUTABLE. Der Service erhält das Attribut exported = "true". Globale Exception wird gefangen und ins Log ausgegeben. |
2023-04-19 | Update der im Beispiel verwendeten Extension TaifunPlayer. Die alte Version funktioniert unter Android 11+ nicht mehr. |
2023-08-15 | Seit Android 12 (auch schon früher?) wird die App nicht mehr automatisch geöffnet, wenn man die Notification anklickt. Fabio hat mir gezeigt, was geändert werden musste. |
1.6 (2023-11-23) | Anpassung an Android 14: Broadcast-Receiver benötigt zusätzliche Flags |
1.7 (2024-10-11) | Anpassung an Android 14: Forgeground-Service benötigt eine Typ-Angabe: Foreground service types are required |
2.0 (2024-10-24) |
Die Extension wurde komplett überarbeitet:
|
2.1 (2024-12-17) | Viele Player setzen zwar einen WakeLock, der reicht jedoch in neueren Android-Version nicht aus, um das
aktiv zu halten. Die Eigenschaften UseForegroundService und UseWakeLock hinzugefügt. |
Schaltflächen in der Media-Notification
Meta-Daten und Erscheinungsbild
Fortschrittsbalken (ProgressBar, ab Android 10, API Level 29)
Das ZIP-Archiv UrsAI2MediaNotification zum Download. Das Archiv enthält den Quellcode, das kompilierte Binary zum Upload in den App Inventor und eine Beispiel-Anwendung.
Gerät | Testergebnis |
---|---|
Smartphone Redmi 5 Plus, Android 8.1 | Companion Version 2.6.1 führt zu einem Fehler. Im Companion stehen nicht alle notwenigen Klassen zur Verfügung. Kompilierte APK funktioniert einwandfrei (kein Progressbar, den gib es erst ab Android 10. |
Smartphone Samsung A02s, Android 12 | Sowohl Companion als auch APK funktionieren einwandfrei. |
Tablet Yestel T13_EEA, Android 13 | Sowohl Companion als auch APK funktionieren einwandfrei. |
Smartphone ZTE Blade A73, Android 13 | Sowohl im Companion als auch in der APK wird kein Progressbar angezeigt. Als
SmallIcon (das Icon in der Taskbar) wird immer das App-Icon angezeigt. Die weiteren Features funktionieren einwandfrei. |
Seit dem API-Level 21 (LolliPop 5.0) gibt es die Media Controls, eine besondere Form der Benachrichtigung (Notification), die eigens zur Steuerung von Mediaplayern entwickelt wurde. Der Clou ist, dass auch Steuerimpulse externer Wiedergabegeräte ausgewertet werden.
Diese Extension ist ausschließlich zu Verwaltung von Media-Notifications gedacht. Wer reguläre Benachrichtigungen erstellen will, sollte die Extension UrsAI2Notifier benutzen.
Mit dem API-Level 26 (Version Oreo 8.0) hat Android das Konzept der Benachrichtigungskanäle eingeführt (NotificationChannel, bei manchen Geräten auch Benachrichtigungskategorien genannt). Sämtliche Benachrichtigungen müssen ab API Level 26 einem solchen Kanal zugeordnet werden. Bis einschließlich API Level 25 sind alle den Benachrichtigungskanal betreffende Angaben und Funktionen wirkungslos.
Das Benachrichtigungskonzept ist relativ komplex. Android erlaubt es, Einstellungen sowohl auf der Geräteebene, als auch auf der Kanalebene und bei den Benachrichtigungen selbst vorzunehmen.
Viele Eigenschaften der Benachrichtigungen, z.B. die Wichtigkeit (siehe Importance, IMPORTANCE_DEFAULT ff.), werden auf der Kanal-Ebene festgelegt. Diese Eigenschaften werden von der App bei der Anlage des Kanals festgelegt. Der Benutzer hat mit Hilfe die App Eigenschaften vollen Zugriff auf die meisten der Kanalattribute und kann sie nach belieben modifizieren. Die Modifikationen des Benutzers sollen nicht überschrieben werden. Deshalb können die Kanaleigenschaften nachträglich nicht mehr per Programm geändert werden. Das geht sogar soweit, dass, wenn ein Kanal gelöscht und anschließend wieder angelegt wird, er mit den zuletzt vorhandenen Einstellungen erzeugt wird. Das "Löschen" ist praktisch nur ein "Verstecken". Lediglich die Eigenschaften Name und Description eines Kanals lassen sich nachträglich ändern. Die Beseitigung von Schreibfehler wäre ansonsten unmöglich.
Man sollte sich also sehr gut überlegen, welche Eigenschaften einem Kanal zugewiesen werden.
Die Extension bietet die Eigenschaften ChannelID, ChannelName, ChannelDescription und ChannelImportance zur Festlegung der Kanaleigenschaften. ChannelImportance legt u.a. fest, unter welchen Bedingungen eine Benachrichtigung angezeigt wird (siehe Importance, IMPORTANCE_DEFAULT ff.). Der Kanal wird angelegt, wenn das erste Mal ShowNotification aufgerufen wird.
Will man andere Eigenschaften ändern, muss ein neuer Kanal (neue ChannelID) verwendet werden. Den bestehenden Kanal versteckt man sinnvollerweise beim Start der App mit HideChannel.
Viele Mediaplayer-Komponenten setzen einen WakeLock, um das Gerät während der Wiedergabe einer Audiodatei aktiv zu halten. In älteren Versionen war dies ausreichend. Seit Android 8.0 (Oreo, API Level 26) hat sich die Android Policy dahingehend geändert, dass eine App, die verhindert, dass das Gerät in den Ruhezustand (Doze Mode) geht, dies dem Benutzer sichtbar anzeigen muss. Seitdem muss zusätzlich zum WakeLock ein ForegroundService gestartet werden. Dieser Service erzwingt die Anzeige einer Benachrichtigung, die den Benutzer darauf hinweist, dass die Batterieoptimierung verhindert wird.
Über die Designer-Eigenschaft UseForegroundService kann eingestellt werden, dass die Extension einen Foreground-Service startet und über diesen die MediaNotification angezeigt wird. Für den Fall, dass der Media Player selbst keinen WakeLock setzt, kann dies auch über die Extension über die Eigenschaft UseWakeLock erfolgen. Die Verwendung von UseWakeLock impliziert UseForegroundService, d.h. die MediaNotification wird über einen Foreground-Service angezeigt.
Sämtliche Änderungen haben keine sofortige Wirkung. Erst beim nächsten Aufruf von ShowNotification werden sie übernommen.
Die Schaltfläche mit dem Play(⯈)- bzw. dem Pause(⏸)-Symbol wird immer angezeigt. Welches der beiden angezeigt wird, kann über die Funktionen SetStatePaused (zeigt ⯈ an) und SetStatePlaying (zeigt ⏸) an) eingestellt werden.
Die anderen Schaltflächen lassen sich über die Support...-Funktionen an- und abstellen, je nachdem welche Funktionen die App unterstützt.
Über die Methode SetActionIcon lassen sich die auf den Schaltflächen der Benachrichtigung angezeigten Symbole anpassen. Es können entweder die Bezeichnung eines System-Icons oder der Name einer hochgeladen Grafik-Datei angegeben werden. So kann man z.B. die Symbole für Previous und Next durch Like und Dislike ersetzen. Insgesamt ist die Benachrichtigung in der Lage bis zu fünf Aktionsschaltflächen anzuzeigen.
WWird DeleteAble gesetzt, kann der Anwender die Benachrichtigung, z.B. durch Wischen, löschen. Wenn die Notification vom Anwender gelöscht wird, wird das Ereignis UserCanceled ausgelöst.
Die Hintergrundfarbe ist nicht einstellbar, sondern wird von Android automatisch aus dem Album-Image ermittelt.
Über die Funktion SetMetaData und SetMetaDataEx können Titel, Interpret und und ein Bild (AlbumImage) angegeben werden, die in der Benachrichtigung angezeigt werden.
Für AlbumImage sind folgende Angaben möglich:
Typ | Präfix | Beispiel |
---|---|---|
URL | http oder https | https://ullisroboterseite.de/android-AI2-MediaNotification/MediaNotification.png |
Asset | // oder nichts | //MediaNotification.png oder einfach nur MediaNotification.png |
Datei, relativer Pfad | / | /data/user/0/appinventor.ai_bienonline.UrsMediaNotificationAI2/MediaNotification.png |
Datei, absoluter Pfad | file:/// | file:///Android/data/appinventor.ai_bienonline.UrsMediaNotificationAI2/MediaNotification.png |
Bei der Angabe einer URL ist zu beachten, dass das Bild synchron geladen wird. Dies kann bei großen Dateien oder langsamen Verbindungen einige Zeit in Anspruch nehmen. Eine andere Möglichkeit ist es, eine Extension zu verwenden, die einen asynchronen (nebenläufigen) Download der Datei ermöglicht (z.B. die Extension ImageLoader).
Um den Zugriff auf die Dateien zu erleichtern, gibt es die Funktionen
Die Möglichkeit, eigene Grafiken für das SmallIcon einzubinden (Eigenschaft SmallIconImage), besteht erst ab API Level 23. Deshalb gibt es zusätzlich die Möglichkeit über die Eigenschaft SmallSystemIcon ein System-Icon auszuwählen (für mögliche Angaben siehe: System Notification Icons). Die Auswahlregel ist wie folgt:
Bedingung | Auswahl | ||
---|---|---|---|
API Level | SmallIconImage | SmallSystemIcon | |
≥ 23 | gefüllt | X | SmallIconImage |
leer | gefüllt | SmallSystemIcon | |
leer | leer | kein Icon | |
< 23 | X | gefüllt | SmallSystemIcon |
X | leer | kein Icon |
Ab Android 10 kann die Media-Notification einen Fortschrittsbalken (ProgressBar) und eine Suchleiste (SeekBar) anzeigen. Dazu muss die Spieldauer (Duration) des Media-Objekts und die aktuelle Abspielposition (CurrentPosition) bekannt sein.
SetMetaDataEx erlaubt die Angabe der Spieldauer, die für die Anzeige eines Fortschrittsbalkens (ProgressBar) benötigt wird. Einige Media-Player, wie der im Beispiel benutzte TaifunPlayer, liefern diese Information. Dazu muss der Player aber entsprechend initialisiert sein.
Der Funktion ShowWithProgressBar zeigt einen Fortschrittsbalken an. Er startet an mit der Position, die im Parameter CurrentPosition übergeben wird. Danach läuft er selbständig weiter. Wenn man die Abspielposition per Programm ändert, muss man den Fortschrittsbalken wieder mit ShowWithProgressBar synchronisieren.
Bei Versionen älter als Android 10, funktionieren SetMetaDataEx und ShowWithProgressBar ebenfalls, allerdings wird kein Fortschrittsbalken angezeigt.
Man stellt die initialen Eigenschaften der Komponente passend ein, übergibt die Media-Metadaten (SetMetaData oder SetMetaDataEx) und ruft anschließend ShowNotification bzw. ShowWithProgressBar auf. Von nun an braucht man eigentlich nur noch auf die Ereignisse reagieren. Je nach Ereignis stellt man den Mediaplayer passend ein, passt Eigenschaften der Komponente an und ruft wieder ShowNotification auf.
In der Regel soll der Mediaplayer nicht nur von der Media-Notification gesteuert werden. Meist enthält die App ebenfalls Steuerelemente, die den Player kontrollieren sollen. Um die Blöcke im Projekt einfach zu gestalten, gibt es die Funktionen Play, Pause, Rewind, etc., die die gleichen Ereignisse auslösen, wie die Notification. Es ist also keine doppelte Programmierung für die Steuerelemente auf der Benutzeroberfläche notwendig. Es muss lediglich die passende Funktion aufgerufen werden.
Fehler werden über das Ereignisse Screen.ErrorOccurred gemeldet.
Code | Text | Bedeutung | Anmerkung |
---|---|---|---|
17500 | Album image not found. | Das angegebene Album-Image konnte nicht geladen werden. | Betroffene Funktionen sind SetMetaData und SetMetaDataEx. |
17501 | SmallSystemIcon invalid. | Die Angabe beim Parameter SmallSystemIcon kann nicht aufgelöst werden. | Betroffene Funktion ist ShowNotification. Die Benachrichtigung wird ohne ein Icon angezeigt. |
17502 | SmallIconImage invalid. | Die Grafik-Datei beim Parameter SmallIconImage kann nicht geladen werden. | Betroffene Funktion ist ShowNotification. Die Benachrichtigung wird ohne ein Icon angezeigt. |
17503 | Image file name invalid. | Die Angabe beim Parameter IconName kann nicht aufgelöst werden. | Betroffene Funktion ist SetActionIcon. Das Symbol wird nicht geändert. |
17504 | ActioNo invalid. | Die Aktionsnummer muss im Bereich 1..6 liegen. | Betroffene Funktion ist SetActionIcon. Es wird kein Symbol geändert. |
17505 | Not an UrsMediaHelper component. | Die übergebene Komponente ist nicht vom Typ UrsMediaHelper. | Betroffene Funktion ist SetMetaDataFromMH. |
GetAppDataDir()
auf meinem Testgerät für das Beispiel-Programm liefert folgendes Ergebnis:GetDownloadDir("file://")
auf meinem Testgerät
für das Beispiel-Programm liefert folgendes Ergebnis:getVolumes("file://)
gibt auf meinem Testgerät folgende Elemente:Das Download-Archiv enthält ein Beispiel-Projekt in zwei Versionen, je eine für App Inventor und Kodular:
UrsMediaNotification
Das Download-ZIP-Archiv enthält im Verzeichnis example eine Beispielprojektdatei.
Das Beispiel demonstriert, wie ein Mediaplayer mit der Extension gesteuert werden kann. Als Player wird der TaifunPlayer verwendet. Es können vier verschiedene Musikstücke abgespielt werden.
In Kodular können die Fonts der Schaltflächen individuell eingestellt werden. U.a. steht dort der Material Icons Font mit vielen nützlichen Symbolen von Google zur Verfügung. Für den App Inventor wird die Extension MyFonts von Anke benutzt.
Die Steuerelemente werden je nach aktuellen Zustand der App freigegeben oder gesperrt. Sie bewirken
Die zugehörige MediaNotification dupliziert diese Steuerelemente. Es werden nur die freigegeben Elemente angezeigt (Off Broadway ist das letzte Stück in der Playlist, SkipToNext ist deshalb gesperrt und wird nicht angezeigt):
(Screenshot Xiaomi Redmi 5 Plus, Android Oreo 8.1)
Die Anzeige eines Stoppsymbols steht in der Android Media-Notification leider nicht zur Verfügung.
In Android 10 wird die Hintergrundfarbe automatisch an die Farbe des Album-Bildes angepasst.
(Screenshot Google Pixel 2 XL API 30 Emulator)
Ein Fortschrittsbalken (ab Android 10) wird eingeblendet, wenn man die Notification aufzieht:
Die Schaltflächen auf dem Screen führen keine direkte Aktion aus. Sie rufen vielmehr die entsprechende Methode in der Extension aus. Die wiederum löst das zugehörige Ereignis aus, das auch beim Anklicken der Aktionsschaltfläche in der Benachrichtigung ausgelöst worden wäre. Dadurch werden beide Benutzeraktionen, das Klicken in der Benachrichtigung und das Klicken der Schaltfläche auf dem Screen der App, durch den gleichen Code bedient.
Die Prozedur SetMusic füllt die Extension mit den Metadaten (Titel, Interpret, Album-Bild). Die MP3-Dateien in dem Beispielprojekt enthalte bereits alle benötigten Metadaten, so dass diese einfach mit Hilfe der UrsMediaHelper-Extension übergeben werden können. Die Metadaten lassen sich z.B. mit dem Programm Mp3tag einsehen und bearbeiten.
Bei der Initialisierung wird dafür gesorgt, dass die App gleich zu Beginn korrekt arbeitet. SetMusic stellt die Metadaten und die Musikquelle für den TainfunPlayer auf das erste Musikstück ein. Mit der Kombination MediaPlayer.Start und MediaPlayer.Pause wird erreicht, dass die Eigenschaften Duration und CurrentPosition des Mediaplayers abgerufen werden können, ohne dass er anfängt zu spielen. MediaNotification.SetStatePaused und MediaNotification.ShowNotification zeigen die Benachrichtigung im Zustand Pause (Symbol ⯈) an.
BBeim Weiterschalten zum nächsten Musikstück (SkipToNext) und beim Zurückschalten (SkipToPrevious) wird zunächst der der aktuelle Zustand des Mediaplayers in einer lokalen Variablen zwischengespeichert. Dieser soll nach dem Umschalten wieder hergestellt werden. Danach wird der Player gestoppt. Das nächste (das vorhergehende) Musikstück wird ermittelt und dabei dem Zustand der Playlist entsprechenden Schaltflächen freigegeben bzw. gesperrt. Die Blöcke werden über die Prozedur SetMusic mit den Daten des neu ausgewählten Musikstücks gefüttert. Zum Schluss werden entsprechend des gespeicherten Player-Zustands die Ereignisse OnPlay bzw. OnPause ausgelöst. In den Ereignissen wird dafür gesorgt, dass der Mediaplayer und die MediaNotification passen eingestellt werden.
Für die Erstellung eigener Extensions habe ich einige Tipps zusammengestellt: AI2 FAQ: Extensions entwickeln.