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

Google Assistant מאפשרת להשתמש בפקודות קוליות כדי לשלוט במכשירים רבים, כמו Google Home, הטלפון שלך ועוד. יש בו יכולת מובנית להבין פקודות מדיה ('play something של Beyoncé') בקרי מדיה (כמו השהיה, דילוג, הרצה קדימה, לייק).

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

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

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

חשוב לשים לב שלמרות ש-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()

נגן מוזיקה אוניברסלי ל-Android פרויקט לדוגמה הוא דוגמה טובה להגדרה של סשן מדיה.

פעולות הפעלה

כדי להתחיל הפעלה משירות, סשן המדיה חייב לכלול את PLAY הפעולות הבאות והקריאות החוזרות (callback) שלהן:

פעולה התקשרות חזרה
ACTION_PLAY onPlay()
ACTION_PLAY_FROM_SEARCH onPlayFromSearch()
ACTION_PLAY_FROM_URI (*) onPlayFromUri()

בנוסף, צריך להטמיע בסשן גם את הפעולות הבאות של PREPARE ואת הקריאות החוזרות (callback) שלהן:

פעולה התקשרות חזרה
ACTION_PREPARE onPrepare()
ACTION_PREPARE_FROM_SEARCH onPrepareFromSearch()
ACTION_PREPARE_FROM_URI (*) onPrepareFromUri()

(*) פעולות שמבוססות על ה-URI של Google Assistant פועלות רק עבור חברות שמספקים ל-Google מזהי URI. מידע נוסף על תיאור תוכן המדיה שלך ל-Google ראה פעולות במדיה.

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

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

כשמשתמש מחפש פריט מדיה ספציפי, למשל "הפעלת מוזיקת ג'אז מופעלת" [שם האפליקציה שלכם] או "האזנה ל-[שם השיר]", onPrepareFromSearch() או onPlayFromSearch() שיטת הקריאה החוזרת מקבלת פרמטר של שאילתה וחבילת תוספות.

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

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

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

התוספות הבאות נתמכות ב-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 לדוגמה.

טיפול בשאילתות ריקות

אם onPrepare(), onPlay(), onPrepareFromSearch() או onPlayFromSearch() מופעלות ללא שאילתת חיפוש, אפליקציית המדיה אמורה להפעיל את הקובץ "הנוכחי" מדיה. אם אין מדיה עדכנית, האפליקציה צריכה לנסות להפעיל משהו, כמו כששיר מהפלייליסט האחרון או תור אקראי. Assistant משתמשת ממשקי ה-API האלה כשהמשתמש מבקש "Play music on [your app name]" בלי מידע נוסף.

כשמשתמש אומר “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.

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

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

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

מומלץ מאוד על תמיכה עבור:

פודקאסט

נדרשת תמיכה: הפעלה, השהיה, עצירה ודילוג

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

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

נדרשת תמיכה: הפעלה, השהיה, עצירה, דילוג אל, הרצה אחורה והרצה קדימה

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

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

שאילתות קוליות לדוגמה שכדאי לנסות

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

התקשרות חזרה של MediaSession הביטוי Ok Google שבו רוצים להשתמש
onPlay()

"Play"

"Resume"

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

"Play music or music on (app name)" זו שאילתה ריקה.

“Play (שיר | אומן | אלבום | ז'אנר | פלייליסט) ב-(app name)."

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

"Read my audio on (app name). "

"Read (audiobook) on (app name) "

פודקאסטים “Play (podcast) on (app name)”.
onPause() "Pause"
onStop() "Stop"
onSkipToNext() “Next (song | episode | track)
onSkipToPrevious() “Previous (song | episode | track)" .
onSeekTo()

"הפעלה מחדש"

"דילוג קדימה ## שניות".

"Go back ## דקות".

לא רלוונטי (יש להשאיר 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 יכול להפעיל אפליקציית אודיו או וידאו ולהתחיל את ההפעלה על ידי שליחת עם קישור עומק.

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

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

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

טיפול בכוונות בזמן אמת

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

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

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

התחלת ההפעלה עם סשן מדיה

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

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

התחברות ל-MediaBrowserService

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

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

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

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

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

אישור החבילה והחתימה של אפליקציית 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>