Englisch version   English version


Version Anpassungen
1.0 (2020-08-12) Initiale Version
1.1 (2020-08-21) - Das Antippen der Benachrichtigung hatte keinen Effekt.
- Die Eigenschaften NotificationIconAsset und ChannelName hinzugefügt
- Die Eigenschaften ChannelDescription, NotificationText, NotificationTitle änderbar gemacht.
- Die Methode UpdateIcon hinzugefügt.
- Die Eigenschaft ChannelID entfernt.

Motivation

Für manche Projekte ist es notwendig zu verhindern, dass die zugehörige App vom Betriebssystem deaktiviert wird. In frühen Android Versionen war es möglich, einen sogenannten WakeLock zu setzen. Dieser bewirkte, dass die App vom Betriebssystem nicht abgeschaltet wurde. Mit der Version 6.0 (Marshmallow) hat Android den Doze-Modus zur Optimierung der Akku-Laufzeiten eingeführt. Diese Funktion schaltet, wenn keine App aktiv bedient wird, nach und nach alles herunter (Display, CPU, WiFi, etc.). Auch die WakeLock-Funktion wurde z.T. außer Kraft gesetzt. Um den Doze Mode wirkungsvoll zu umgehen, ist es notwendig, zusätzlich zu einem WakeLock einen Foreground-Service anzulegen. Ein solcher Service kann nur gestartet werden, wenn gleichzeitig eine für den Benutzer sichtbare Benachrichtigung (Notification) angelegt wird.

Seit der Version nb184 des MIT App Inventor ist es möglich, dass Extensions Services erstellen. Diese Extension (UrsAi2KeepAlive) legt einen Foreground-Service in Kombination mit einem Partial-Wakelock an. Dadurch wird erreicht, dass die CPU nicht abgeschaltet wird und die App über einen langen Zeitraum aktiv bleibt.

Hinweis für Kodular-Nutzer:

Zum heutigen Zeitpunkt (12.8.2020) hat Kodular noch nicht nachgezogen. Mit Kodular entwickelte Apps können aber nachträglich bearbeitet und zur Funktion gebracht werden.

Zunächst einmal kann während der Testphase die Extension ganz normal eingebunden werden. Die App stürzt nicht ab, es wird lediglich kein Foreground-Service angelegt. Wenn die App dann soweit fertiggestellt ist, kann man sie nachträglich manipulieren. Android verlang, dass ein benutzter Service in der Manifest-Datei ausgewiesen wird. Diese Ausweisung muss man nachträglich in die APK-Datei hinein patchen. So geht's:

  1. Die App in Kodular ganz normal entwickeln und testen.
  2. Die fertige APK-Datei auf den PC herunterladen (exportieren, "Save .apk to my Computer").
  3. Den Service in die Manifest-Datei eintragen. Dazu eignet sich z.B. das Programm APK Editor Studio. Folgende Zeile muss auf der gleichen Ebene wie die Activity-Tags eingetragen werden (eine Zeile):
    <activity android:...>
    ...
    </activity>
    <service android:enabled="true" android:exported="false" android:name="de.ullisroboterseite.ursai2keepalive.UrsService"/>
  4. Die so bearbeitete APK kann nun verwand werden, z.B. per USB auf das Endgerät übertragen und mit dem Dateimanager installiert werden.

In­halts­ver­zeich­nis

Download

Verwendung

NotificationIcon

Referenz

Beispiel

Werkzeuge

Download

Das ZIP-Archiv UrsAI2KeepAlive zum Download. Das Archiv enthält den Quellcode, das kompilierte Binary zum Upload in den App Inventor und eine Beispiel-Anwendung.

Verwendung

Wie bereits oben erwähnt, erstellt die Extension einen ForegroundService, der zusammen mit einem WakeLock den Doze-Modus außer Kraft setzt. Damit bleibt die App auch dann aktiv, wenn sie in den Hintergrund verschoben wird oder das Display abgedunkelt wird. Inwieweit auch alle Ressourcen zur Verfügung stehen, ist noch nicht getestet. Auf jeden Fall bleiben die Netzwerk- und Audio-Funktionen verfügbar.

