Google Assistant ואפליקציות מדיה

בעזרת Google Assistant תוכלו להשתמש בפקודות קוליות כדי לשלוט במכשירים רבים, כמו Google Home, הטלפון ועוד. יש לו יכולת מובנית להבין פקודות מדיה ("הפעל משהו של ביונסה") והוא תומך באמצעי בקרה של מדיה (כמו השהיה, דילוג, הרצה קדימה, לייק).

‫Assistant מתקשרת עם אפליקציות מדיה ל-Android באמצעות סשן מדיה. היא יכולה להשתמש בכוונה או בשירותים כדי להפעיל את האפליקציה ולהתחיל את ההפעלה. כדי להשיג את התוצאות הטובות ביותר, האפליקציה צריכה להטמיע את כל התכונות שמתוארות בדף הזה.

שימוש בסשן מדיה

כל אפליקציית אודיו ווידאו צריכה להטמיע סשן מדיה כדי שהעוזר הדיגיטלי יוכל להפעיל את אמצעי הבקרה להפעלה אחרי שההפעלה מתחילה.

חשוב לזכור ש-Assistant משתמשת רק בפעולות שמפורטות בקטע הזה, אבל מומלץ להטמיע את כל ממשקי ה-API של ההכנה וההפעלה כדי להבטיח תאימות לאפליקציות אחרות. לגבי פעולות שאין להן תמיכה, אפשר להחזיר שגיאה באמצעות הקריאות החוזרות (callback) של סשן המדיה באמצעות ERROR_CODE_NOT_SUPPORTED.

כדי להפעיל את אמצעי הבקרה של המדיה וההעברה, צריך להגדיר את הדגלים האלה באובייקט MediaSession של האפליקציה:

Kotlin

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

Java

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

בסשן המדיה של האפליקציה צריך להצהיר על הפעולות שהיא תומכת בהן, ולהטמיע את הקריאות החוזרות (callback) המתאימות של סשן המדיה. מצהירים על הפעולות הנתמכות ב-setActions().

פרויקט הדוגמה Universal Android Music Player הוא דוגמה טובה להגדרה של סשן מדיה.

פעולות הפעלה

כדי להתחיל בהפעלה משירות, סשן מדיה צריך לכלול את 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 ב-Google Assistant פועלות רק בחברות שמספקות ל-Google כתובות URI. למידע נוסף על תיאור תוכן המדיה ל-Google, ראו פעולות במדיה.

הטמעה של ממשקי ה-API להכנה יכולה להפחית את זמן האחזור של ההפעלה אחרי פקודה קולית. אפליקציות מדיה שרוצות לשפר את זמן האחזור של ההפעלה יכולות להשתמש בזמן הנוסף כדי להתחיל לשמור תוכן במטמון ולהכין את הפעלת המדיה.

ניתוח שאילתות חיפוש

כשמשתמש מחפש פריט מדיה ספציפי, כמו “Play jazz on [your app name]” או “Listen to [song title]”, שיטת הקריאה החוזרת (callback) onPrepareFromSearch() או onPlayFromSearch() מקבלת פרמטר של שאילתה וחבילת נתונים נוספים.

האפליקציה צריכה לנתח את שאילתת החיפוש הקולי ולהתחיל בהפעלה לפי השלבים הבאים:

  1. משתמשים בחבילת התוספים ובמחרוזת שאילתת החיפוש שמוחזרות מהחיפוש הקולי כדי לסנן את התוצאות.
  2. תבנה תור השמעה על סמך התוצאות האלה.
  3. להפעיל את פריט המדיה הרלוונטי ביותר מתוך התוצאות.

השיטה onPlayFromSearch() מקבלת פרמטר extras עם מידע מפורט יותר מחיפוש קולי. התוספים האלה עוזרים לכם למצוא את תוכן האודיו באפליקציה כדי להפעיל אותו. אם תוצאות החיפוש לא יכולות לספק את הנתונים האלה, אפשר להטמיע לוגיקה כדי לנתח את שאילתת החיפוש הגולמית ולהפעיל את הרצועות המתאימות על סמך השאילתה.

התוספים הבאים נתמכים ב-Android Automotive OS וב-Android Auto:

בקטע הקוד הבא מוצג איך להחליף את השיטה onPlayFromSearch() בהטמעה של MediaSession.Callback כדי לנתח את שאילתת החיפוש הקולי ולהתחיל בהפעלה:

Kotlin

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
    }
}

Java

@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 האלה כשמשתמש מבקש “להשמיע מוזיקה ב[שם האפליקציה שלך]” בלי לספק מידע נוסף.

כשמשתמש אומר “Play music on [your app name]”, מערכת Android Automotive OS או Android Auto מנסה להפעיל את האפליקציה ולהשמיע אודיו על ידי קריאה לשיטה 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>

אמצעי בקרה להפעלה

אחרי שסשן המדיה של האפליקציה פעיל, Assistant יכולה להנפיק פקודות קוליות כדי לשלוט בהפעלה ולעדכן את המטא-נתונים של המדיה. כדי שהפעולה הזו תתבצע, הקוד צריך לאפשר את הפעולות הבאות ולהטמיע את הקריאות החוזרות המתאימות:

