German Version   German Version


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 .

  • Create a backup copy of the project .
  • Export the project.
  • In the .aia file (is a ZIP archive), in the assets/external_comps directory delete de.ullisroboterseite.ursai2medianotificationk directory.
  • Copy the de.ullisroboterseite.ursai2medianotification directory from the extension (.aix file, also a ZIP archive) to this point in the .aia file.
  • Re-import the project (.aia file) into Kodular.
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


Download

Usage

About notification channels

Configuration

Foreground service / Doze mode

Buttons inside media notification

Activate events

Meta data and appearance

ProgressBar (since Android 10, API Level 29)

How to

Error handling

Reference

Properties

Methods

Events

Example

The user interface

Controls

Options

Implementation hints

Build the Extension

Providing the libraries

Copy the libraries

Declarations

Pacht the .aix files for Kodular

Tools

 

Download

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.

Usage

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.

About notification channels

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 .

Configuration

All changes have no immediate effect. They are applied the next time ShowNotification is called.

Foreground service / Doze mode

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.

Buttons inside media notification

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.

Activate events

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

Meta data and appearance

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.

AlbumImage

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:

Small Icon

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

ProgressBar (since Android 10, API Level 29)

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.

How to

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.

Error handling

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.

Reference

Properties

ChannelDescription
Description of the NotificationChanel that is visible to the user. This property can be changed later.
ChannelID
The ID of the NotificationChanel (cannot be changed later).
ChannelImportance
The importance of the channel. The default is NotificationManager.IMPORTANCE_LOW.
ChannelName
Name of the NotificationChanel visible to the user. This property can be changed later.
ClickAble
true: Clicking the notification brings the app to the foreground and triggers the OnClick event there. false: Clicking the notification has no effect.
Note:
- Changing this property will take effect with the next call to ShowNotification.
- Does not work with all Android versions.
DeleteAble
The notification can be deleted by the user, for example by swiping. This option has no effect when the foreground service is active. The foreground service prohibits deleting the notification.
Note:
- Changing this property will take effect with the next call to ShowNotification.
- Does not work with all Android versions.
ForegroundService
When the notification is created, a foreground service is started. The foreground service prevents Doze mode from shutting down the device. The function has no effect in the Companion, since no foreground services are supported there. Depending on the Android version and manufacturer of the device, further measures are necessary (see example).
IsRunningInCompanion
Returns whether the application is currently running in the Companion.
SmallIconImage
Since API level 23! Asset name (category Media in the App Inventor) of the file for the icon in the status bar (see Notification.Builder.setSamallIcon). This information is ignored for versions up to API Level 22. See also Metadata and appearance.
Whether the icon is displayed depends on the Android version, the device manufacturer and the level of importance of the notification channel.
Note: Changing this property will take effect with the next call to ShowNotification.
SmallSystemIcon
Icon for Android versions up to API level 22. For possible options see: System Notification Icons. See also Metadata and appearance.
Whether the icon is displayed depends on the Android version, the device manufacturer and the level of importance of the notification channel.
Note: Changing this property will take effect with the next call to ShowNotification.
StateIsPlaying
Returns the status of the notification. true: The pause symbol () is displayed. false: the play symbol () is displayed.
SupportFastForward
The Fast Forward icon (>>) is shown in the notification.
Note: Changing this property will take effect with the next call to ShowNotification.
SupportRewind
The Rewind icon (<<) is shown in the notification.
Note: Changing this property will take effect with the next call to ShowNotification.
SupportSkipToNext
The Skip to next icon (>|) is shown in the notification.
Note: Changing this property will take effect with the next call to ShowNotification.
SupportSkipToPrevious
The Skip to previous icon (|<) is shown in the notification.
Note: Changing this property will take effect with the next call to ShowNotification.
Version
Version name of the extension. Version names for Kodular begin with a "K", eg "K1.0.0".
VersionSDK
The API level of the currently running Android instance.

Methods

FastForward ()
Triggers the OnFastForward event . Buttons on the user interface can thus trigger the same event (and therefore the same code) as tapping the media button Fast Forward.
GetAppDataDir ()
Returns the path for the app sepcific directory. Calling GetAppDataDir()on my test device (in the example below) returns the following result:
     /data/user/0/appinventor.ai_bienonline.UrsMediaNotificationAI2/
GetDownloadDir (Prefix)
Returns the path for the download directory. Prefix is placed in front of the name. Calling GetDownloadDir("file://") on my test device (in the example below) returns the following result:
     file:///storage/emulated/0/Download/
