How to get album artwork in Android from currently playing media using Jetpack Media3?

119 views Asked by At

Media3 has a nice API for getting media files but I am running into a permission error.

In my manifest I have a service that has permission to listen to notifications

        <service android:name=".NotificationService"
            android:label="NotificationService"
            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
            android:exported="true">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            </intent-filter>
        </service>

This is what the notification service looks like:

package com.aatorque.stats

import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.FutureCallback
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.MoreExecutors
import timber.log.Timber

class NotificationService : Service() {

    override fun onCreate() {
        super.onCreate()

        val sessionToken = SessionToken.getAllServiceTokens(this)
        val listener = object : Player.Listener {
            override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) {
                Timber.d("artist ${mediaMetadata.artist}")
                super.onMediaMetadataChanged(mediaMetadata)
            }

            override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
                super.onMediaItemTransition(mediaItem, reason)
                Timber.d("artist ${mediaItem?.mediaMetadata?.artist}")
            }
        }
        for (token in sessionToken) {
            val controller = MediaController.Builder(this, token).buildAsync()
            Futures.addCallback(controller, object : FutureCallback<MediaController> {
                override fun onSuccess(result: MediaController?) {
                    result?.addListener(listener)
                    Timber.d("success ${result?.mediaMetadata?.artist}")
                }

                override fun onFailure(t: Throwable) {
                    Timber.d(t, "failure ${token.packageName}")
                }

            }, MoreExecutors.directExecutor())
        }
    }
    override fun onBind(intent: Intent): IBinder? {
        return null
    }
}

I can see it getting all the services in getAllServiceTokens. However all the apps I care about reject the connection request:

failure com.spotify.music
java.lang.SecurityException: Session rejected the connection request.
at androidx.media3.session.MediaControllerHolder.maybeSetException(MediaControllerHolder.java:67)
at androidx.media3.session.MediaControllerHolder.onRejected(MediaControllerHolder.java:57)
at androidx.media3.session.MediaController.release(MediaController.java:529)
at androidx.media3.session.MediaControllerImplLegacy$ConnectionCallback.onConnectionFailed(MediaControllerImplLegacy.java:1720)
at android.support.v4.media.MediaBrowserCompat$ConnectionCallback$ConnectionCallbackApi21.onConnectionFailed(MediaBrowserCompat.java:721)
at android.media.browse.MediaBrowser$7.run(MediaBrowser.java:649)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8762)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Is there a simple way to get what is currently playing without listening to intents? I have my app enabled to listen to notifications and Spotify enabled to allow other apps to see what it is playing. I don't want to use APIs specific to one application.

0

There are 0 answers