פעולה התקשרות חזרה תיאור
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.

סביר להניח שפעולות קוליות שונות יתמכו בסוגי תוכן שונים.

סוג תוכן פעולות נדרשות
מוזיקה

חובה לתמוך: הפעלה, השהיה, עצירה, דילוג אל הבא ודילוג אל הקודם

מומלץ מאוד לתמוך ב: מעבר למיקום מסוים

פודקאסט

חובה לתמוך בפעולות הבאות: הפעלה, השהיה, עצירה ודילוג למיקום מסוים

המלצה לתמיכה ב: דילוג אל הבא ודילוג אל הקודם

ספר אודיו חובה לתמוך בפעולות הבאות: הפעלה, השהיה, עצירה ודילוג למיקום מסוים
רדיו חייבת להיות תמיכה: הפעלה, השהיה ועצירה
חדשות חובה לתמוך: הפעלה, השהיה, עצירה, דילוג אל הבא ודילוג אל הקודם
וידאו

חובה לתמוך בפעולות הבאות: הפעלה, השהיה, עצירה, מעבר למיקום מסוים, הרצה אחורה והרצה קדימה

מומלץ מאוד לתמוך ב: דילוג אל הפריט הבא ודילוג אל הפריט הקודם

אתם צריכים לתמוך בכמה שיותר מהפעולות שמופיעות למעלה, בהתאם למוצרים שאתם מציעים, אבל עדיין להגיב בצורה נאותה לכל פעולה אחרת. לדוגמה, אם רק למשתמשי פרימיום יש אפשרות לחזור לפריט הקודם, יכול להיות שתציגו שגיאה אם משתמש בתוכנית חינמית יבקש מהעוזר הדיגיטלי לחזור לפריט הקודם. הנחיות נוספות מפורטות בקטע בנושא טיפול בשגיאות.

דוגמאות לשאילתות קוליות שאפשר לנסות

בטבלה הבאה מפורטות כמה שאילתות לדוגמה שמומלץ להשתמש בהן כשבודקים את ההטמעה:

קריאה חוזרת (callback) של MediaSession הביטוי שאומרים כדי להפעיל את Ok Google
onPlay()

‫Play (הפעלה).

‫"Resume".

onPlayFromSearch()
onPlayFromUri()
מוזיקה

‫"Play music or songs on (app name)". (הפעלת מוזיקה או שירים ב-(שם האפליקציה)). זו שאילתה ריקה.

