دستیار گوگل و برنامه های رسانه ای

دستیار گوگل به شما امکان می‌دهد از دستورات صوتی برای کنترل بسیاری از دستگاه‌ها، مانند گوگل هوم، تلفن همراه و موارد دیگر استفاده کنید. این دستیار دارای قابلیت داخلی برای درک دستورات رسانه‌ای (مانند «پخش آهنگی از بیانسه») است و از کنترل‌های رسانه‌ای (مانند مکث، رد کردن، جلو بردن سریع، تایید انگشت شست) پشتیبانی می‌کند.

دستیار با استفاده از یک جلسه رسانه‌ای با برنامه‌های رسانه‌ای اندروید ارتباط برقرار می‌کند. می‌تواند از اهداف یا سرویس‌ها برای راه‌اندازی برنامه شما و شروع پخش استفاده کند. برای بهترین نتیجه، برنامه شما باید تمام ویژگی‌های شرح داده شده در این صفحه را پیاده‌سازی کند.

از یک جلسه رسانه‌ای استفاده کنید

هر برنامه صوتی و تصویری باید یک جلسه رسانه‌ای (media session) پیاده‌سازی کند تا دستیار بتواند پس از شروع پخش، کنترل‌های انتقال را اجرا کند.

توجه داشته باشید که اگرچه دستیار فقط از اقدامات ذکر شده در این بخش استفاده می‌کند، بهترین روش، پیاده‌سازی تمام APIهای آماده‌سازی و پخش برای اطمینان از سازگاری با سایر برنامه‌ها است. برای هر اقدامی که شما پشتیبانی نمی‌کنید، فراخوانی‌های جلسه رسانه می‌توانند به سادگی با استفاده از ERROR_CODE_NOT_SUPPORTED خطایی را برگردانند.

با تنظیم این پرچم‌ها در شیء MediaSession برنامه خود، کنترل‌های رسانه و انتقال را فعال کنید:

کاتلین

session.setFlags(
        MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)

جاوا

session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

جلسه رسانه برنامه شما باید اقداماتی را که پشتیبانی می‌کند، اعلام کند و فراخوانی‌های مربوط به جلسه رسانه را پیاده‌سازی کند. اقدامات پشتیبانی شده خود را در setActions() اعلام کنید.

پروژه نمونه پخش‌کننده موسیقی یونیورسال اندروید، مثال خوبی از نحوه راه‌اندازی یک جلسه رسانه‌ای است.

اقدامات پخش

برای شروع پخش از یک سرویس ، یک جلسه رسانه‌ای باید این اقدامات PLAY و فراخوانی‌های آنها را داشته باشد:

اکشن تماس برگشتی
ACTION_PLAY onPlay()
ACTION_PLAY_FROM_SEARCH onPlayFromSearch()
ACTION_PLAY_FROM_URI (*) onPlayFromUri()

جلسه شما همچنین باید این اقدامات PREPARE و فراخوانی‌های آنها را پیاده‌سازی کند:

اکشن تماس برگشتی
ACTION_PREPARE onPrepare()
ACTION_PREPARE_FROM_SEARCH onPrepareFromSearch()
ACTION_PREPARE_FROM_URI (*) onPrepareFromUri()

(*) اقدامات مبتنی بر URI دستیار گوگل فقط برای شرکت‌هایی که URIها را به گوگل ارائه می‌دهند، کار می‌کند. برای کسب اطلاعات بیشتر در مورد توصیف محتوای رسانه‌ای خود به گوگل، به اقدامات رسانه‌ای مراجعه کنید.

با پیاده‌سازی APIهای آماده‌سازی، می‌توان تأخیر پخش پس از یک فرمان صوتی را کاهش داد. برنامه‌های رسانه‌ای که می‌خواهند تأخیر پخش را بهبود بخشند، می‌توانند از زمان اضافی برای شروع ذخیره‌سازی محتوا و آماده‌سازی پخش رسانه استفاده کنند.

تجزیه عبارات جستجو

وقتی کاربر یک آیتم رسانه‌ای خاص، مانند «پخش موسیقی جاز در [نام برنامه شما]» یا «گوش دادن به [عنوان آهنگ]» را جستجو می‌کند، متد فراخوانی onPrepareFromSearch() یا onPlayFromSearch() یک پارامتر کوئری و یک بسته اضافی دریافت می‌کند.