Singuläre Verwendung / MultiScreen Apps

Die Extension berücksichtigt, dass der Service und der WakeLock für die gesamte App und nicht für eine einzelne Activity (Screen) wirksam ist. Intern, im Java-Code, werden die entsprechenden Objekte als static deklariert, d.h. die internen Felder sind die gleichen für alle Instanzen der Extension. Die Konsequenz ist, dass es pro App nur einen einzigen ForegroundService und nur einen einzigen WakeLock gibt. Mehrere Instanzen der Extension auf einem oder auf mehreren Screens wirken alle auf denselben Service und denselben Lock.

Die Funktionen Start und Stop prüfen vor der Ausführung, ob der Zustand der Extension dies zulässt. Start startet nur dann einen neunen Service, wenn aktuell kein Service aktiv ist. Stop ist wirkungslos, wenn kein Service gestartet wurde. Der mehrfache Aufruf der Funktionen ist also unkritisch, aber wirkungslos. Dies gilt auch bei mehrfacher Instanziierung der Extension. Über die Eigenschaft isActive kann der Zustand jederzeit abgefragt werden.

Je nach dem, welche Aktion bei der notwendigen Notification (s.u.) hinterlegt wird, kann es sein, dass ein weiterer Screen der App aufgerufen wird, ohne das der aktuell geöffnete Screen geschlossen wird. Wenn in dem neu geöffneten Screen der Zustand der Extension geändert wird, ist dies für die anderen Screens nicht immer ersichtlich. Das Ereignis onResume wird jedes Mal ausgelöst, wenn der zur Instanz der Extension gehörende Screen in den Vordergrund kommt. Hier kann über isActive der Zustand der Extension abgefragt werden.

 Zu beachten ist, dass immer nur ein Service aktiv sein kann. Bevor also ein neuer gestartet werden kann, muss erst ein bereits aktiver gestoppt werden.

Benachrichtigung / Benachrichtigungskanäle

In den neueren Android-Versionen ist es nicht mehr möglich, einen ForegroundService anzulegen, ohne eine für den Benutzer sichtbare Benachrichtigung (Notification) anzulegen. Diese Benachrichtigung muss in neueren Android-Versionen einem Benachrichtigungskanal (NotificationChanel) zugeordnet sein. Die Extension legt beim Start des Foreground-Service eine einfache Benachrichtigung und bei Bedarf auch einen Benachrichtigungskanal an. Die ID des Kanals (ChannelId) ist "KEEPALIVE".

Über die Eigenschaften ChannelName, ChannelDescription werden die für den Anwender sichtbaren Eigenschaften des Kanals festgelegt.

Benachtigungsaktionen

Benachrichtigungen sind i.d.R. mit einer Aktion verbunden, die ausgeführt wird, wenn der Benutzer auf die Benachrichtigung klickt. Über die Eigenschaft ScreenToOpen kann festgelegt werden, welche Aktion ausgeführt wird. Die Möglichkeiten sind:

Das Feld leer lassen Die Benachrichtigung wird mit keiner Aktion verbunden. Das Anklicken der Benachrichtigung ist wirkungslos.
Ein Screen-Namen der App Beim Anklicken der Benachrichtigung wird der angegebene Screen der App geöffnet. Der Screen wird als neue Activity/ als neuer Task gestartet.

Der neue Screen wird mit dem unter der Eigenschaft StartValue hinterlegten Wert geöffnet (s. Control.get start value und Control.get plain start text).

Wenn es den angegeben Screen nicht gibt, wird kein Foreground-Service angelegt und das Ereignis Screen.ErrorOccured mit dem Fehlercode 17001 ausgelöst.
Eine voll qualifizierte Activity-Bezeichnung Die angegebene Activity wird gestartet. Es müssen Package-Name und Klassen-Name angegeben werden.

Automatischer Stopp

Ein ForegroundService und die damit verbundene Benachrichtigung bleibt auch dann aktiv, wenn die App per Back-Taste verlassen wird. Wenn Eigenschaft AutoStop ausgewählt ist, stoppt die Extension den Service, wenn der Screen mit dem Name "Screen1" geschlossen wird (Android-Ereignis onDestroy). Screen1 ist die Hauptaktivität der App.

