Google 助理上的媒体播放命令无法启动我的应用

问题描述

我在让 Google 助理为我的媒体应用播放媒体时遇到了一些问题。

我已经使用 Media Controller Tester 应用验证了播放操作正在运行。我可以通过 Google 助理使用 Open Feature Actions。

但每次我尝试使用 Play AppNamePlay Station on AppName 之类的短语时,Google 助理都会尝试启动 TuneIn。 如果我尝试 Play music on AppName 助理启动 YouTube 音乐。

我已经尝试了文档 here 中的所有内容,并使用 UAMP 作为基础(我也看到了类似的行为)

这是我的音频服务的精简版:

class AudioService : MediabrowserServiceCompat() {

    @Inject
    lateinit var audioServicebrowserManager: AudioServicebrowserManager

    @Inject
    lateinit var schedulerProvider: RxSchedulerProvider

    @Inject
    lateinit var playbackPreparer: AppPlaybackPreparer

    @Inject
    lateinit var playbackControldispatcher: AppControldispatcher

    @Inject
    lateinit var audioProvider: AudioProvider

    @Inject
    lateinit var playbackManager: PlaybackManager

    @Inject
    lateinit var mediaSessionChangedCallback: MediaSessionChangedCallback

    private lateinit var mediaSession: MediaSessionCompat
    private lateinit var mediaSessionConnector: MediaSessionConnector
    private lateinit var mediaController: MediaControllerCompat
    private lateinit var audionotificationmanager: Audionotificationmanager

    private lateinit var packageValidator: PackageValidator

    private val disposables = Compositedisposable()

    companion object {
        private const val SEEK_BACKWARD_INCREMENT = 15000
        private const val SEEK_FORWARD_INCREMENT = 30000

        private const val MEDIA_SESSION_TAG: String = "AudioService"

        internal const val MetaDATA_MEDIA_TYPE = "au.net.app.player.service.Metadata.mediaType"
        internal const val MetaDATA_MEDIA_TYPE_ondemand_VIDEO = 0L
        internal const val MetaDATA_MEDIA_TYPE_ondemand_AUdio = 2L
        internal const val MetaDATA_MEDIA_TYPE_LIVE = 1L

        val DEFAULT_PLAYBACK_STATE: PlaybackStateCompat = PlaybackStateCompat.Builder()
                .setState(PlaybackStateCompat.STATE_NONE,PlaybackStateCompat.PLAYBACK_POSITION_UNKNowN,1.0f)
                .build()
    }

    override fun onCreate() {
        AndroidInjection.inject(this)
        super.onCreate()

        mediaSession = MediaSessionCompat(this,MEDIA_SESSION_TAG).apply {
            setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
            isActive = true
        }

        sessionToken = mediaSession.sessionToken

        mediaSessionConnector = MediaSessionConnector(mediaSession).apply {
            setRewindIncrementMs(SEEK_BACKWARD_INCREMENT)
            setFastForwardIncrementMs(SEEK_FORWARD_INCREMENT)
            setPlaybackPreparer(playbackPreparer)
            setQueueNavigator(AppQueueNavigator(mediaSession,audioProvider,this@AudioService,this))
            setControldispatcher(playbackControldispatcher)
            setPlayer(playbackManager.currentPlayback.playerImpl)
            registerCustomCommandReceiver(playbackManager.mediaSessionCommandReceiver)
        }

        disposables.add(
                playbackManager.currentPlaybackObservable.subscribe { currentPlayback ->
                    mediaSessionConnector.setPlayer(currentPlayback.playerImpl)
                }
        )

        mediaController = MediaControllerCompat(this,mediaSession)
        mediaController.registerCallback(mediaSessionChangedCallback)

        try {
            audionotificationmanager = Audionotificationmanager(this,mediaController)
        } catch (e: remoteexception) {
            throw IllegalStateException("Could not create a Medianotificationmanager",e)
        }

        packageValidator = PackageValidator(this,R.xml.allowed_media_browser_callers)
    }

    private var currentLoadChildrendisposable: disposable? = null
    override fun onLoadChildren(parentId: String,result: Result<MutableList<MediabrowserCompat.MediaItem>>) {
        Timber.d("""
            onLoadChildren(
                parentId = $parentId,result = $result
            )
        """.trimIndent())
    }

    override fun onGetRoot(clientPackageName: String,clientUid: Int,rootHints: Bundle?): browserRoot? {
        Timber.d("""
            onGetRoot(
                clientPackageName = $clientPackageName,clientUid = $clientUid,rootHints = $rootHints
            )
        """.trimIndent())
    }

    override fun onStartCommand(intent: Intent?,flags: Int,startId: Int): Int {
        super.onStartCommand(intent,flags,startId)

        return START_STICKY
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)

        playbackManager.handleStop()
        stopSelf()
    }

    override fun onDestroy() {
        super.onDestroy()

        playbackManager.handleStop()

        disposables.dispose()

        mediaSession.isActive = false
        mediaSession.release()
    }
}

模块清单(注意 - 该服务不在我的主应用模块中)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="au.net.app.player.service">

    <uses-permission android:name="android.permission.INTERNET" />

    <application>

        <service
            android:name="au.net.app.player.service.AudioService"
            android:exported="true">
            <intent-filter>
                <action android:name="android.media.browse.MediabrowserService" />
            </intent-filter>
        </service>

        <receiver android:name="androidx.media.session.MediaButtonReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

        <Meta-data
            android:name="com.google.android.gms.car.application"
            android:resource="@xml/automotive_app_desc" />

    </application>

</manifest>

在主应用清单中:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="au.net.app"
    android:installLocation="auto">

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:name=".AppApplication"
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:networkSecurityConfig="@xml/network_security_config"
        android:supportsRtl="true"
        android:hardwareAccelerated="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".mainscreen.MainActivity"
            android:launchMode="singleTask"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar"
            android:resizeableActivity="true"
            android:supportsPictureInPicture="true"
            android:windowSoftInputMode="adjustResize">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <!-- Intent filters to open Feature screens -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.broWSABLE" />
                <data
                    android:host="feature"
                    android:pathPattern="/.*"
                    android:scheme="${APP_SCHEME}" />
            </intent-filter>

            <!-- Declares that the app handles SEARCH intent for media playback -->
            <!-- This is mandatory for Android Auto support: -->
            <!-- https://stackoverflow.com/questions/31953155/android-auto-voice-cannot-perform-play-x-on-y/31976075#31976075 -->
            <intent-filter>
                <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>


        <!-- required for Google Assistant integration -->
        <Meta-data android:name="com.google.android.actions" android:resource="@xml/actions" />

    </application>

</manifest>

我还尝试使用以下方法设置我的播放状态:

        val DEFAULT_PLAYBACK_STATE: PlaybackStateCompat = PlaybackStateCompat.Builder()
                .setActions(getSupportedActions())
                .setState(PlaybackStateCompat.STATE_NONE,1.0f)
                .build()

        private fun getSupportedActions(): Long {
            return PlaybackStateCompat.ACTION_PLAY or
                    PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH or
                    PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
                    PlaybackStateCompat.ACTION_SKIP_TO_PREVIoUS or
                    PlaybackStateCompat.ACTION_PLAY_PAUSE
        }

但我的理解是我不需要,因为 MediaSessionConnector 应该负责(因为我使用的是 ExoPlayer)。添加这个没有帮助。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...