برنامه شما باید با دنبال کردن این مراحل، عبارت جستجوی صوتی را تجزیه و تحلیل کرده و پخش را شروع کند:

  1. برای فیلتر کردن نتایج، از بسته‌ی افزونه‌ها و رشته‌ی جستجوی بازگشتی از جستجوی صوتی استفاده کنید.
  2. بر اساس این نتایج، یک صف پخش بسازید.
  3. مرتبط‌ترین آیتم رسانه‌ای را از نتایج پخش کنید.

متد onPlayFromSearch() یک پارامتر اضافی با اطلاعات دقیق‌تر از جستجوی صوتی دریافت می‌کند. این اطلاعات اضافی به شما کمک می‌کنند محتوای صوتی را در برنامه خود برای پخش پیدا کنید. اگر نتایج جستجو قادر به ارائه این داده‌ها نباشند، می‌توانید منطقی را برای تجزیه عبارت جستجوی خام و پخش آهنگ‌های مناسب بر اساس عبارت جستجو پیاده‌سازی کنید.

موارد اضافی زیر در سیستم عامل Android Automotive و Android Auto پشتیبانی می‌شوند:

قطعه کد زیر نحوه‌ی بازنویسی متد onPlayFromSearch() را در پیاده‌سازی MediaSession.Callback برای تجزیه‌ی جستجوی صوتی و شروع پخش نشان می‌دهد:

کاتلین

override fun onPlayFromSearch(query: String?, extras: Bundle?) {
    if (query.isNullOrEmpty()) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS)
        if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) {
            isArtistFocus = true
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
        } else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) {
            isAlbumFocus = true
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM)
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    var result: String? = when {
        isArtistFocus -> artist?.also {
            searchMusicByArtist(it)
        }
        isAlbumFocus -> album?.also {
            searchMusicByAlbum(it)
        }
        else -> null
    }
    result = result ?: run {
        // No focus found, search by query for song title
        query?.also {
            searchMusicBySongTitle(it)
        }
    }

    if (result?.isNotEmpty() == true) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result)
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

جاوا

@Override
public void onPlayFromSearch(String query, Bundle extras) {
    if (TextUtils.isEmpty(query)) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS);
        if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)) {
            isArtistFocus = true;
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST);
        } else if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE)) {
            isAlbumFocus = true;
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM);
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    if (isArtistFocus) {
        result = searchMusicByArtist(artist);
    } else if (isAlbumFocus) {
        result = searchMusicByAlbum(album);
    }

    if (result == null) {
        // No focus found, search by query for song title
        result = searchMusicBySongTitle(query);
    }

    if (result != null && !result.isEmpty()) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result);
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

برای مثالی دقیق‌تر در مورد نحوه‌ی پیاده‌سازی جستجوی صوتی برای پخش محتوای صوتی در برنامه‌تان، به نمونه‌ی Universal Android Music Player مراجعه کنید.

مدیریت کوئری‌های خالی

اگر onPrepare() ، onPlay() ، onPrepareFromSearch() یا onPlayFromSearch() بدون جستجوی کوئری فراخوانی شوند، برنامه رسانه شما باید رسانه "فعلی" را پخش کند. اگر هیچ رسانه فعلی وجود نداشته باشد، برنامه باید سعی کند چیزی مانند آهنگی از جدیدترین لیست پخش یا یک صف تصادفی را پخش کند. دستیار از این APIها زمانی استفاده می‌کند که کاربر بدون اطلاعات اضافی درخواست "پخش موسیقی در [نام برنامه شما]" را داشته باشد.

وقتی کاربر می‌گوید «در [نام برنامه شما] موسیقی پخش کنید» ، سیستم عامل اندروید اتو یا اندروید اتو تلاش می‌کند برنامه شما را اجرا کند و با فراخوانی متد onPlayFromSearch() برنامه شما، صدا را پخش کند. با این حال، از آنجایی که کاربر نام آیتم رسانه را نگفته است، متد onPlayFromSearch() یک پارامتر کوئری خالی دریافت می‌کند. در این موارد، برنامه شما باید با پخش فوری صدا، مانند آهنگی از جدیدترین لیست پخش یا یک صف تصادفی، پاسخ دهد.

اعلام پشتیبانی از دستورات صوتی قدیمی