Dies kann zu unerwünschtem Verhalten führen und muss beachtet werden, wenn in ScreenToOpen "Screen1" als zu öffnender Screen angegeben wird. Die Foreground-Service wird dann an der falschen Stelle gestoppt.

Anpassen der Benachrichtigungen

Über die Methoden UpdateText, UpdateTitle, UpdateIcon lassen sich die Eigenschaften der Benachrichtigung nachträglich anpassen.

Referenz

  Bezeichnung Typ Funktion Voreinstellung
AutoStop boolean Der Service wird automatisch gestoppt, wenn Screen1 geschlossen wird (s.o). true
ChanelDescription String Für den Benutzer sichtbare Beschreibung des Benachrichtigungskanals (NotificationChanel). Diese Eigenschaft kann nachträglich geändert werden. -none-
ChannelName String Für den Benutzer sichtbarer Name des Benachrichtigungskanals (NotificationChanel). Diese Eigenschaft kann nachträglich geändert werden. "URS KeepAlive"
NotificationIcon String Bezeichnung des System-Icons für die Benachrichtigung. Die möglichen Angaben sind im Abschnitt System Notification Icons aufgeführt.
Das Icon kann über die Methode UpdateIcon geändert werden.
"ic_launcher"
NotificationIconAsset Asset Bezeichnung einer hochgeladenen Datei, die das anzuzeigende Icon1) enthält. Diese Angabe hat Vorrang vor der Eigenschaft NotificationIcon. Das Icon kann über die Methode UpdateIcon geändert werden. -none-
NotificationText String Zweite Zeile der Benachrichtigung (Notification). Diese Eigenschaft kann nachträglich geändert werden. -none-
NotificationTitle String Erste Zeile der Benachrichtigung (Notification). Diese Eigenschaft kann nachträglich geändert werden. "Keep Alive Service"
ScreenToOpen String Aktion, die beim Anklicken der Benachrichtigung ausgelöst wird:
- leer: keine Aktion
- Name eines Screens der App (z.B. "Screen1"): Der Screen wird geöffnet.
- Vollständiger Name einer Activity (PackageName + ClassName): Start der Activity.
Wenn die zu startende Activity nicht gefunden werden kann, wird keine Aktion gespeichert, sondern das Ereignis Screen.ErrorOccurred ausgelöst (s.u.).
-none-
StartValue String AI2-Startwert, falls ScreenToOpen eine Screen-Namen enthält. -none-

1) Für das Icon empfiehlt sich eine Größe von 96x96 Pixel². Das Icon wird zweifarbig dargestellt: Transparente Bereiche erscheinen hell, farbige Bereiche dunkel. Wird ein dunkles Quadrat angezeigt, enthält die Grafik wahrscheinlich keine transparenten Bereiche.

 

Block Funktion Anmerkung
Startet den Service. Mehrfachaufruf ist unkritisch.
  Stoppt den Service. Mehrfachaufruf ist unkritisch.
  Gibt an, ob der Service aktiv ist.  
Ruft den für den Benutzer sichtbaren Namen des Benachrichtigungskanals ab oder legt ihn fest. Die Änderung wirkt sich auch auf bereits angelegte Benachrichtigungskanäle aus.
Ist der Kanal noch nicht angelegt (niemals die Methode Start aufgerufen worden), wird der Kanal angelegt.
Ruft die für den Benutzer sichtbare Beschreibung der Benachrichtigung ab oder legt sie fest. -dito-
Ruft den Titel der Benachrichtigung (erste Zeile) ab oder legt ihn fest. Die Änderung wirkt sich auch auf bereits angezeigte Benachrichtigungen aus.
Ruft den Text der Benachrichtigung (zweite Zeile) ab oder legt ihn fest. Die Änderung wirkt sich auch auf bereits angezeigte Benachrichtigungen aus.
Ändert das Symbol der Benachrichtigung.
IconName: Entweder der Name eines System Icons oder der Name einer hochgeladenen Datei.
Die Änderung wirkt sich zukünftig anzuzeigende und auf bereits angezeigte Benachrichtigungen aus.
  Der Screen mit der Instanz der Extension kommt in den Vordergrund. Siehe Understand the Activity Lifecycle
