Version | Anpassungen |
---|---|
1.0 (2021-04-26) | Initiale Version |
2.0 (2021-05-15) | ScreenToOpen hat bei Kodular nicht funktionert, wenn der Package Name geändert wurde. |
2.1 (2021-06-12) | - Eigenschaft FlagNewTask im
AlarmIntent wurde nicht korrekt ausgewertet. Ab Android 10
muss dieses Flag auf true gesetzt sein, damit eine Activity
gestartet werden kann. - Default für Eigenschaft FlagNewTask im AlarmIntent auf true gesetzt. - Parameter WakeUp bei den Funktionen CreateAlarm... wurde nicht korrekt ausgewertet. |
2.2 (2021-07-21) |
Bei längeren Alarmzeiten bei ausgeschaltetem Bildschirm hat der Alarm das Gerät nicht zuverlässig aufgeweckt.
Siehe Abschnitt Regeln für die zu startende Activity |
2.3 (2022-10-13) | Angepasst für SDK31 (Android 12): Alle PendingIntent erhalten das Flag FLAG_IMMUTABLE. android.permission.SCHEDULE_EXACT_ALARM angefordert. Globale Exception wird gefangen und ins Log ausgegeben. |
Obwohl die Android Dokumentation dringend empfiehlt, mit Benachrichtigungen zu arbeiten (siehe developer.android.com), ist es unter bestimmten Umständen dennoch notwendig, dass eine App zu einem bestimmten Zeitpunkt gestartet wird. Mit dieser Extension können Apps zu festgelegten Zeitpunkten gestartet werden. Wer zu bestimmten Zeiten Benachrichtigungen erzeugen will, muss die Extension UrsAI2Notifier benutzen.
Hinweis für Benutzer von Geräten mit Android 10 und aufwärts:
Zum Alarmzeitpunkt wird eine Klasse der Extension im Hintergrund gestartet. Seit Android 10 benötigt eine App zusätzliche Berechtigungen (SYSTEM_ALERT_WINDOW), wenn sie eine andere App starten will, während sie selbst im Hintergrund ausgeführt wird (siehe Restrictions on starting activities from the background, letzter Punkt). Diese Berechtigung kann nicht per Programm gesetzt werden, sondern muss von Anwender über einen Dialog bestätigt werden (s. Zusätzliche Berechtigungen ab Android 10 und bei Xiaomi-Geräten). Eine mögliche Lösung für das Problem wird im Beispiel vorgestellt.
Um eine neue Activity zu starten, muss ab Android 10 die Eigenschaft FlagNewTask im AlarmIntent auf true gesetzt sein!
Hinweis für Benutzer von Geräten des Herstellers Xiaomi:
Zum Alarmzeitpunkt wird eine Klasse der Extension im Hintergrund gestartet. Bei Xiaomi-Geräten wird, unabhängig von der installierten Android-Version, eine weitere Berechtigung benötigt, wenn eine App eine andere App starten will, während sie selbst im Hintergrund ausgeführt wird. Diese Berechtigung nicht per Programm gesetzt werden, sondern muss von Anwender über einen Dialog bestätigt werden (s. Zusätzliche Berechtigungen ab Android 10 und bei Xiaomi-Geräten). Es kann auch nicht per Programm abgefragt werden, ob diese Berechtigung bereits erteilt wurde. Eine mögliche Lösung für das Problem wird im Beispiel vorgestellt.
Benutzer von Xiaomi-Geräten mit der Android Version 10 und aufwärts müssen beide Berechtigungen erteilen.
Inhaltsverzeichnis
Regeln für die zu startende Activity
Zusätzliche Berechtigungen ab Android 10 und bei Xiaomi-Geräten
Das ZIP-Archiv UrsAI2Alarm zum Download. Das Archiv enthält den Quellcode, das kompilierte Binary zum Upload in den App Inventor und eine Beispiel-Anwendung.
Hinweis:
Die Extension funktioniert nicht im Companion. Alarme werden über Broadcast-Nachrichten ausgelöst. Damit eine App auch im geschlossenen Zustand Broadcast-Nachrichten empfangen kann, muss die App einen Broadcast-Receiver bereitstellen. Dieser muss im Manifest der App deklariert werden. Letzteres ist beim Companion nicht möglich.
Eine Möglichkeit wäre die Companion-APK zu patchen: https://ullisroboterseite.de/android-ai2-faq.html?topic=patchcompanion
Aufgaben, die vom Betriebssystem übernommen werden sollen, werden diese über einen Intent mitgeteilt. Ein Intent (deutsch: Vorhaben, Intention) ist eine abstrakte Beschreibung einer auszuführenden Operation. Die Operation, die zum Alarmzeitpunkt ausgeführt werden soll, wird durch einen Intent festgelegt.
Die Extension enthält zwei Blöcke:
UrsAI2Alarm: Stellt Eigenschaften und Methoden bereit, die die Erzeugung von Alarmen ermöglichen.
AlarmIntent: Legt die Operation fest, die zum Alarmzeitpunkt ausgeführt werden soll.
Der UrsAI2Alarm-Block kann Alarme erzeugen und auch wieder löschen. Ein Alarm wird eindeutig durch den RequestCode identifiziert. Beider Erstellung eines Alarms muss dieser festgelegt werden und kann zum Löschen eines noch nicht ausgelösten Alarms genutzt werden.
Leider bietet Android keine Möglichkeit, abzurufen, welche Alarme noch auf die Auslösung warten. Man muss dies selbst verwalten. Das Beispiel Notification Alarm Test zeigt, wie dies geschehen kann.
CreateAlarm und CreateAlarmEx erzeugen Alarme nach einer angegebenen Verzögerung Der Android AlarmManager führt die hinterlegte Aktion auch dann aus, wenn die App bereits geschlossen ist. CreateAlarmAt erzeugt den Alarm zu einem bestimmten Zeitpunkt.
CancelAlarm löscht einen noch nicht ausgelösten Alarm.
Android benutzt Intent-Objekte, um dem System Aufträge zu erteilen. Ein Intent enthält alle Angaben, die notwendig sind, den Auftrag auszuführen. Der AI2 ActivityStarter z.B. benutzt intern Intents um andere Apps zu starten.
Über ein AlarmIntent-Objekt, dass den CreateAlarm...-Funktionen übergeben wird, wird festgelegt, welche Aktion ausgeführt werden soll, wenn der Alarmzeitpunkt eintritt.
Die AlarmIntent-Komponente ist eine Sammlung von Eigenschaften. Sie besitzt weder Funktionen noch Ereignisse.
Der Aktionstyp (ActionType) bestimmt, welche Aktion mit dem Intent ausgelöst wird:
Damit Apps (Activities) auch dann das Gerät verlässlich aufwecken, muss die zu startende App entsprechend ausgerüstet sein1). Zunächst muss die App über entsprechende Berechtigungen verfügen. Dies müssen bei Xiaomi-Geräten vom Benutzer in einem separatem Dialog erteilt werden (siehe folgenden Abschnitt).
Dann muss die gestartete Activity (beim App Inventor ist dies ein Screen) anfordern, dass der Bildschirm angeschaltet wird und die Anzeige auf dem Lock-Screen erfolgen soll. Ebenfalls muss die Eingabe des PIN-Codes abgeschaltet werden. Dies kann für AI2- bzw. Kodular-Apps über die Funktion EnableShowOnLockScreen erreicht werden. Außerdem muss die Activity einen WakeLock setzen, weil das Gerät sonst unmittelbar wieder in den Lock-Zustand übergeht.
Wird ein AI2-Screen als Ziel gewählt, empfiehlt sich folgender Code-Block beim Screen.Initialize-Ereignis:
1) Die Test erfolgten auf einem Xiaomi Redmi 5 plus mit Android 8.1. Mir ist nicht bekannt, inwieweit andere Geräte anders behandelt werden müssen.
Hinweis: Benutzer von Xiaomi-Geräten mit der Android Version 10 und aufwärts müssen beide Berechtigungen erteilen!
Seit Android 10 benötigt eine App zusätzliche Berechtigungen (SYSTEM_ALERT_WINDOW), wenn sie eine andere App starten will, während sie selbst im Hintergrund ausgeführt wird (siehe Restrictions on starting activities from the background, letzter Punkt). Diese Berechtigung kann nicht per Programm gesetzt werden, sondern muss von Anwender über einen Dialog bestätigt werden.
Die Extension stellt die Funktion CheckBackgroundLaunchPermission bereit. Die Funktion prüft, ob die installierte Android Version einen API-Level von 29 (Android 10) oder höher hat und ob dann die Berechtigung SYSTEM_ALERT_WINDOW erteilt wurde. Falls es notwendig ist, dass diese Berechtigung noch erteilt wird, wird ein Dialog zum Erteilen der Berechtigung geöffnet:
Deutsche Variante | Englische Variante |
Das Ergebnis der Berechtigungsabfrage wird über das Ereignis AfterBackgroundPermssion zurück geliefert. Der neue Zustand der Berechtigung ist nicht sofort nach dem Schließen des Berechtigungsdialogs verfügbar. Das Ereignis wird deshalb erst nach einer kurzen Verzögerung (Eigenschaft PermissionDelay, Vorreinstellung 500 ms) nach dem Schließen des Dialogs ausgelöst. Während dieser Zeit ist der Zustand der Berechtigung undefiniert. Es empfiehlt sich, darauf zu achten, dass währenddessen keine unsinnigen Daten angezeigt werden oder problematische Eingabe gemacht werden können. Am besten stellt man alle betroffenen Bildschirmelemente in ein VerticalArrangement. Dieses kann man zeitweise unsichtbar schalten oder z.B. über die Funktion EnableArrangement der UrsAI2ComponentGroup Extension deaktivieren (s.u. Beispiel).
Zur detaillierten Behandlung dieser Berechtigung stehen zur weiterhin Verfügung:
Bei Xiaomi-Geräten wird, unabhängig von der installierten Android-Version, eine weitere Berechtigung benötigt, wenn eine App eine andere App starten will, während sie selbst im Hintergrund ausgeführt wird. Diese Berechtigung nicht per Programm gesetzt werden, sondern muss von Anwender über einen Dialog bestätigt werden (s. Zusätzliche Berechtigungen ab Android 10 und bei Xiaomi-Geräten). Erschwerend kommt hinzu, dass nicht per Programm abgefragt werden kann, ob diese Berechtigung bereits erteilt wurde.
Für die Behandlung dieser Problematik stehen folgende Elemente zur Verfügung:
Der Xiaomi-Berechtigungseditor:
Deutsche Variante | Englische Variante |
Das Beispielprojekt zeigt eine Möglichkeit, wie diese Berechtigungen behandelt werden können.
Man kann die Berechtigungen auch über die Settings-App vergeben. Dieser Blog-Eintrag zeigt wie es geht: Enable “display pop-up windows” on new Xiaomi phones. Eine andere Zugangsmöglichkeit: Lange auf das Symbol der App drücken, dann "App-Info" auswählen.
Type erlaubt die Angaben 0..3. Es werden die folgenden Methoden aufgerufen:
Reicht der aktuelle API-Level nicht aus, wird der Typ angepasst: 1→0; 2→0; 3→1→0;
Wenn der Wert für Delay kleiner als Null ist, wird die Benachrichtigung nicht angelegt und Screen.ErrorOccurred mit ErrorNumber 17007 ausgelöst.
Wenn das übergebene Objekt im Parameter AlarmIntentObject nicht vom Typ AlarmIntent ist, wird die Benachrichtigung nicht angelegt und Screen.ErrorOccurred mit ErrorNumber 17006 ausgelöst.
Type erlaubt die Angaben 0..3. Es werden die folgenden Methoden aufgerufen:
Reicht der aktuelle API-Level nicht aus, wird der Typ angepasst: 1→0; 2→0; 3→1→0;
Der Wert für Millis kann aus einer Clock-Komponente über
die Methode GetMillis gewonnen werden.
Wenn der Wert für Millis kleiner als die aktuelle Systemzeit ist, wird die Benachrichtigung nicht angelegt und Screen.ErrorOccurred mit ErrorNumber 17008 ausgelöst.
Wenn das übergebene Objekt im Parameter AlarmIntentObject nicht vom Typ AlarmIntent ist, wird die Benachrichtigung nicht angelegt und Screen.ErrorOccurred mit ErrorNumber 17006 ausgelöst.
Type erlaubt die Angaben 0..3. Es werden die folgenden Methoden aufgerufen:
Reicht der aktuelle API-Level nicht aus, wird der Typ angepasst: 1→0; 2→0; 3→1→0;
Wenn der Wert für Delay kleiner als Null ist, wird die Benachrichtigung nicht angelegt und Screen.ErrorOccurred mit ErrorNumber 17007 ausgelöst.
Wenn das übergebene Objekt im Parameter UrsNotificationObject
nicht vom Typ UrsNotification ist, wird die Benachrichtigung
nicht angelegt und
Screen.ErrorOccurred mit ErrorNumber 17005 ausgelöst.
Wenn das übergebene Objekt im Parameter UrsIntentObject
nicht vom Typ UrsIntent ist, wird die Benachrichtigung nicht
angelegt und
Screen.ErrorOccurred mit ErrorNumber 17006 ausgelöst.
Hinweis:
Bei den meisten Flags ist mir
nicht wirklich klar, was sie genau bewirken. Ich habe mich zwar um eine passende Übersetzung bemüht,
bin mir aber nicht sicher on das überall gelungen ist. Im Zweifelsfall bitte die verlinkte Originalreferenz
benutzen.
Ab Android 10 muss die Eigenschaft FlagNewTask auf true gesetzt sein.
Es gibt ein Problem beim fortlaufenden Erzeugen von Alarmen mit Intents vom Typ Screen, wenn dies nicht Screen1 ist und anschließend mit control.close application geschlossen werden. Grund sind interne Abläufe in App-Inventor-Programmen. Am besten ruft man über den Alarm Screen1 auf, wertet den Parameter control.get start value aus und ruft von Screen1 über control.open another screen den entsprechenden Screen auf.
Der Aktionstyp (ActionType) bestimmt, welche Aktion mit dem Intent ausgelöst wird:
Die Spalte S markiert Felder die für den ActionType Screen relevant sind, die Spalte L die für den ActionType Launcher.
Bezeichnung | Typ | S | L | Funktion | Voreinstellung |
---|---|---|---|---|---|
ActionType | choice (nur im Designer) |
X | X | Legt den Aktionstyp fest. Mögliche Werte sind Screen oder Launcher. Je nach Aktionstyp werden verschiedene Felder ausgewertet. | None |
ActivityAction | text | - | X | Die auszuführende allgemeine Aktion. | -none- |
ActivityClass | text | - | X |
Gibt den Klassenteil der spezifischen Komponente an, die gestartet werden soll.
ActivityPackage und ActivityClass definieren die Activity, die beim Antippen der Benachrichtigung gestartet werden soll. |
-none- |
ActivityPackage | text | - | X | Gibt den Paketnamen der spezifischen Komponente an, die gestartet werden soll. ActivityPackage und ActivityClass definieren die Activity, die beim Antippen der Benachrichtigung gestartet werden soll. |
-none- |
DataType | text | - | X | Gibt den MIME-Typ an, der an die Activity übergeben werden soll | -none- |
DataUri | text | - | X | Gibt den Daten-URI an, der zum Starten der Activity verwendet wird. | -none- |
FlagClearTask | boolean | X | X | Die gestartete Aktivität wird ein neuer Task im Verlaufsstapel. | false |
FlagClearTop | boolean | X | X | Wenn die zu startende Activity bereits in dem aktuellen Task ausgeführt wird, wird keine neue Instanz dieser Activity gestartet. Stattdessen werden alle anderen darüber liegenden Activitys geschlossen und dieser Intent an die alte Aktivität, jetzt an der Spitze des Activity-Stacks, als neuer Intent übergeben. | false |
FlagExcludeFromRecent | boolean | X | X | Die neue Activity wird nicht in der Liste der kürzlich gestarteten Activitys gespeichert. | false |
FlagNewTask | boolean | X | X | Diese
Activity
wird zum Beginn eines neuen
Task in diesem Verlaufsstapel. Ab Android 10, muss dieses Flag auf true gesetzt sein. |
true |
FlagNoAnimation | boolean | X | X | Dieses Flag verhindert, dass das System eine Aktivitätsübergangsanimation erzeugt, um zum nächsten Aktivitätsstatus zu gelangen. | false |
FlagNoHistory | boolean | X | X | Die neue Activity wird nicht im Verlaufsstapel gespeichert. Sobald der Benutzer sie verlässt, ist die Activity beendet. | false |
FlagPreviousIsTop | boolean | X | X | Wenn dieser Intent dazu verwendet wird, um eine neue Activity zu starten, wird die aktuelle Activity nicht als oberste Activity gewertet. Die vorherige Activity wird als oberste Activity verwendet, wobei davon ausgegangen wird, dass die aktuelle Activity sofort beendet wird. | false |
FlagReorderToFront | boolean | X | X | Dieses Flag bewirkt, dass die gestartete Activity an die Spitze des Verlaufsstapels verschoben wird, wenn sie bereits ausgeführt wird. | false |
FlagResetTaskIfNeeded | boolean | X | X | Wird diese Activity entweder in einem neuen Task gestartet oder an die Spitze eines vorhandenen Task gebracht, wird sie als Einstieg für den Task gestartet. Dies führt dazu, dass alle Zugehörigkeiten erstellt werden, die erforderlich sind, um diesen Task in den richtigen Zustand zu versetzen (Activity hineinverschieben oder entfernen) oder diesen Task bei Bedarf einfach in seinen Ausgangszustand zurückzusetzen. | false |
FlagSingleTop | boolean | X | X | Wenn gesetzt, wird die Activity nicht gestartet, wenn sie bereits oben im Verlaufsstapel ausgeführt wird. | false |
FlagTaskOnHome | boolean | X | X | Dieses Flag bewirkt, dass ein neu startender Task über der aktuellen Home Activity (falls vorhanden) platziert wird. Das heißt, wenn der Benutzer die Back-Taste drückt, wird er immer wieder zum Home Screen zurückkehren, auch wenn dies nicht die letzte Activity war, die er ausgeführt hat. Dies kann nur in Verbindung mit FLAG_ACTIVITY_NEW_TASK verwendet werden. | false |
ScreenStartValue | text | X | - | Der Startwert, der dem zu öffnenden Screen übergeben werden soll. Abrufbar per Control.getStartValue . | -none- |
ScreenToOpen | text | X | - | Der Name des Screens, der beim Alarm geöffnet werden soll. Siehe auch Hinweis zu Beginn diese Abschnitts. | "Screen1" |
Das Download-Archiv enthält ein Beispiel-Projekt:
UrsAlarmTest
Hinweis:
Die Extension funktioniert nicht im Companion. Alarme werden über Broadcast-Nachrichten ausgelöst. Damit eine App auch im geschlossenen Zustand Broadcast-Nachrichten muss die App einen Broadcast-Receiver bereitstellen. Dieser muss im Manifest der App deklariert werden. Letzteres ist beim Companion nicht möglich.
Das Projekt besitzt zwei Schaltflächen:
Jeweils nach der Erstellung des Alarms wird die App geschlossen. Der Alarm wird also bei nicht geöffneter App ausgeführt.
Android bietet leider keine Möglichkeit zu ermitteln, welche Alarme noch ausstehen. Das Beispiel Notification Alarm Test zeigt, wie man Alarme verwalten kann.
Das Beispiel zur AppLauncher-Extension zeigt, wie die besonderen Berechtigungen bei Android 10 oder Xiaomi-Geräten angefordert werden können.
Für die Erstellung eigener Extensions habe ich einige Tipps zusammengestellt: AI2 FAQ: Extensions entwickeln.