در بیشتر موارد، مدیریت اقدامات پخش که در بالا توضیح داده شد، تمام قابلیت‌های پخش مورد نیاز برنامه شما را فراهم می‌کند. با این حال، برخی از سیستم‌ها نیاز دارند که برنامه شما حاوی یک فیلتر Intent برای جستجو باشد. شما باید پشتیبانی از این فیلتر Intent را در فایل‌های مانیفست برنامه خود اعلام کنید.

این کد را در فایل مانیفست برای یک برنامه تلفن همراه قرار دهید:

<activity>
    <intent-filter>
        <action android:name=
             "android.media.action.MEDIA_PLAY_FROM_SEARCH" />
        <category android:name=
             "android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

کنترل‌های حمل و نقل

پس از فعال شدن جلسه رسانه‌ای برنامه شما، دستیار می‌تواند دستورات صوتی را برای کنترل پخش و به‌روزرسانی فراداده‌های رسانه صادر کند. برای اینکه این کار انجام شود، کد شما باید اقدامات زیر را فعال کرده و فراخوانی‌های مربوطه را پیاده‌سازی کند:

اکشن تماس برگشتی توضیحات
ACTION_SKIP_TO_NEXT onSkipToNext() ویدیوی بعدی
ACTION_SKIP_TO_PREVIOUS onSkipToPrevious() آهنگ قبلی
ACTION_PAUSE, ACTION_PLAY_PAUSE onPause() مکث
ACTION_STOP onStop() توقف
ACTION_PLAY onPlay() رزومه
ACTION_SEEK_TO onSeekTo() 30 ثانیه به عقب برگردید
ACTION_SET_RATING onSetRating(android.support.v4.media.RatingCompat) شست بالا/پایین.
ACTION_SET_CAPTIONING_ENABLED onSetCaptioningEnabled(boolean) زیرنویس را روشن/خاموش کنید.

لطفا توجه داشته باشید:

  • برای اینکه دستورات جستجو کار کنند، PlaybackState باید از نظر state, position, playback speed, and update time به‌روز باشد. برنامه باید هنگام تغییر وضعیت setPlaybackState() را فراخوانی کند.
  • برنامه رسانه همچنین باید متادیتای جلسه رسانه را به‌روز نگه دارد. این امر از سوالاتی مانند "چه آهنگی در حال پخش است؟" پشتیبانی می‌کند. برنامه باید هنگام تغییر فیلدهای مربوطه (مانند عنوان آهنگ، هنرمند و نام) تابع setMetadata() را فراخوانی کند.
  • MediaSession.setRatingType() باید برای نشان دادن نوع رتبه‌بندی پشتیبانی‌شده توسط برنامه تنظیم شود، و برنامه باید onSetRating() را پیاده‌سازی کند. اگر برنامه از رتبه‌بندی پشتیبانی نمی‌کند، باید نوع رتبه‌بندی را روی RATING_NONE تنظیم کند.

احتمالاً عملکردهای صوتی که پشتیبانی می‌کنید بر اساس نوع محتوا متفاوت خواهد بود.

نوع محتوا اقدامات لازم
موسیقی

باید از موارد زیر پشتیبانی کند : پخش، مکث، توقف، رفتن به بعدی و رفتن به قبلی

اکیداً توصیه می‌کنیم از موارد زیر پشتیبانی کنید : به دنبال

پادکست

باید از موارد زیر پشتیبانی کند : پخش، مکث، توقف، و جستجو برای

پشتیبانی توصیه شده برای : پرش به بعدی و پرش به قبلی

کتاب صوتی باید از موارد زیر پشتیبانی کند : پخش، مکث، توقف، و جستجو برای
رادیو باید از موارد زیر پشتیبانی کند : پخش، مکث و توقف
اخبار باید از موارد زیر پشتیبانی کند : پخش، مکث، توقف، رفتن به بعدی و رفتن به قبلی
ویدئو

پشتیبانی از : پخش، مکث، توقف، جستجو، عقب بردن و سریع به جلو

اکیداً توصیه می‌شود از موارد زیر پشتیبانی کنید : پرش به بعدی و پرش به قبلی

شما باید تا جایی که محصولات شما اجازه می‌دهند، از اقدامات ذکر شده در بالا پشتیبانی کنید، اما همچنان به سایر اقدامات نیز به خوبی پاسخ دهید. به عنوان مثال، اگر فقط کاربران ویژه امکان بازگشت به مورد قبلی را داشته باشند، اگر یک کاربر سطح رایگان از دستیار بخواهد به مورد قبلی برگردد، ممکن است خطایی ایجاد شود. برای راهنمایی بیشتر به بخش مدیریت خطا مراجعه کنید.

