Команды воспроизведения мультимедиа в Google Assistant не запускают мое приложение

Мне не удается заставить Google Assistant воспроизводить мультимедиа для моего мультимедийного приложения.

Я проверил с помощью приложения Media Controller Tester , что действия воспроизведения работают. Я могу использовать действия с открытыми функциями с помощником.

Но каждый раз я пытаюсь использовать такие фразы, как Play AppName или же Play Station on AppName, Ассистент пытается запустить TuneIn. Если я попробую Play music on AppName Ассистент запускает YouTube Music.

Я пробовал все в документации здесь и использовал 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, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 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). Добавление этого не помогает.

0 ответов

Другие вопросы по тегам