‫"Play (song | artist | album | genre | playlist) on (app name)" (הפעלת (שיר | אומן | אלבום | ז'אנר | פלייליסט) ב-(שם האפליקציה))

רדיו ‫“Play (frequency | station) on (app name).”
ספר אודיו

‫"Read my audiobook on (שם האפליקציה)".

‫"Read (audiobook) on (app name)".

פודקאסטים ‫“Play (podcast) on (app name).”
onPause() ‫"Pause" (השהיה)
onStop() ‫"Stop".
onSkipToNext() ‫"(שיר | פרק | רצועה) הבא".
onSkipToPrevious() ‫"(שיר | פרק | רצועה) הקודם".
onSeekTo()

‫"Restart" (הפעלה מחדש).

‫"Skip ahead ## seconds" (דילוג ## שניות קדימה).

‫"Go back ## minutes" (חזרה אחורה ב-## דקות).

לא רלוונטי (צריך לעדכן את MediaMetadata) ‫"What’s playing?‎"

שגיאות

ה-Assistant מטפל בשגיאות מסשן מדיה כשהן מתרחשות, ומדווח עליהן למשתמשים. חשוב לוודא שעדכוני סשן המדיה מעדכנים את מצב ההעברה ואת קוד השגיאה ב-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 כמו שהם.
  • המשתמש מבקש תוכן שאין לו התאמה מדויקת. לדוגמה, משתמש בתוכנית חינמית מבקש תוכן שזמין רק למשתמשים בתוכנית פרימיום.
    • מומלץ לא להחזיר שגיאה, אלא לתת עדיפות למציאת תוכן דומה להפעלה. ה-Assistant יקריא את התשובה הקולית הכי רלוונטית לפני שההפעלה תתחיל.

הפעלה עם כוונה

ה-Assistant יכול להפעיל אפליקציית אודיו או וידאו ולהתחיל השמעה על ידי שליחת intent עם קישור עמוק.

הכוונה והקישור העמוק שלה יכולים להגיע ממקורות שונים:

  • כש-Assistant מתחילה אפליקציה לנייד, היא יכולה להשתמש בחיפוש Google כדי לאחזר תוכן עם תגי עיצוב שמספק פעולת צפייה עם קישור.
  • כשאסיסטנט מפעיל אפליקציית טלוויזיה, האפליקציה צריכה לכלול ספק חיפוש בטלוויזיה כדי לחשוף מזהי URI של תוכן מדיה. ‫Assistant שולחת שאילתה לספק התוכן, שאמור להחזיר כוונה שמכילה URI לקישור עומק ופעולה אופציונלית. אם השאילתה מחזירה פעולה ב-Intent,‏ Assistant שולחת את הפעולה ואת ה-URI בחזרה לאפליקציה. אם הספק לא ציין פעולה, Assistant תוסיף ACTION_VIEW ל-Intent.

ה-Assistant מוסיף את הנתונים הנוספים EXTRA_START_PLAYBACK עם הערך true ל-Intent שהוא שולח לאפליקציה. האפליקציה צריכה להתחיל בהפעלה כשהיא מקבלת Intent עם EXTRA_START_PLAYBACK.

טיפול בכוונות בזמן שהן פעילות

משתמשים יכולים לבקש מ-Assistant להפעיל משהו בזמן שהאפליקציה שלכם עדיין מפעילה תוכן מבקשה קודמת. המשמעות היא שהאפליקציה יכולה לקבל כוונות חדשות להתחלת הפעלה בזמן שפעילות ההפעלה שלה כבר הופעלה ופעילה.

הפעילויות שתומכות בכוונות עם קישורי עומק צריכות לבטל את ההגדרה onNewIntent() כדי לטפל בבקשות חדשות.

כשמתחילים בהפעלה, יכול להיות ש-Assistant תוסיף דגלים נוספים לכוונת הפעולה שהיא שולחת לאפליקציה. בפרט, יכול להיות שהיא תוסיף את FLAG_ACTIVITY_CLEAR_TOP או את FLAG_ACTIVITY_NEW_TASK או את שניהם. למרות שאין צורך שהקוד שלך יטפל בדגלים האלה, מערכת Android מגיבה להם. יכול להיות שהדבר ישפיע על התנהגות האפליקציה כשמגיעה בקשה שנייה להפעלה עם 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 של השירות. שירות הדפדפן למדיה לא אמור להפעיל פעילות. ‫Assistant תפעיל את הפעילות על סמך PendingIntent שתגדירו באמצעות setSessionActivity().

חשוב להגדיר את MediaSession.Token כשמפעילים את שירות דפדפן המדיה. חשוב לזכור להגדיר את פעולות ההפעלה הנתמכות בכל שלב, כולל במהלך האתחול. ‫Assistant מצפה שאפליקציית המדיה תגדיר את פעולות ההפעלה לפני ש-Assistant ישלח את פקודת ההפעלה הראשונה.

כדי להתחיל משירות, Assistant מטמיעה את ממשקי ה-API של לקוח דפדפן המדיה. הוא מבצע קריאות ל-TransportControls שמפעילות קריאות חוזרות (callback) של פעולת PLAY בסשן המדיה של האפליקציה.

בתרשים הבא מוצג סדר השיחות שנוצרות על ידי Assistant ופונקציות ה-callback המתאימות של סשן המדיה. (הקריאות החוזרות להכנה נשלחות רק אם האפליקציה תומכת בהן). כל השיחות הן אסינכרוניות. ‫Assistant לא מחכה לתגובה מהאפליקציה.

התחלת הפעלה באמצעות סשן מדיה

כשמשתמש נותן פקודה קולית להפעלה, Assistant מגיבה בהודעה קצרה. ברגע שההודעה מסתיימת, Assistant מפעילה פעולת PLAY. היא לא מחכה למצב הפעלה ספציפי.

אם האפליקציה תומכת בפעולות ACTION_PREPARE_*, ‏ Assistant קוראת לפעולה PREPARE לפני שהיא מתחילה את ההודעה.

התחברות ל-MediaBrowserService

כדי להשתמש בשירות להפעלת האפליקציה, ל-Assistant צריכה להיות אפשרות להתחבר אל MediaBrowserService של האפליקציה ולאחזר את MediaSession.Token שלה. בקשות לקישור מטופלות בשיטה onGetRoot() של השירות. יש שתי דרכים לטפל בבקשות:

  • אישור כל בקשות החיבור
  • אישור בקשות לחיבור רק מאפליקציית Assistant
.

אישור כל בקשות החיבור

כדי לאפשר ל-Assistant לשלוח פקודות לסשן המדיה, צריך להחזיר BrowserRoot. הדרך הכי קלה היא לאפשר לכל האפליקציות מסוג MediaBrowser להתחבר ל-MediaBrowserService. חובה להחזיר BrowserRoot שאינו null. זה הקוד הרלוונטי מ-Universal Music Player:

Kotlin

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...
}

Java

@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...
}

אישור חבילת ה-APK והחתימה של Assistant

אתם יכולים לאפשר ל-Assistant להתחבר לשירות של דפדפן המדיה באופן מפורש על ידי בדיקה של שם החבילה והחתימה שלו. שם החבילה של האפליקציה יתקבל בשיטה onGetRoot של MediaBrowserService. כדי לאפשר ל-Assistant לשלוח פקודות לסשן המדיה, צריך להחזיר BrowserRoot. הדוגמה Universal Music Player שומרת רשימה של שמות חבילות וחתימות ידועים. בהמשך מפורטים שמות החבילות והחתימות שבהם נעשה שימוש ב-Google Assistant.

<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>