בצע פקודות

דף זה מתאר כיצד למלא פקודות עם אינטראקציה קולית.

בצע פקודות מדיה

ניתן לפצל פקודה הקשורה למדיה לשלוש קבוצות שונות:

  • מקורות מדיה חיצוניים (כגון Spotify המותקן ב-AAOS).
  • מקורות מדיה עורפיים (כגון מוזיקה המוזרמת דרך ה-VIA).
  • מקורות מדיה מקומיים (כגון רדיו לרכב).

טפל בפקודות מקור מדיה חיצוניות

מקורות מדיה חיצוניים מוגדרים כאפליקציות אנדרואיד התומכות בממשקי API של MediaSessionCompat ו- MediaBrowseCompat (עיין בבניית אפליקציות מדיה למכוניות להסבר מפורט על השימוש בממשקי API אלה).

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

  1. להיות מותקן בחתימת מערכת (ראה הנחיות לפיתוח יישומי מדיה עבור AAOS וקוד PackageValidator לדוגמה).
  2. החזק את הרשאת 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 עבור פעולות הכוונה ACTION_PACKAGE_ADDED , ACTION_PACKAGE_CHANGED , ACTION_PACKAGE_REPLACED ו- ACTION_PACKAGE_REMOVED .

התחבר למקור המדיה המתנגן כעת

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

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)

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

ידית רדיו

ניתן לזהות את Radio MediaBrowseService על ידי מסנן הכוונות ACTION_PLAY_BROADCASTRADIO . הם צפויים לעקוב אחר בקרות ההשמעה ומבנה הגלישה במדיה המתוארים ברדיו Implement . AAOS מציעה את ספריית car-broadcastradio-support המכילה קבועים ושיטות כדי לעזור ליצרני OEM ליצור יישומי MediaBrowseService עבור שירותי הרדיו שלהם העוקבים אחר הפרוטוקול המוגדר, ומספקת תמיכה ליישומים שצורכים את עץ הגלישה שלהם (לדוגמה, VIA).

טפל בקלט עזר, שמע CD ומדיה USB

אין יישום ברירת מחדל של מקורות מדיה אלה כחלק מ-AOSP. הגישה המוצעת היא:

טיפול ב-Bluetooth

תוכן מדיה Bluetooth נחשף דרך פרופיל AVRCP Bluetooth. על מנת להקל על הגישה לפונקציונליות זו, AAOS כולל יישום MediaBrowserService ו-MediaSession שמוציא את פרטי התקשורת (ראה חבילות/אפליקציות/Bluetooth ).

מבנה העץ המתאים של דפדפן המדיה מוגדר בכיתה BrowseTree . ניתן להעביר פקודות בקרת השמעה בדומה לכל אפליקציה אחרת, על ידי שימוש ביישום MediaSession שלה.

טיפול בפקודות מדיה זורמת

כדי ליישם הזרמת מדיה בצד השרת, ה-VIA חייב להפוך בעצמו למקור מדיה, תוך הטמעת MediaBrowse ו-MediaSession API. עיין בבניית אפליקציות מדיה למכוניות . על ידי הטמעת ממשקי API אלה, אפליקציית שליטה קולית תוכל (בין היתר):

  • השתתף בצורה חלקה בבחירת מקור המדיה
  • תתחדש אוטומטית לאחר הפעלה מחדש של המכונית
  • ספק בקרת השמעה וגלישה באמצעות ממשק המשתמש של Media Center
  • קבל אירועי לחצני מדיה סטנדרטיים בחומרה

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

מילא פקודות רכב

גישה למאפייני הרכב הן לקריאה והן לכתיבה מסופקת דרך CarPropertyManager . סוגי מאפייני הרכב, היישום שלו ופרטים נוספים מוסברים בתצורות נכס . לתיאור מדויק של המאפיינים הנתמכים על ידי אנדרואיד, עדיף לפנות ישירות hardware/interfaces/automotive/vehicle/2.0/types.hal . הרשימה של VehicleProperty המוגדרת שם מכילה מאפיינים סטנדרטיים וספציפיים לספק, סוגי נתונים, מצב שינוי, יחידות והגדרת גישת קריאה/כתיבה.

כדי לגשת לאותם קבועים מג'אווה, אתה יכול להשתמש ב- 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);
    }

    ...
}

הגדר מאפיין רכב

להלן דוגמה המראה כיצד להפעיל ולכבות את ה-AC הקדמי.

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 חייב לטפל בהודעות נכנסות בעקבות זרימת "הקש לקריאה" המתוארת בעוזרת הקולית הקשה לקריאה , שיכולה לטפל באופן אופציונלי בשליחת תשובות חזרה לשולח ההודעה הנכנסת. בנוסף, VIAs יכולים להשתמש SmsManager (חלק מחבילת android.telephony ) כדי ליצור ולשלוח הודעות SMS ישירות מהמכונית או באמצעות Bluetooth.

טיפול בפקודות שיחה

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

בצע פקודות אחרות

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

פעולות סוחפות (הצגת תוכן ויזואלי)

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

כדי לאפשר התאמה אישית ועקביות עם שאר העיצוב של יחידת הראש (HU), VIAs צריכים להשתמש ברכיבי ספריית ממשק המשתמש של רכב עבור רוב רכיבי ממשק המשתמש. לפרטים, ראה התאמה אישית .