Version | Adjustments |
---|---|
1.0 (2021-05-05) | Initial Version |
1.1 (2021-07-11) |
|
1.2 (2021-09-12) |
If the extension is used in several apps, tapping an action button triggers the associated event in all apps at the same time. This is due to the global nature of the broadcast receivers used. Solution: The intents for the action buttons are individualized with the app name. When a broadcast is received, the name is checked and only the appropriate messages are processed. Kodular now also provides many Androidx functions. Only "androidx.media.jar" has to be integrated. The Foreground service is now declared via UsesServices annotaion in the source and manually in the broadcastReceivers section in the generated .aix file. With these changes it is no longer necessary to have two different versions of the extension for App Inventor and Kodular. Adaptation of existing Kodular projects : Internal names are identical. Therefore the old version can be replaced by the new one. However, this is only possible by exporting the project and reimporting it .
|
1.3 (2021-09-15) | As above. Now also for clicking the notification and deleting it individualized intents are used to prevent overlaps when using the extension in several apps at the same time. |
1.4 (2022-10-10) | Adapted for SDK31 (Android 12): All PendingIntents get the FLAG_IMMUTABLE flag. The service receives the attribute exported = "true". Global exception is caught and written to the log. |
2023-04-19 | Update of the TaifunPlayer extension used in the example. The old version no longer works on Android 11+. |
2023-08-15 | Since Android 12 (even earlier?), the app no longer opens automatically when you click on the notification. Fabio told me what needed to be changed. |
1.6 (2023-11-23) | Adapted to Android 14 |
Foreground service / Doze mode
Buttons inside media notification
ProgressBar (since Android 10, API Level 29)
The UrsAI2MediaNotification ZIP archive for download. The archive contains the source code, the compiled binary for uploading to the App Inventor and a sample application.
Since API level 21 (LolliPop 5.0) there is a special type of notification that was specially developed to control media players. External playback devices like headphones can control the media player via this notification too.
This extension is only intended for the administration of media notifications. If you want to create regular notifications, you should use the extension UrsAI2Notifier.
With API level 26 (version Oreo 8.0), Android introduced the concept of notification channels (NotificationChannel, also known as notification categories on some devices). All notifications must be assigned to such a channel up from API level 26. Up to and including API Level 25, all information and functions relating to the notification channel are irgnored.
The notification concept is quite complex. Android allows settings to be made on the device level as well as on the channel level and with the notifications themselves.
Many properties of the notifications, e.g. importance, are defined at the channel level. These properties are set by the app when the channel is created. With the help of the Android app Settings, the user has full access to most of the channel attributes and can modify them as required. The modifications made by the user should not be overwritten. This is why the channel properties can no longer be changed later via a program. Even if a channel is deleted and then created again, it is created with the last settings. "Erasing" is practically just "hiding". Only the properties Name and Descriptionof can be changed later. Eliminating spelling mistakes would otherwise be impossible.
So you should think carefully about which properties are assigned to a channel.
The extension offers the properties ChannelID, ChannelName, ChannelDescription and ChannelImportance as channel properties. The channel is created automatically after starting the app (in Companion when ShowNotification is called for the first time ).
If you want to change other properties, a new channel (new ChannelID) must be used. You should hide the existing channel when starting the app with HideChannel .
All changes have no immediate effect. They are applied the next time ShowNotification is called.
For some projects it is necessary to prevent the app from being deactivated by the operating system. With version 6.0 ( Marshmallow) Android introduced Doze mode to optimize battery life time. If no app is actively operated, this function gradually switches everything down (display, CPU, WiFi, etc.). In order to bypass Doze mode effectively, it is necessary to create a foreground service. A lot of useful background information can be found in the ZEBRA Developer Portal: Keeping your Android application running when the device wants to sleep.
Most Android installations recognize when a media player is active and do not switch the device off. If it does, the ForegroundService property can be used to specify that a foreground service is started when the notification is created. If the installed Android version has an API level of less than 26, this property has no effect.
The button with the play (⯈) or the pause (⏸) symbol is always displayed. Which of them is displayed can be set using the SetStatePaused (shows ⯈) and SetStatePlaying (shows ⏸) methods.
Depending on which functions the app wants to supports, the other buttons can be switched on and off by the Support... functions.
If ClickAble is set, the app is brought to the foreground when the notification body is clicked and the OnClick event is triggered. If ClickAble is set to false, nothing happens when you tapp the notification.
Um die App in dem Ereignis in den Vordergrund zu bringen, ist ein kleiner Trick notwendig. Im Beispiel wird im OnClick-Ereignis ein Screen mit der Bezeichnung Dummy geöffnet. Dieser Screen schließt sich unmittelbar im Ereignis Screen.Initialize selbst wieder:
A little trick is necessary to bring the app to the foreground when the event is triggered. In the example, a screen with name Dummy is opened in the OnClick event. This screen closes itself immediately in the Screen.Initialize event:
If DeleteAble is set, the user can delete the notification, for example by swiping. If the notification is deleted by the user, the UserCanceled event is triggered
The background color is not programmatically adjustable. It is automatically determined by Android from the album image colors.
The SetMetaData and SetMetaDataEx functions can be used to specify the title, artist and the image (AlbumImage) that will be shown on the notification.
You can specify AlbumImage as follows:
Type | Prefix | Example |
---|---|---|
URL | http or https | https://ullisroboterseite.de/android-AI2-MediaNotification/MediaNotification.png |
Asset | // or nothing | //MediaNotification.png or simply MediaNotification.png |
File, relative path | / | /data/user/0/appinventor.ai_bienonline.UrsMediaNotificationAI2/MediaNotification.png |
File, absolute path | file:/// | file:///Android/data/appinventor.ai_bienonline.UrsMediaNotificationAI2/MediaNotification.png |
When specifying a URL, notice that the image is loaded synchronously. This can take some time with large files or slow connections. Another option is to use an extension that enables an asynchronous (concurrent) download of the file ( e.g. the ImageLoader extension ).
To simplify the file acces you can use:
The option of including your own graphics for the SmallIcon (SmallIconImage property ) is available up from API Level 23. The SmallSystemIcon property to allows to select a system icon for older versions (for possible options see: System Notification Icons). The selection rule is as follows:
Condition | Selection | ||
---|---|---|---|
API Level | SmallIconImage | SmallSystemIcon | |
≥ 23 | specified | X | SmallIconImage |
empty | specified | SmallSystemIcon | |
empty | empty | no icon | |
< 23 | X | specified | SmallSystemIcon |
X | empty | no icon |
Since Android 10, the media notification can display a progress bar and a seek bar. To do this, the duration of the media object and the current position of the player must be known.
SetMetaDataEx allows the specification of the playing time, which is required for the display of a progress bar. Some media players, such as the TaifunPlayer (used in the example) provide this information. To get the necessary ininformations the player must be initialized accordingly.
The ShowWithProgressBar function shows a progress bar. It starts at the position that is specified with the CurrentPosition parameter. Then it continues to run independently. If you change the playback position programmatically, you have to synchronize the progress bar with ShowWithProgressBar.
With versions older than Android 10, SetMetaDataE and ShowWithProgressBar also work, but no progress bar is displayed.
You set the initial properties of the component, transfer the media metadata ( SetMetaData or SetMetaDataEx) and then call ShowNotification or ShowWithProgressBar. From now on you prectically need to react to the events. Depending on the event, you set the media player appropriately, adjust the properties of the component and call ShowNotification again .
Usually the media player is not only controlled by the media notification. The app also contains controls that are supposed to control the player. To make the blocks in the project simple, there are the functions Play, Pause, Rewind, etc., which trigger the same events as the notification does. So there is no need for double the code for the controls on the user interface. All you have to do is call the appropriate function.
Errors are reported via the Screen.ErrorOccurred event.
Code | Text | Meaning | Comment |
---|---|---|---|
17500 | Album image not found. | The specified album image could not be loaded. | Affected functions are SetMetaData und SetMetaDataEx. |
17501 | No meta data set. | The notification is displayed without calling SetMetaData or SetMetaDataEx beforehand . | Affected function is ShowNotification. |
17502 | SmallIconImage invalid. | The graphic file for the SmallIconImage parameter cannot be loaded. | Affected function is ShowNotification. The notification is shown without an icon. |
17503 | SmallSystemIcon invalid. | The specification for the SmallSystemIcon parameter cannot be resolved. | Affected function is ShowNotification. The notification is shown without an icon. |
17504 | Cannot start foreGround service. | The requested foreground service cannot be started. | Affected function is ShowNotification. |
17506 | Not an UrsMediaHelper component. | The specified component is not of type UrsMediaHelper. | Affected function is SetMetaDataFromMH. |
GetAppDataDir()
on my test
device (in the example below) returns the following result:GetDownloadDir("file://")
on my test device (in the example below)
returns the following result: getVolumes("file://)
on my test device returns the following items : The download archive contains a sample project in two versions fpr App Inventor and for Kodular:
UrsMediaNotification
The download ZIP archive contains a sample project file for App Inventor (UrsMediaNotification.aia) and one for Kodular (UrsMediaNotificationK.aia) in the example directory
The example demonstrates how a media player can be controlled with the extension. The TaifunPlayer is used as the player . Four different pieces of music can be played.
In Kodular the fonts of the buttons can be set individually. Among other things, the Material Icons Font with many useful symbols from Google is available there. The MyFonts extension from Anke is used for the App Inventor
The controls are enabled or disabled depending on the current state of the app. Effect:
The associated MediaNotification duplicates these controls. Only the supported elements are displayed (Off Broadway is the last track in the playlist, SkipToNext is therefore not shown):
(Screenshot Xiaomi Redmi 5 Plus, Android Oreo 8.1)
The display of a stop symbol is unfortunately not available in the Android media notification
In Android 10, the background color is automatically calculated from the color of the album picture.
(Screenshot Google Pixel 2 XL API 30 Emulator)
A progress bar (since Android 10) is displayed when you open the notification:
The SetMusic procedure fills the extension with the metadata (title, artist, album image). The MP3 files in the sample project already contain all the necessary metadata so that they can be easily extracted using the UrsMediaHelper extension. The metadata can be viewed and edited with the Mp3tag program, for example.
During initialization, it is ensured that the app works correctly right from the start. SetMusic sets the metadata and the music source for the TainfunPlayer to the first track of the playlist. The combination of MediaPlayer.Start and MediaPlayer.Pause ensures that the Duration and CurrentPosition properties of the media player can be called without the player starta playing. MediaNotification.SetStatePaused and MediaNotification.ShowNotification create the notification in the pause state (symbol ⯈). WakeLock.RequestBatteryOptimizationputs the app on the WhiteList. When the app is started for the first time, the user receives a request for permission to do so.
When changing the WithForegroundService option, a foreground service that has already been started must be stopped with the MediaNotifiaction.Stop method. This method triggers the OnStop event. Usually the MedaiPlayer is stopped in this event. However, this should not happen when switching the option. The DontStopPlayer variable cotrols this.
When skipping to the next song (SkipToNext) or the previous (SkipToPrevious), the current state of the media player is temporarily stored in a local variable. This should be restored after switching. Then the player is stopped. The next (the previous) song is determined and the buttons corresponding to the state of the playlist are enabled or disabled. The blocks are fed with the metadata of the newly selected song via the SetMusic procedure . Finally, the OnPlay or OnPause events are triggered according to the saved player state. . In the events it is ensured that the media player and the media notification are set correctly.
Together with the UrsAI2WakeLock extension, the extension can prevent Doze mode from shutting down the device. To do so a foreground service is can be started. This service must be declared in the app's manifest file. This works in the App Inventor but not with Kodular. Here the created .aix file has to be patched afterwards.
The following description assumes that the development environment has been set up according to the scheme described in AI2 FAQ: Developing Extensions.
In the following sections, <user> must always be replaced by the appropriate name!
he extension uses many Android classes from the Androidx library. In the ZIP archive in the de.ullisroboterseite directory contains the subdirectories for the source code and two Androidx library files: androidx.core-1.3.2-runtime.jar and androidx.media-1.3.1-runtime. Kodular needs both files, App Inventor only needs androidx.media-1.3.1-runtime. androidx.core is available there (unfortunately out of date or incomplete).
To integrate the libraries into the extension, they must be copied into the intended directory and declared in the source code.
Copying is done via the build script. To do so, in file
C:/Users/<user>/appinventor-sources/appinventor/components/build.xml
the following entries in the CopyComponentLibraries section have to be added (one line!):
<copy toFile="${public.deps.dir}/androidx.media.jar" file="C:/Users/<user>/appinventor-sources/appinventor/components/src/de/ullisroboterseite/androidx.media-1.3.1-runtime.jar"/>
The media library is declared by the annotation @UsesLibraries and the foreground service by annotation @UsesServices:
@UsesServices(services = {
@ServiceElement(name = "de.ullisroboterseite.ursai2medianotification.ForegroundService", enabled = "true", exported = "true") })
@UsesLibraries(libraries = "androidx.media.jar")
The environment is now prepared to compile the extension.
The build process creates the file de.ullisroboterseite.ursai2medianotification.aix. This file is in the format of a ZIP file, so it can be opened and edited with a simpole ZIP-editor. If necessary, it helps to rename the file to ...zip.
The .aix file contains the de.ullisroboterseite.ursai2medianotification directory with the subdirectory files . In folder files are the two files component_build_info.json and component_build_infos.json. Both can be opened and edited with a simple text editor. Both files contain these entries
"services":["<service android:name=\"de.ullisroboterseite.ursai2medianotification.ForegroundService\" android:enabled=\"true\" android:exported=\"false\">\n <\/service>\n"]
and
"broadcastReceivers":[] (do not confuse with "broadcastReceiver":[])!
The text between the square brackets for services must be copied into the square brackets for broadcastReceivers:
"broadcastReceivers":["<service android:name=\"de.ullisroboterseite.ursai2medianotification.ForegroundService\" android:enabled=\"true\" android:exported=\"false\">\n <\/service>\n"]
If the files were extracted from the ZIP archive for editing, they must now be copied back into the archive (the .aix file).
For developing own extensions I gathered some tips: AI2 FAQ: Develop Extensions. On the site you can find further informations.