flutterjust-audioaudio-service

audio_service notification not nice on real device


I followed instructions to configure audio_service as a foreground service for Android. This works perfectly on simulators. When the app is running a notification is displayed with the icon I provide. Tapping on the notification takes the user back to the app.

But on real devices, there is no icon and tapping on the notification takes user to android app settings instead of the app itself! So it is working, but is different!

Audio service is initialized like this:

    audioHandler = await AudioService.init(
            builder: () => TimerAudioHandler(audioControls),
            config: const AudioServiceConfig(
              androidNotificationChannelId:
                  'app.example.example.timer',
              androidNotificationChannelName: 'Timer',
              androidNotificationOngoing: true,
              androidStopForegroundOnPause: true,
              androidNotificationIcon: 'drawable/ic_notification',
            ),
          );

And I do request user to allow notifications, this all works fine.

I suspect the issue must be to do with permissions as sims are a little lax there. What am I missing in the Android manifest below?

Note that the app is also using flutter_local_notifications, thus more permissions for that...

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.android.vending.BILLING" />
    <!-- for notifications -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>


    <uses-feature android:name="android.software.leanback"
        android:required="false" />
    <uses-feature android:name="android.hardware.touchscreen"
        android:required="false" />


    <application
        android:label="Workout From Home"
        android:banner="@drawable/ic_banner"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:enableOnBackInvokedCallback="true">
               
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize"
            android:screenOrientation="unspecified"> 
       
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme"
            />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

        <!-- ADD THIS "SERVICE" element -->
        <service android:name="com.ryanheise.audioservice.AudioService"
            android:foregroundServiceType="mediaPlayback"
            android:exported="true" tools:ignore="Instantiatable">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>

        <!-- ADD THIS "RECEIVER" element -->
        <receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver"
            android:exported="true" tools:ignore="Instantiatable">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
        </receiver> 

        <receiver android:exported="false"
            android:name="com.dexterous.flutterlocalnotifications.ActionBroadcastReceiver" />
        <receiver android:exported="false"
            android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
        <receiver android:exported="false"
            android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
                <action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
            </intent-filter>
        </receiver>

    </application>

</manifest> 

Solution

  • The problem was that R8 strips the custom icon from build and that resulted in notification not working correctly. I missed below in the documentation, that fixed it. Perhaps documentation should specify that failing to do this will not just affect the icon, but would affect how notification works more fundamentally!

    If you use any custom icons in notification, create the file android/app/src/main/res/raw/keep.xml to prevent them from being stripped during the build process:

    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@drawable/*" />`
    

    By default plugin's default icons are not stripped by R8. If you don't use them, you may selectively strip them. For example, the rules below will keep all your icons and discard all the plugin's:

    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@drawable/*"
    tools:discard="@drawable/audio_service_*" 
    />