Deutsche Version   Deutsche Version


Version Adjustments
1.0 (2020-08-12) Initial Version
1.1 (2020-08-21) - Touching the Notitication had no effect
- Properties NotificationIconAsset and ChannelName added
- Methods ChannelDescription, NotificationText, NotificationTitle made changeable
- Method UpdateIcon added
- Property ChannelID removed

Motivation

For some projects it is necessary to prevent the app from being deactivated by the operating system. In early Android versions it was possible to aquire a so-called WakeLock to prevent the operating system from destroying the app. With version 6.0 (Marshmallow) Android introduced the doze mode to optimize battery life. If no app is actively in use, this mode gradually switches everything down (display, CPU, WiFi, etc.). The WakeLock function has also been partially disabled. To bypass the doze mode effectively, it is necessary to create a foreground service in addition to a WakeLock. Buit such service can be started only when at the same time notification is applied that is visible to the user.

With version nb184 of the MIT App Inventor it is possible that extensions create services. This extension (UrsAi2KeepAlive) creates a foreground service in combination with a partial WakeLock. This ensures that the CPU is not switched off and the app remains active for a long period of time.

Notes for Kodular users:

As of today (August 12, 2020) Kodular has not yet this new feature. Apps developed with Kodular can, however, be edited and made functional afterwards.

The extension can be integrated normally during the test phase. The app doesn't crash, just no foreground service is created. When the app is finished, you can manipulate it afterwards. Android requires that a used service is declared in the manifest file. This declaration has to be patched into the APK file later. That's how it's done:

  1. Develop and test the app in Kodular as normal.
  2. Download the finished .apk file to the PC (export, "Save.apk to my Computer").
  3. Declare the service in the manifest file. The APK Editor Studio program is suitable for this. The following line must be entered on the same level as the activity tags (one line):
    <activity android:...>
    ...
    </activity>
    <service android:enabled="true" android:exported="false" android:name="de.ullisroboterseite.ursai2keepalive.UrsService"/>
  4. The APK edited in this way can now be used, e.g. transferred to the device via USB and installed with the file manager.

Content

Download

Usage

NotificationIcon

Reference

Example

Tools

Download

The UrsAI2KeepAlive ZIP-Archiv for download. The archive contains the source code, the compiled binary for uploading to the App Inventor and a sample application.

Usage

As already mentioned above, the extension creates a ForegroundService which, together with a WakeLock, overrides the Doze mode. This means that the app remains active even if it is moved to the background or the display is shut off. Which all resources are available has not yet been tested. In any case, the network and audio functions remain available.

Singular use / MultiScreen Apps

The extension takes into account that the service and the WakeLock are effective for the entire app and not only for a single activity (Screen). Internally, in the Java code, the corresponding objects are declared as static, i.e. the internal fields are the same for all instances of the extension. The consequence is that there is only one ForegroundService and only one WakeLock per app. Several instances of the extension on one or more Screens all affect the same service and the same lock.

The functions Start and Stop check before execution whether the state of the extension allows this. Start only starts a new service if no service is currently active. Stop has no effect if no service has been started. Calling the functions multiple times is therefore uncritical, but ineffective. This also applies to multiple instantiations of the extension. The status can be queried at any time using the isActive property.

Depending on which action is assigned to the necessary notification (see below), it may be that another Screen of the app is called without the currently open Screen being closed. If the state of the extension is changed in the newly opened screen, this is not always visible for the other screens. The onResume event is triggered every time the screen belonging to the instance of the extension comes into the foreground. The state of the extension can be queried here via isActive.

 It should be noted that only one service can be active at a time. So before a new one can be started, an already active one must first be stopped.

Notification / NotificationChannel

In the newer versions of Android, it is not possible, to create a ForegroundService a user visible notification (Notification). Also this notification must be assigned to a notification channel (NotificationChanel). When the ForegroundService starts, this extension creates a simple Notification and, if necessary, a NotificationChannel. The ID of the Channell (ChannelId) is "KEEPALIVE".

The properties of the channel that are visible to the user are defined via the properties ChannelName and ChannelDescription.

Notification actions

Notifications are usually associated with an action that will be executed when the user clicks the Notification. The ScreenToOpen property can be used to define which action is to be executet. The possibilities are:

