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