نمونه سوالات صوتی برای امتحان کردن

جدول زیر برخی از نمونه سوالاتی را که باید هنگام آزمایش پیاده‌سازی خود استفاده کنید، شرح می‌دهد:

پاسخ به تماس MediaSession عبارت «سلام گوگل» برای استفاده
onPlay()

«بازی کن.»

«رزومه»

onPlayFromSearch()
onPlayFromUri()
موسیقی

«پخش موسیقی یا آهنگ در (نام برنامه) ». این یک پرس‌وجوی خالی است.

«پخش (آهنگ | هنرمند | آلبوم | ژانر | لیست پخش) در (نام برنامه)

رادیو «پخش (فرکانس | ایستگاه) روی (نام برنامه) ».
کتاب صوتی

«کتاب صوتی من را در (نام برنامه) بخوانید.»

« (کتاب صوتی) را در (نام برنامه) بخوانید.»

پادکست‌ها « (پادکست) را در (نام برنامه) پخش کن.»
onPause() «مکث کن.»
onStop() «بس کن.»
onSkipToNext() «بعدی (آهنگ | قسمت | قطعه) »
onSkipToPrevious() « (آهنگ | قسمت | قطعه) قبلی.»
onSeekTo()

«راه‌اندازی مجدد»

« ## ثانیه جلوتر برو.»

«برگرد به دقیقه قبل

ناموجود ( MediaMetadata خود را به‌روز نگه دارید) «چه چیزی پخش می‌شود؟»

خطاها

دستیار، خطاهای یک جلسه رسانه‌ای را هنگام وقوع مدیریت می‌کند و آنها را به کاربران گزارش می‌دهد. مطمئن شوید که جلسه رسانه‌ای شما، وضعیت انتقال و کد خطا را در PlaybackState خود به درستی به‌روزرسانی می‌کند، همانطور که در بخش «کار با یک جلسه رسانه‌ای» توضیح داده شده است. دستیار تمام کدهای خطایی را که توسط getErrorCode() برگردانده می‌شوند، تشخیص می‌دهد.

موارد معمولاً نادرست رسیدگی شده

در اینجا چند نمونه از موارد خطا وجود دارد که باید مطمئن شوید به درستی آنها را مدیریت می‌کنید:

  • کاربر باید وارد سیستم شود
    • کد خطای PlaybackState را روی ERROR_CODE_AUTHENTICATION_EXPIRED تنظیم کنید.
    • پیام خطای PlaybackState را تنظیم کنید.
    • در صورت نیاز برای پخش، وضعیت PlaybackState را روی STATE_ERROR تنظیم کنید، در غیر این صورت بقیه‌ی PlaybackState را به همان صورت قبلی نگه دارید.
  • کاربر درخواست یک اقدام غیرقابل دسترس را می‌دهد
    • کد خطای PlaybackState به طور مناسب تنظیم کنید. برای مثال، اگر اکشن پشتیبانی نمی‌شود، PlaybackState روی ERROR_CODE_NOT_SUPPORTED یا اگر اکشن دارای قابلیت ورود به سیستم محافظت شده است، ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED تنظیم کنید.
    • پیام خطای PlaybackState را تنظیم کنید.
    • بقیه‌ی PlaybackState را به همان شکل فعلی نگه دارید.
  • کاربر محتوایی را درخواست می‌کند که در برنامه موجود نیست
    • کد خطای PlaybackState را به طور مناسب تنظیم کنید. برای مثال، ERROR_CODE_NOT_AVAILABLE_IN_REGION استفاده کنید.
    • پیام خطای PlaybackState را تنظیم کنید.
    • برای قطع پخش، وضعیت PlaybackSate را روی STATE_ERROR تنظیم کنید، در غیر این صورت بقیه‌ی PlaybackState را به همان شکل قبلی نگه دارید.
  • کاربر محتوایی را درخواست می‌کند که دقیقاً مشابه آن در دسترس نیست. برای مثال، یک کاربر با سطح دسترسی رایگان درخواست محتوایی را می‌کند که فقط برای کاربران با سطح دسترسی ویژه در دسترس است.
    • توصیه می‌کنیم خطایی ارسال نکنید و در عوض، یافتن چیزی مشابه پخش را در اولویت قرار دهید. دستیار صوتی قبل از شروع پخش، مرتبط‌ترین پاسخ صوتی را بیان خواهد کرد.