Keep the field blank The notification is not associated with any action. Clicking the notification has no effect.
A Screen name of the app When the notification is clicked, the specified Screen of the app opens.The Screen is started as a new Activity / as a new Task.

The new screen is opened with the start value stored at the StartValue property (see Control.get start value and Control.get plain start text).

If the specified Screen does not exist, no ForegroundService is created and the Screen.ErrorOccured event is triggered with the error code 17001.
A fully qualified Activity name The specified Activity is started. PackageName and ClassName must be specified.

Automatic stop

A ForegroundService and the associated Notification remains active even if the app is exited by pressing the back button. If the AutoStop property is selected, the extension stops the service when the Screen with the name "Screen1" is closed (Android event onDestroy). Screen1 is the main activity of the app

This can lead to undesirable behavior and must be taken into account if "Screen1" is specified as the Screen to be opened in ScreenToOpen property. The ForegroundService then maybe stopped at an unexpected time.

Reference

  Bezeichnung Typ Funktion Voreinstellung
AutoStop boolean The service is stopped automatically when Screen1 is closed (see above). true
ChanelDescription String Description of the notification channel that is visible to the user (NotificationChanel). This property can be changed later. -none-
ChannelName String Name of the notification channel visible to the user (NotificationChanel). This property can be changed later. "URS KeepAlive"
NotificationIcon String Name of the system icon for the notification. The possible options are listed in the System System Notification Icons. The icon can be changed using the UpdateIcon method. "ic_launcher"
NotificationIconAsset Asset Name of an uploaded file that contains the icon1) to be displayed. This property has priority over the NotificationIcon property. The icon can be changed using the UpdateIcon method. -none-
NotificationText String Second line of the Notification. This property can be changed later. -none-
NotificationTitle String First line of the Notification. This property can be changed later. "Keep Alive Service"
ScreenToOpen String Action that is triggered when the Notification is clicked:
- empty: no action
- name of a Screen o the app (eg "Screen1"): The screen is opened.
- Full name of an Activity (PackageName + ClassName): Start of the Activity.
If the activity to be started cannot be found, no action is stored and the Screen.ErrorOccurred event is triggered (see below).
-none-
StartValue String AI2 start value, if ScreenToOpen contains a Screen name. -none-

1) A size of 96x96 pixel² is recommended for the icon. The icon is displayed in two colors: Transparent areas appear light, colored areas appear dark. If you see a dark square, the graphic probably does not contain any transparent areas.

Block Function Comment
Start the service. Multiple calls are not critical.
  Stop the service. Multiple calls are not critical.
  Returns whether the service is active.  
Gets or sets the user-visible name of the notification channel. The change also affects notification channels that have already been created.
If the channel has not yet been created (the
Start method has never been called), the channel is created.
Gets or sets the user-visible description of the notification. see above
Gets or sets the title of the notification (first line). The change also affects notifications that have already been displayed.
Gets or sets the text of the notification (second line). The change also affects notifications that have already been displayed.
Changes the notification icon.
IconName: Either the name of a System Icons or the name of an uploaded file.
The change affects notifications to be displayed in the future and notifications that have already been displayed.
The screen with the instance of the extension comes to the foreground. See Understand the Activity Lifecycle
Is triggered if a non-existent Screen is specified in ScreenToOpen property.

component: Reference of the instance of the extension
functionName: "Start"
errorNumber: 17001
message: "Screen not found: " + <SscreenToOpen>
 

NotificationIcon

In the following table lists the icons that I found on different internet pages. The appearance is not necessarily the same on every device.

  Name Value Comment
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

 

Example

Test app

A small sample app shows how to use the extension (see Download).

The example sends the current time via UDP every 10 seconds. So you can check on an external device whether the app is active even when the display is shut off. The buttons start and stop the service. The current status of the service is shown and the start value with which the screen was opened.

Screenshot of the app.
Screenshot of the Notification.

UDP receiver

The example also contains a project file for a simple UDP receiver. With a second Android device, you can check whether the test app is still active.
Screenshot of the simple UDP receiver.

Tools

For developing own extensions I gathered some tips: AI2 FAQ: Develop Extensions.