Wird ausgelöst, wenn in ScreenToOpen ein nicht vorhandener Screen angegeben wird.

component: Referenz zu der Instanz der Extension
functionName: "Start"
errorNumber: 17001
message: "Screen not found: " + <SscreenToOpen>
 

System Notification Icons

In der folgenden Tabelle habe ich die Icons zusammengestellt, die ich auf verschiedenen Seiten gefunden habe. Die Ansichten müssen nicht unbedingt in jedem Gerät gleich sein.

  Bezeichnung Wert Anmerkung
alert_dark_frame
alert_dark_frame 17301504
alert_light_frame
alert_light_frame 17301505
arrow_down_float
arrow_down_float 17301506
arrow_up_float
arrow_up_float 17301507
bottom_bar
bottom_bar 17301658
btn_default
btn_default 17301508
btn_default_small
btn_default_small 17301509
btn_dialog
btn_dialog 17301527
btn_dropdown
btn_dropdown 17301510
btn_minus
btn_minus 17301511
btn_plus
btn_plus 17301512
btn_radio
btn_radio 17301513
btn_star
btn_star 17301514
btn_star_big_off
btn_star_big_off 17301515
btn_star_big_on
btn_star_big_on 17301516
button_onoff_indicator_off
button_onoff_indicator_off 17301518
button_onoff_indicator_on
button_onoff_indicator_on 17301517
checkbox_off_background
checkbox_off_background 17301519
checkbox_on_background
checkbox_on_background 17301520
dark_header
dark_header 17301521 Drawable to use as a background for separators on a list with a dark background
dialog_frame
dialog_frame 17301521
dialog_holo_dark_frame
dialog_holo_dark_frame 17301682
dialog_holo_light_frame
dialog_holo_light_frame 17301683
divider_horizontal_bright
divider_horizontal_bright 17301522
divider_horizontal_dark
divider_horizontal_dark 17301524
divider_horizontal_dim_dark
divider_horizontal_dim_dark 17301525
divider_horizontal_textfield
divider_horizontal_textfield 17301523
edit_text
edit_text 17301526
editbox_background
editbox_background 17301528
editbox_background_normal
editbox_background_normal 17301529
editbox_dropdown_dark_frame
editbox_dropdown_dark_frame 17301530
editbox_dropdown_light_frame
editbox_dropdown_light_frame 17301531
gallery_thumb
gallery_thumb 17301532
ic_btn_speak_now
ic_btn_speak_now 17301668
ic_delete
ic_delete 17301533
ic_dialog_alert
ic_dialog_alert 17301543
ic_dialog_dialer
ic_dialog_dialer 17301544
ic_dialog_email
ic_dialog_email 17301545
ic_dialog_info
ic_dialog_info 17301659
ic_dialog_map
ic_dialog_map 17301546
ic_input_add
ic_input_add 17301547
ic_input_delete
ic_input_delete 17301548
ic_input_get
ic_input_get 17301549
ic_lock_idle_alarm
ic_lock_idle_alarm 17301550
ic_lock_idle_charging
ic_lock_idle_charging 17301534
ic_lock_idle_lock
ic_lock_idle_lock 17301535
ic_lock_idle_low_battery
ic_lock_idle_low_battery 17301536
ic_lock_lock
ic_lock_lock 17301551
ic_lock_power_off
ic_lock_power_off 17301552
ic_lock_silent_mode
ic_lock_silent_mode 17301553
ic_lock_silent_mode_off
ic_lock_silent_mode_off 17301554
ic_media_ff
ic_media_ff 17301537
ic_media_next
ic_media_next 17301538
ic_media_pause
ic_media_pause 17301539
ic_media_play
ic_media_play 17301540
ic_media_previous
ic_media_previous 17301541
ic_media_rew
ic_media_rew 17301542
ic_menu_add
ic_menu_add 17301555
ic_menu_agenda
ic_menu_agenda 17301556
ic_menu_always_landscape_portrait
ic_menu_always_landscape_portrait 17301557
ic_menu_call
ic_menu_call 17301558
ic_menu_camera
ic_menu_camera 17301559
ic_menu_close_clear_cancel
ic_menu_close_clear_cancel 17301560
ic_menu_compass
ic_menu_compass 17301561
ic_menu_crop
ic_menu_crop 17301562
ic_menu_day
ic_menu_day 17301563
ic_menu_delete
ic_menu_delete 17301564
ic_menu_directions
ic_menu_directions 17301565
ic_menu_edit
ic_menu_edit 17301566
ic_menu_gallery
ic_menu_gallery 17301567
ic_menu_help
ic_menu_help 17301568
ic_menu_info_details
ic_menu_info_details 17301569
ic_menu_manage
ic_menu_manage 17301570
ic_menu_mapmode
ic_menu_mapmode 17301571
ic_menu_month
ic_menu_month 17301572
ic_menu_more
ic_menu_more 17301573
ic_menu_my_calendar
ic_menu_my_calendar 17301574
ic_menu_mylocation
ic_menu_mylocation 17301575
ic_menu_myplaces
ic_menu_myplaces 17301576
ic_menu_preferences
ic_menu_preferences 17301577
ic_menu_recent_history
ic_menu_recent_history 17301578
ic_menu_report_image
ic_menu_report_image 17301579
ic_menu_revert
ic_menu_revert 17301580
ic_menu_rotate
ic_menu_rotate 17301581
ic_menu_save
ic_menu_save 17301582
ic_menu_search
ic_menu_search 17301583
ic_menu_send
ic_menu_send 17301584
ic_menu_set_as
ic_menu_set_as 17301585
ic_menu_share
ic_menu_share 17301586
ic_menu_slideshow
ic_menu_slideshow 17301587
ic_menu_sort_alphabetically
ic_menu_sort_alphabetically 17301660
ic_menu_sort_by_size
ic_menu_sort_by_size 17301661
ic_menu_today
ic_menu_today 17301588
ic_menu_upload
ic_menu_upload 17301589
ic_menu_upload_you_tube
ic_menu_upload_you_tube 17301590
ic_menu_view
ic_menu_view 17301591
ic_menu_week
ic_menu_week 17301592
ic_menu_zoom
ic_menu_zoom 17301593
ic_notification_clear_all
ic_notification_clear_all 17301594
ic_notification_overlay
ic_notification_overlay 17301595
ic_partial_secure
ic_partial_secure 17301596
ic_popup_disk_full
ic_popup_disk_full 17301597
ic_popup_reminder
ic_popup_reminder 17301598
ic_popup_sync
ic_popup_sync 17301599
ic_search_category_default
ic_search_category_default 17301600
ic_secure
ic_secure 17301601
list_selector_background
list_selector_background 17301602
menu_frame
menu_frame 17301603
menu_full_frame
menu_full_frame 17301604
menuitem_background
menuitem_background 17301605
picture_frame
picture_frame 17301606
presence_audio_away
presence_audio_away 17301679
presence_audio_busy
presence_audio_busy 17301680
presence_audio_online
presence_audio_online 17301681
presence_away
presence_away 17301607
presence_busy
presence_busy 17301608
presence_invisible
presence_invisible 17301609
presence_offline
presence_offline 17301610
presence_online
presence_online 17301611
presence_video_away
presence_video_away 17301676 Presence drawables for videochat or audiochat capable contacts
presence_video_busy
presence_video_busy 17301677
presence_video_online
presence_video_online 17301678
progress_horizontal
progress_horizontal 17301612
progress_indeterminate_horizontal
progress_indeterminate_horizontal 17301613
radiobutton_off_background
radiobutton_off_background 17301614
radiobutton_on_background
radiobutton_on_background 17301615
screen_background_dark
screen_background_dark 17301656
screen_background_dark_transparent
screen_background_dark_transparent 17301673 Semi-transparent background that can be used when placing a dark themed UI on top of some arbitrary background (such as the wallpaper).
screen_background_light
screen_background_light 17301657
screen_background_light_transparent
screen_background_light_transparent 17301674 Background drawable that can be used for a transparent activity to be able to display a light UI: this lightens its background to make a light UI more visible.
spinner_background
spinner_background 17301616
spinner_dropdown_background
spinner_dropdown_background 17301617
star_big_off
star_big_off 17301619
star_big_on
star_big_on 17301621
star_off
star_off 17301618
star_on
star_on 17301620
stat_notify_call_mute
stat_notify_call_mute 17301622
stat_notify_chat
stat_notify_chat 17301623
stat_notify_error
stat_notify_error 17301624
stat_notify_missed_call
stat_notify_missed_call 17301631
stat_notify_more
stat_notify_more 17301625
stat_notify_sdcard
stat_notify_sdcard 17301626
stat_notify_sdcard_prepare
stat_notify_sdcard_prepare 17301675
stat_notify_sdcard_usb
stat_notify_sdcard_usb 17301627
stat_notify_sync
stat_notify_sync 17301628
stat_notify_sync_noanim
stat_notify_sync_noanim 17301629
stat_notify_voicemail
stat_notify_voicemail 17301630
stat_sys_data_bluetooth
stat_sys_data_bluetooth 17301632
stat_sys_download
stat_sys_download 17301633
stat_sys_download_done
stat_sys_download_done 17301634
stat_sys_headset
stat_sys_headset 17301635
stat_sys_phone_call
stat_sys_phone_call 17301636 This constant was deprecated in API level 15. Replaced by a private asset in the phone app.
stat_sys_phone_call_forward
stat_sys_phone_call_forward 17301637 This constant was deprecated in API level 15. Replaced by a private asset in the phone app.
stat_sys_phone_call_on_hold
stat_sys_phone_call_on_hold 17301638 This constant was deprecated in API level 15. Replaced by a private asset in the phone app.
stat_sys_speakerphone
stat_sys_speakerphone 17301639
stat_sys_upload
stat_sys_upload 17301640
stat_sys_upload_done
stat_sys_upload_done 17301641
stat_sys_vp_phone_call
stat_sys_vp_phone_call 17301671 This constant was deprecated in API level 15. Replaced by a private asset in the phone app.
stat_sys_vp_phone_call_on_hold
stat_sys_vp_phone_call_on_hold 17301672 This constant was deprecated in API level 15. Replaced by a private asset in the phone app.
stat_sys_warning
stat_sys_warning 17301642
status_bar_item_app_background
status_bar_item_app_background 17301643
status_bar_item_background
status_bar_item_background 17301644
sym_action_call
sym_action_call 17301645
sym_action_chat
sym_action_chat 17301646
sym_action_email
sym_action_email 17301647
sym_call_incoming
sym_call_incoming 17301648
sym_call_missed
sym_call_missed 17301649
sym_call_outgoing
sym_call_outgoing 17301650
sym_contact_card
sym_contact_card 17301652
sym_def_app_icon
sym_def_app_icon 17301651
title_bar
title_bar 17301653
title_bar_tall
title_bar_tall 17301670 Drawable to use as a background for a taller version of the titlebar
toast_frame
toast_frame 17301654
zoom_plate
zoom_plate 17301655

 

Beispiel

Test-App

Eine kleine Beispiel-App zeigt die Verwendung der Extension (s. Download).

Das Beispiel sendet in einem 10-Sekunden-Abstand die aktuelle Zeit per UDP. So kann man auch bei verdunkelten Display auf einem externen Gerät kontrollieren, ob die App aktiv ist. Die Schaltflächen starten und stoppen den Service. Angezeigt wird außerdem wie der aktuelle Status des Services ist und mit welchem Startwert der Screen geöffnet wurde.

Screenshot der App.
Screenshot der Benachrichtigung.

UDP-Receiver

Das Bespiel enthält außerdem eine Projekt-Datei für einen einfachen UDP-Receiver. Mit einem zweiten Android-Gerät kann man so kontrollieren, ob die Test-App noch aktiv ist.
Screenshot des einfachen UDP-Empfängers.

Werkzeuge

Für die Erstellung eigener Extensions habe ich einige Tipps zusammengestellt: AI2 FAQ: Extensions entwickeln.