GetVolumes (Prefix)
Returns a list of the installed volumes. The internal memory can usually be found under index 1, the external SD card under index 2. Prefix is placed in front of the name. Calling getVolumes("file://) on my test device returns the following items :
     file:///storage/emulated/
     file:///storage/F277-0260/
HideChannel ()
Hides the channel. Since API level 26!
If a new channel is created with the same ID, the deleted channel will be restored with all the settings it had before it was deleted.
Pause ()
Triggers the OnPause event . Buttons on the user interface can thus trigger the same event (and therefore the same code) as tapping the media button Pause.
Play ()
Triggers the OnPlay event . Buttons on the user interface can thus trigger the same event (and therefore the same code) as tapping the media button Play.
Rewind ()
Triggers the OnRewind event . Buttons on the user interface can thus trigger the same event (and therefore the same code) as tapping the media button Rewind.
SetMetaData (Title, Artist, AlbumImage)
The metadata is applied to the notification. How the parameter AlbumImage can be fielld is explained in more detail in the section Metadata and Appearance. If the album image cannot be loaded, the Screen.ErrorOccurred event is triggered with the error number 17500. The meta data is created without an album image
Note: Changing this data will take effect with the next call to ShowNotification.
SetMetaDataEx (Title, Artist, AlbumImage, Duration)
The metadata is applied to the notification. How the parameter AlbumImage can be fielld is explained in more detail in the section Metadata and Appearance. If the album image cannot be loaded, the Screen.ErrorOccurred event is triggered with the error number 17500. The meta data is created without an album image
Note: Changing this data will take effect with the next call to ShowNotification.
SetMetaDataFromMH (MediaHelper)
The metadata are loaded from an UrsMediaHelper component.
If the specified component is not of type UrsMediaHelper, the Screen.ErrorOccurred event is triggered with the error number 17500.
Note:
Changing this data will take effect with the next call to ShowNotification.
SetStatePaused ()
The notification state is set to Pause. The play symbol () is displayed.
Note: Changing this data will take effect with the next call to ShowNotification.
SetStatePlaying ()
The notification state is set to Play. The pause symbol () is displayed.
Note: Changing this data will take effect with the next call to ShowNotification.
ShowNotification ()
The notification is created / displayed. If property ForegroundService is set, a foreground service is started in the compiled app (not in Comapnion). For possible errors see section Error handling.
ShowWithProgressBar (CurrentPosition)
The notification is created / displayed. Since Android 10 and if duration is set via SetMetaDataEx a progress bar is diaplayed. The position of the progress marker is determined from the information on CurrentPosition in relation to Duration.
 If property ForegroundService is set, a foreground service is started in the compiled app (not in Comapnion). For possible errors see section Error handling.
SkipToNext ()
Triggers the OnSkipToNext event. Buttons on the user interface can thus trigger the same event (and therefore the same code) as tapping the media button Skip to next.
SkipToPrevious ()
Triggers the SkipToPrevious event. Buttons on the user interface can thus trigger the same event (and therefore the same code) as tapping the media button Skip to previous.
Stop ()
Cancels the notification. Any activated Foreground service will be stopped.

Events

OnClick ()
The notification was clicked. Only active if the ClickAble property is set
OnFastForward (FromNotification)
The Fast Forward symbol (>>) in the notification was clicked (FromNotification = true) or the FastForward function was called (FromNotification = false).
OnPause (FromNotification)
The Pause symbol () in the notification was clicked (FromNotification = true) or the Pause function was called (FromNotification = false).
OnPlay (FromNotification)
The Play symbol () in the notification was clicked (FromNotification = true) or the Play function was called (FromNotification = false).
OnRewind (FromNotification)
The Rewind symbol (<<) in the notification was clicked (FromNotification = true) or the Rewind function was called (FromNotification = false).
OnSkipToNext (FromNotification)
The Skip to next symbol (>|) in the notification was clicked (FromNotification = true) or the SkipToNext function was called (FromNotification = false).
OnSkipToPrevious (FromNotification)
The Skip to previous  symbol (|<) in the notification was clicked (FromNotification = true) or the SkipToPrevious function was called (FromNotification = false).
OnStop (FromNotification)
The Stop function was called ( FromNotification = false ). An active foreground service has been stopped.
UserCanceled ()
The user has canceled the notification. Only active if the DeleteAble property is set and no foreground service is active. An active foreground service prohibits the deletion of the associated notification.

Example

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 user interface

Controls

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:

Options

Implementation hints

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.

Build the extension

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!

Providing the libraries

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).

Copy the libraries

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"/>

Declarations

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.

Pacht the .aix files for Kodular

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).

Tools

For developing own extensions I gathered some tips: AI2 FAQ: Develop Extensions. On the site you can find further informations.