בדף הזה מוסבר איך למלא פקודות באמצעות אינטראקציה קולית.
מילוי פקודות מדיה
אפשר לפצל פקודות שקשורות למדיה לשלוש קבוצות:
- מקורות מדיה חיצוניים (כמו Spotify שמותקן ב-AAOS).
- מקורות מדיה בקצה העורפי (למשל מוזיקה שמשודרת בסטרימינג דרך VIA).
- מקורות מדיה מקומיים (כמו רדיו ברכב).
טיפול בפקודות של מקורות מדיה חיצוניים
מקורות מדיה חיצוניים מוגדרים כאפליקציות ל-Android שתומכות ב-MediaSessionCompat
ו-MediaBrowseCompat
ממשקי API (למידע נוסף, תוכלו לקרוא על בניית אפליקציות מדיה עבור
Car כדי לקבל הסבר מפורט על השימוש בממשקי ה-API האלה.
חשוב: כדי שאפליקציית Assistant תוכל להתחבר אל
MediaBrowseService
מכל אפליקציות המדיה המותקנות ב-
היא חייבת:
- להיות מותקן כחתימת מערכת (יש לעיין בהנחיות לפיתוח אפליקציות מדיה עבור
AAOS וקוד
PackageValidator
לדוגמה). - עם
android.permission.MEDIA_CONTENT_CONTROL
הרשאות מערכת הרשאה (ראה הענקה הרשאות שמוגדרות על ידי המערכת).
בנוסף ל-MediaBrowserCompat
וגם MediaControllerCompat
,
AAOS מספק את הפרטים הבאים:
CarMediaService
מספקת מידע מרכזי על מקור המדיה הנוכחי שנבחר. הדבר משמש גם להמשך הפעלה של מקור מדיה שהופעל בעבר לאחר כיבוי הרכב מחדש.car-media-common
מספק שיטות נוחות לרשימה, לחיבור ולאינטראקציה עם אפליקציות מדיה.
בהמשך מפורטות הנחיות ספציפיות ליישום של אינטראקציה קולית נפוצה פקודות.
קבלת רשימה של מקורות מדיה מותקנים
ניתן לזהות מקורות מדיה באמצעות PackageManager
,
וסינון לפי שירותים שתואמים ל-MediaBrowserService.SERVICE_INTERFACE
.
במכוניות מסוימות עשויות להיות הטמעות מסוימות של שירותי דפדפן מדיה מיוחדים,
שצריך להחריג. הנה דוגמה ללוגיקה הזאת:
private Map<String, MediaSource> getAvailableMediaSources() { List<String> customMediaServices = Arrays.asList(mContext.getResources() .getStringArray(R.array.custom_media_packages)); List<ResolveInfo> mediaServices = mPackageManager.queryIntentServices( new Intent(MediaBrowserService.SERVICE_INTERFACE), PackageManager.GET_RESOLVED_FILTER); Map<String, MediaSource> result = new HashMap<>(); for (ResolveInfo info : mediaServices) { String packageName = info.serviceInfo.packageName; if (customMediaServices.contains(packageName)) { // Custom media sources should be ignored, as they might have a // specialized handling (e.g., radio). continue; } String className = info.serviceInfo.name; ComponentName componentName = new ComponentName(packageName, className); MediaSource source = MediaSource.create(mContext, componentName); result.put(source.getDisplayName().toString().toLowerCase(), source); } return result; }
חשוב לדעת שיכול להיות שמקורות מדיה מותקנים או יוסרו בכל שלב. לחשבון
כדי לשמור על רשימה מדויקת, מומלץ להטמיע BroadcastReceiver
מופע של פעולות Intent ACTION_PACKAGE_ADDED
,
ACTION_PACKAGE_CHANGED
,
ACTION_PACKAGE_REPLACED
,
וגם ACTION_PACKAGE_REMOVED
.
התחברות למקור המדיה שפועל עכשיו
CarMediaService
מספק שיטות לקבלת מקור המדיה הנוכחי שנבחר.
שינויים במקור. השינויים האלה עשויים להתרחש מאחר שהמשתמש יצר אינטראקציה עם
בממשק משתמש ישיר, או בגלל שימוש בלחצני חומרה ברכב. לעומת זאת,
הספרייה car-media-common מציעה דרכים נוחות להתחבר למדיה נתונה
מקור. הנה קטע טקסט פשוט שמסביר איך להתחבר לרשת הנוכחית שנבחרה
אפליקציית מדיה:
public class MediaActuator implements MediaBrowserConnector.onConnectedBrowserChanged { private final Car mCar; private CarMediaManager mCarMediaManager; private MediaBrowserConnector mBrowserConnector; … public void initialize(Context context) { mCar = Car.createCar(context); mBrowserConnector = new MediaBrowserConnector(context, this); mCarMediaManager = (CarMediaManager) mCar.getCarManager(Car.CAR_MEDIA_SERVICE); mBrowserConnector.connectTo(mCarMediaManager.getMediaSource()); … } @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { // TODO: Handle connected/disconnected browser } … }
שליטה בהפעלה של מקור המדיה שמופעל כרגע
עם MediaBrowserCompat
מחובר
קל לשלוח תחבורה
שליטה בפקודות לאפליקציית היעד. הנה דוגמה
דוגמה:
public class MediaActuator … { … private MediaControllerCompat mMediaController; @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { if (browser != null && browser.isConnected()) { mMediaController = new MediaControllerCompat(mContext, browser.getSessionToken()); } else { mMediaController = null; } } private boolean playSongOnCurrentSource(String song) { if (mMediaController == null) { // No source selected. return false; } MediaControllerCompat.TransportControls controls = mMediaController.getTransportControls(); PlaybackStateCompat state = controller.getPlaybackState(); if (state == null || ((state.getActions() & PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH) == 0)) { // Source can't play from search return false; } controls.playFromSearch(query, null); return true; } … }
טיפול בפקודות מקור של מדיה מקומית (רדיו, נגן CD, Bluetooth, USB)
מקורות מדיה מקומיים חושפים את הפונקציונליות שלהם למערכת באמצעות ממשקי ה-API של MediaSession ו-MediaBrowse כפי שמפורט למעלה. כדי להתאים את המאפיינים הספציפיים כל סוג של חומרה, שירותי MediaBrowse האלה משתמשים במוסכמות ספציפיות כדי לארגן המידע ופקודות המדיה שלהם.
ידית הרדיו
ניתן לזהות את Radio MediaBrowseService באמצעות ACTION_PLAY_BROADCASTRADIO
מסנן Intent. הם צפויים לפעול לפי פקדי ההפעלה ועיון במדיה
שמתואר במאמר הטמעת רדיו. AAOS מציעה
car-broadcastradio-support
הספרייה שמכילה קבועים ושיטות כדי לעזור ליצרני ציוד מקורי ליצור MediaBrowseService
בשירותי רדיו משלהם בהתאם לפרוטוקול שהוגדר,
ומספק תמיכה לאפליקציות שצורכות את עץ העיון שלהן (לדוגמה, VIA).
לטפל בקלט עזר, באודיו ב-CD ובמדיה בחיבור USB
אין הטמעת ברירת מחדל של מקורות המדיה האלה כחלק מ-AOSP. הגישה המוצעת היא:
- יצרני ציוד מקורי (OEM) יכולים להטמיע שירותי מדיה לכל אחד מהם. פרטים נוספים זמינים במאמר בניית אפליקציות מדיה לרכבים.
- ההטמעות האלה של MediaBrowseService יזוהו וייענו מתוך כוונה פעולות שמוגדרות בקטע General play Intents.
- השירותים האלה יחשפו עץ גלישה בהתאם להנחיות שמתוארות בקטע סוגי מקורות אחרים.
ידית Bluetooth
תוכן מדיה בחיבור Bluetooth נחשף דרך פרופיל AVRCP Bluetooth. לחשבון כדי להקל על הגישה לפונקציונליות הזו, AAOS כולל ההטמעה של MediaBrowserService ו-MediaSession שמפשטת את פרטי התקשורת (מידע נוסף זמין במאמר חבילות/אפליקציות/Bluetooth).
המבנה המתאים של עץ דפדפן המדיה מוגדר ב-BrowseTree בכיתה. אפשר להעביר פקודות של בקרת הפעלה כמו לכל אפליקציה אחרת, באמצעות ההטמעה של MediaSession.
טיפול בפקודות מדיה בסטרימינג
כדי להטמיע סטרימינג של מדיה בצד השרת, ה-VIA חייב להיות עצמו מקור מדיה, שמטמיע את MediaBrowse ו-MediaSession API. פרטים נוספים יצירת אפליקציות מדיה למכוניות. הטמעת ממשקי ה-API האלה תאפשר לאפליקציה לבקרה באמצעות הקול (בין היתר):
- להשתתף בצורה חלקה בבחירת מקורות המדיה
- המשך באופן אוטומטי אחרי הפעלה מחדש של הרכב
- לספק בקרת הפעלה וגלישה באמצעות ממשק המשתמש של Media Center
- קבלת אירועים של לחצני מדיה סטנדרטיים בחומרה
ביצוע פקודות ניווט
אין דרך סטנדרטית לקיים אינטראקציה עם כל אפליקציות הניווט. לגבי שילובים עם מפות Google, אפשר לעבור לקישור הבא: Google Maps for Android Automotive Intents לשילובים עם רשתות אחרות אפליקציות, לפנות ישירות למפתחי האפליקציות. לפני ההפעלה כוונה כלשהי לאפליקציה כלשהי (כולל מפות Google), לוודא שהכוונה יכולה להיות טופל (ראו Intent בקשות). כך נוצרת הזדמנות ליידע את המשתמש במקרה אפליקציית היעד לא זמינה.
מילוי פקודות הרכב
אפשר לגשת למאפייני הרכב לקריאה ולכתיבה דרך
CarPropertyManager.
נסביר על סוגי המאפיינים של הרכב, על ההטמעה שלהם ופרטים נוספים
בנכס
. לקבלת תיאור מדויק של המאפיינים הנתמכים
ב-Android, מומלץ להפנות ישירות אל hardware/interfaces/automotive/vehicle/2.0/types.hal
.
נכס הרכב
'טיפוסים בני מנייה (enum)' שמוגדרים שם מכילים גם מאפיינים סטנדרטיים וגם מאפיינים ספציפיים לספק, נתונים
סוגים, שינוי מצב, יחידות והגדרות של גישת קריאה/כתיבה.
כדי לגשת לאותם קבועים מ-Java, אפשר להשתמש ב-VehiclePropertyIds והמחלקות הנלוות שלו. לנכסים שונים יש הרשאות שונות ב-Android ששולטות בהם גישה. ההרשאות האלה מוצהרות ב-CarService מניפסט, והמיפוי בין נכסים והרשאות שמתוארים ב-VehiclePropertyIds Javadoc ואכיפה ב-PropertyHalServiceIds.
קריאת מאפיין רכב
בדוגמה הבאה אפשר לראות איך קוראים את מהירות הרכב:
public class CarActuator ... { private final Car mCar; private final CarPropertyManager mCarPropertyManager; private final TextToSpeech mTTS; /** Global VHAL area id */ public static final int GLOBAL_AREA_ID = 0; public CarActuator(Context context, TextToSpeech tts) { mCar = Car.createCar(context); mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE); mTTS = tts; ... } @Nullable private void getSpeedInMetersPerSecond() { if (!mCarPropertyManager.isPropertyAvailable(VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID)) { mTTS.speak("I'm sorry, but I can't read the speed of this vehicle"); return; } // Data type and unit can be found in // automotive/vehicle/2.0/types.hal float speedInMps = mCarPropertyManager.getFloatProperty( VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID); int speedInMph = (int)(speedInMetersPerSecond * 2.23694f); mTTS.speak(String.format("Sure. Your current speed is %d miles " + "per hour", speedInUserUnit); } ... }
הגדרת מאפיין רכב
בדוגמה הבאה אפשר לראות איך להפעיל ולכבות את המזגן הקדמי.
public class CarActuator … { … private void changeFrontAC(boolean turnOn) { List<CarPropertyConfig> configs = mCarPropertyManager .getPropertyList(new ArraySet<>(Arrays.asList( VehiclePropertyIds.HVAC_AC_ON))); if (configs == null || configs.size() != 1) { mTTS.speak("I'm sorry, but I can't control the AC of your vehicle"); return; } // Find the front area Ids for the AC property. int[] areaIds = configs.get(0).getAreaIds(); List<Integer> areasToChange = new ArrayList<>(); for (int areaId : areaIds) { if ((areaId & (VehicleAreaSeat.SEAT_ROW_1_CENTER | VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_RIGHT)) == 0) { continue; } boolean isACInAreaAlreadyOn = mCarPropertyManager .getBooleanProperty(VehiclePropertyIds.HVAC_AC_ON, areaId); if ((!isACInAreaAlreadyOn && turnOn) || (isACInAreaAlreadyOn && !turnOn)) { areasToChange.add(areaId); } } if (areasToChange.isEmpty()) { mTTS.speak(String.format("The AC is already %s", turnOn ? "on" : "off")); return; } for (int areaId : areasToChange) { mCarPropertyManager.setBooleanProperty( VehiclePropertyIds.HVAC_AC_ON, areaId, turnOn); } mTTS.speak(String.format("Okay, I'm turning your front AC %s", turnOn ? "on" : "off")); } … }
מילוי פקודות תקשורת
טיפול בפקודות בהעברת הודעות
מודעות VIA חייבות לטפל בהודעות נכנסות שמגיעות אחרי שמקישים על 'הקשה לקריאה' תהליך מתואר
בעוזר הדיגיטלי
'הקשה לקריאה', שיכולה לטפל בשליחת תשובות חזרה לשולח ההודעה הנכנסת.
בנוסף, נכסי VIA יכולים להשתמש ב-SmsManager
(חלק מ-android.telephony
חבילה) כדי לכתוב ולשלוח הודעות SMS ישירות מהמכונית או באמצעות Bluetooth.
טיפול בפקודות השיחה
באופן דומה, מודעות VIA יכולות להשתמש ב-TelephonyManager
כדי לבצע שיחות טלפון ולהתקשר למספר הדואר הקולי של המשתמש. במקרים כאלה,
מודעות VIA יפעלו עם מקבץ הטלפוניה ישירות או עם החייגן ברכב
אפליקציה. בכל מקרה, אפליקציית החייגן לרכב צריכה להיות מוצגת
לממשק משתמש שקשור לשיחות קוליות.
ביצוע פקודות אחרות
לרשימה של נקודות שילוב אפשריות אחרות בין VIA כדאי לעיין ברשימת האובייקטים מסוג Intent ב-Android המוכרים. הרבה ניתן לפענח פקודות משתמש בצד השרת (לדוגמה, הודעות אימייל ואירועים ביומן של משתמשים) ולא מצריכות אינטראקציה עם המערכת מלבד האינטראקציה הקולית עצמה.
פעולות אימרסיביות (הצגת תוכן חזותי)
כאשר הדבר משפר את פעולות המשתמש או את הבנתו, VIA יכול לספק תוכן חזותי נוסף במסך הרכב. כדי לצמצם את הסחות הדעת של הנהג, לספק תוכן כזה פשוט, קצר ומעשי. פרטים על ההנחיות לגבי ממשק המשתמש או חוויית המשתמש בפעולות של צפייה היקפית, עוזרים שנטענו מראש: הדרכה לגבי חוויית המשתמש.
כדי לאפשר התאמה אישית ועקביות עם שאר עיצוב היחידה הראשית (HU), יש להשתמש ב-VIA מכונית רכיבי ספריית ממשק המשתמש עבור רוב הרכיבים של ממשק המשתמש. פרטים נוספים זמינים במאמר התאמה אישית.