پخش با هدف

دستیار می‌تواند با ارسال یک هدف (intent) با یک لینک عمیق (deep link)، یک برنامه صوتی یا تصویری را اجرا کرده و پخش را شروع کند.

نیت و پیوند عمیق آن می‌تواند از منابع مختلفی ناشی شود:

  • وقتی دستیار گوگل در حال اجرای یک برنامه موبایل است، می‌تواند از جستجوی گوگل برای بازیابی محتوای علامت‌گذاری‌شده‌ای استفاده کند که یک عمل ساعت را با یک لینک ارائه می‌دهد.
  • وقتی دستیار در حال اجرای یک برنامه تلویزیونی است، برنامه شما باید شامل یک ارائه‌دهنده جستجوی تلویزیون باشد تا URIها را برای محتوای رسانه نمایش دهد. دستیار یک پرس‌وجو به ارائه‌دهنده محتوا ارسال می‌کند که باید یک intent حاوی یک URI برای لینک عمیق و یک اقدام اختیاری را برگرداند. اگر پرس‌وجو یک action در intent برگرداند، دستیار آن action و URI را به برنامه شما ارسال می‌کند. اگر ارائه‌دهنده actionی را مشخص نکرده باشد، دستیار ACTION_VIEW به Intent اضافه می‌کند.

دستیار EXTRA_START_PLAYBACK اضافی را با مقدار true به intent ارسالی به برنامه شما اضافه می‌کند. برنامه شما باید هنگام دریافت intent با EXTRA_START_PLAYBACK پخش را شروع کند.

مدیریت intentها در حین فعال بودن

کاربران می‌توانند از دستیار بخواهند چیزی را پخش کند در حالی که برنامه شما هنوز در حال پخش محتوای درخواست قبلی است. این بدان معناست که برنامه شما می‌تواند در حالی که فعالیت پخش آن از قبل راه‌اندازی و فعال است، اهداف جدیدی را برای شروع پخش دریافت کند.

فعالیت‌هایی که از intentها با لینک‌های عمیق پشتیبانی می‌کنند، باید onNewIntent() را برای مدیریت درخواست‌های جدید، override کنند.

هنگام شروع پخش، دستیار ممکن است پرچم‌های دیگری را به هدفی که به برنامه شما ارسال می‌کند اضافه کند. به طور خاص، ممکن است FLAG_ACTIVITY_CLEAR_TOP یا FLAG_ACTIVITY_NEW_TASK یا هر دو را اضافه کند. اگرچه کد شما نیازی به مدیریت این پرچم‌ها ندارد، اما سیستم اندروید به آنها پاسخ می‌دهد. این ممکن است بر رفتار برنامه شما تأثیر بگذارد، زمانی که درخواست پخش دوم با یک URI جدید در حالی که URI قبلی هنوز در حال پخش است، می‌رسد. ایده خوبی است که نحوه پاسخ برنامه خود را در این حالت آزمایش کنید. می‌توانید از ابزار خط فرمان adb برای شبیه‌سازی وضعیت استفاده کنید (ثابت 0x14000000 OR بیتی بولی دو پرچم است):

adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<first_uri>"' -f 0x14000000
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<second_uri>"' -f 0x14000000

پخش از یک سرویس

اگر برنامه شما دارای یک media browser service است که امکان اتصال از Assistant را فراهم می‌کند، Assistant می‌تواند برنامه را با برقراری ارتباط با media session سرویس آغاز کند. سرویس مرورگر رسانه هرگز نباید یک Activity را اجرا کند. Assistant Activity شما را بر اساس PendingIntent که با setSessionActivity() تعریف می‌کنید، اجرا خواهد کرد.

هنگام مقداردهی اولیه سرویس مرورگر رسانه، حتماً MediaSession.Token را تنظیم کنید. به یاد داشته باشید که اقدامات پخش پشتیبانی شده را همیشه، از جمله در حین مقداردهی اولیه، تنظیم کنید. دستیار انتظار دارد که برنامه رسانه شما اقدامات پخش را قبل از ارسال اولین دستور پخش توسط دستیار تنظیم کند.

برای شروع از یک سرویس، دستیار، APIهای کلاینت مرورگر رسانه را پیاده‌سازی می‌کند. این دستیار فراخوانی‌های TransportControls را انجام می‌دهد که فراخوانی‌های PLAY action را در جلسه رسانه برنامه شما فعال می‌کند.

نمودار زیر ترتیب فراخوانی‌های تولید شده توسط Assistant و فراخوانی‌های مربوط به جلسه رسانه را نشان می‌دهد. (فراخوان‌های آماده‌سازی فقط در صورتی ارسال می‌شوند که برنامه شما از آنها پشتیبانی کند.) همه فراخوانی‌ها ناهمزمان هستند. Assistant منتظر هیچ پاسخی از برنامه شما نمی‌ماند.

شروع پخش با یک جلسه رسانه‌ای

وقتی کاربر دستور صوتی پخش می‌دهد، دستیار با یک اعلان کوتاه پاسخ می‌دهد. به محض اینکه اعلان کامل شد، دستیار دستور پخش (PLAY) را صادر می‌کند. منتظر هیچ وضعیت پخش خاصی نمی‌ماند.

اگر برنامه شما از اقدامات ACTION_PREPARE_* پشتیبانی کند، دستیار قبل از شروع اعلان، اقدام PREPARE را فراخوانی می‌کند.

اتصال به یک سرویس مرورگر رسانه‌ای (MediaBrowserService)

برای استفاده از یک سرویس برای شروع برنامه شما، دستیار باید بتواند به MediaBrowserService برنامه متصل شود و MediaSession.Token آن را بازیابی کند. درخواست‌های اتصال در متد onGetRoot() سرویس مدیریت می‌شوند. دو روش برای مدیریت درخواست‌ها وجود دارد:

  • پذیرش تمام درخواست‌های اتصال
  • درخواست‌های اتصال را فقط از برنامه‌ی «دستیار» بپذیرید

پذیرش تمام درخواست‌های اتصال

برای اینکه به دستیار اجازه دهید دستوراتی را به جلسه رسانه شما ارسال کند، باید یک BrowserRoot برگردانید. ساده‌ترین راه این است که به همه برنامه‌های MediaBrowser اجازه دهید به MediaBrowserService شما متصل شوند. شما باید یک BrowserRoot غیر تهی برگردانید. در اینجا کد مربوطه از Universal Music Player آمده است:

کاتلین

override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
): BrowserRoot? {

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        Log.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. Returning empty "
                + "browser root so all apps can use MediaController. $clientPackageName")
        return MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null)
    }

    // Return browser roots for browsing...
}

جاوا

@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                             Bundle rootHints) {

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        LogHelper.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. "
                + "Returning empty browser root so all apps can use MediaController."
                + clientPackageName);
        return new MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null);
    }

    // Return browser roots for browsing...
}

بسته و امضای برنامه دستیار را بپذیرید

شما می‌توانید با بررسی نام بسته و امضای سرویس مرورگر رسانه خود، به طور صریح به دستیار اجازه دهید تا به آن متصل شود. برنامه شما نام بسته را در متد onGetRoot از MediaBrowserService شما دریافت خواهد کرد. برای اینکه به دستیار اجازه دهید دستوراتی را به جلسه رسانه شما ارسال کند، باید یک BrowserRoot برگردانید. نمونه Universal Music Player لیستی از نام‌ها و امضاهای بسته شناخته شده را نگهداری می‌کند. در زیر نام‌ها و امضاهای بسته‌ای که توسط دستیار گوگل استفاده می‌شوند، آمده است.

<signature name="Google" package="com.google.android.googlequicksearchbox">
    <key release="false">19:75:b2:f1:71:77:bc:89:a5:df:f3:1f:9e:64:a6:ca:e2:81:a5:3d:c1:d1:d5:9b:1d:14:7f:e1:c8:2a:fa:00</key>
    <key release="true">f0:fd:6c:5b:41:0f:25:cb:25:c3:b5:33:46:c8:97:2f:ae:30:f8:ee:74:11:df:91:04:80:ad:6b:2d:60:db:83</key>
</signature>

<signature name="Google Assistant on Android Automotive OS" package="com.google.android.carassistant">
    <key release="false">17:E2:81:11:06:2F:97:A8:60:79:7A:83:70:5B:F8:2C:7C:C0:29:35:56:6D:46:22:BC:4E:CF:EE:1B:EB:F8:15</key>
    <key release="true">74:B6:FB:F7:10:E8:D9:0D:44:D3:40:12:58:89:B4:23:06:A6:2C:43:79:D0:E5:A6:62:20:E3:A6:8A:BF:90:E2</key>
</signature>