ดำเนินการตามคําสั่ง

หน้านี้จะอธิบายวิธีดำเนินการตามคำสั่งด้วยการโต้ตอบด้วยเสียง

ดำเนินการตามคําสั่งของสื่อ

คำสั่งที่เกี่ยวข้องกับสื่อสามารถแบ่งออกเป็น 3 กลุ่มดังนี้

  • แหล่งที่มาของสื่อภายนอก (เช่น Spotify ที่ติดตั้งใน AAOS)
  • แหล่งที่มาของสื่อแบ็กเอนด์ (เช่น เพลงที่สตรีมผ่าน VIA)
  • แหล่งที่มาของสื่อท้องถิ่น (เช่น วิทยุรถยนต์)

จัดการคําสั่งของแหล่งที่มาของสื่อภายนอก

แหล่งที่มาของสื่อภายนอกหมายถึงแอป Android ที่รองรับ MediaSessionCompat และ MediaBrowseCompat API (ดูคำอธิบายโดยละเอียดเกี่ยวกับการใช้ API เหล่านี้ได้ที่สร้างแอปสื่อสำหรับรถยนต์)

สำคัญ: แอปผู้ช่วยต้องมีคุณสมบัติต่อไปนี้จึงจะเชื่อมต่อกับMediaBrowseService ของแอปสื่อที่ติดตั้งทั้งหมดในระบบได้

  1. ติดตั้งเป็นโค้ดที่ระบบรับรอง (ดูหลักเกณฑ์การพัฒนาแอปพลิเคชันสื่อสำหรับ AAOS และโค้ด PackageValidator ตัวอย่าง)
  2. มีสิทธิ์ระดับandroid.permission.MEDIA_CONTENT_CONTROLของระบบ (ดูให้สิทธิ์ระดับระบบ)

นอกจาก MediaBrowserCompat และ MediaControllerCompat แล้ว AOAS ยังมีบริการต่อไปนี้

  • 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 มีวิธีการรับแหล่งที่มาของสื่อที่เลือกอยู่ในปัจจุบัน และเมื่อมีการเปลี่ยนแปลงแหล่งที่มาของสื่อนี้ การเปลี่ยนแปลงเหล่านี้อาจเกิดขึ้นเนื่องจากผู้ใช้โต้ตอบกับ UI โดยตรง หรือใช้ปุ่มฮาร์ดแวร์ในรถ ในทางกลับกัน ไลบรารี 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;
    }

    
}

จัดการคำสั่งแหล่งที่มาของสื่อภายใน (วิทยุ เครื่องเล่นซีดี บลูทูธ USB)

แหล่งที่มาของสื่อในเครื่องจะแสดงฟังก์ชันการทำงานต่อระบบโดยใช้ MediaSession และ MediaBrowse API เดียวกันตามที่อธิบายไว้ข้างต้น บริการ MediaBrowse เหล่านี้ใช้รูปแบบเฉพาะในการจัดระเบียบข้อมูลและคำสั่งสื่อเพื่อให้รองรับลักษณะเฉพาะของฮาร์ดแวร์แต่ละประเภท

จัดการวิทยุ

คุณสามารถระบุ Radio MediaBrowseService ได้ด้วยตัวกรอง Intent ACTION_PLAY_BROADCASTRADIO โดยจะต้องเป็นไปตามโครงสร้างการควบคุมการเล่นและการเรียกดูสื่อที่อธิบายไว้ในใช้งานวิทยุ AAOS มีห้องสมุด car-broadcastradio-support ที่มีค่าคงที่และเมธอดเพื่อช่วย OEM สร้างการใช้งาน MediaBrowseService สำหรับบริการวิทยุของตนเองซึ่งเป็นไปตามโปรโตคอลที่กําหนด และรองรับแอปที่ใช้ทรีการเรียกดู (เช่น VIA)

จัดการอินพุตเสริม เสียงจาก CD และสื่อ USB

AOSP ไม่มีการใช้แหล่งที่มาของสื่อเหล่านี้โดยค่าเริ่มต้น แนวทางที่แนะนำคือ

จัดการบลูทูธ

เนื้อหาสื่อบลูทูธจะแสดงผ่านโปรไฟล์บลูทูธ AVRCP AAOS มีการใช้งาน MediaBrowserService และ MediaSession ที่จะแยกรายละเอียดการสื่อสารออก (ดูที่ packages/apps/Bluetooth) เพื่อให้เข้าถึงฟังก์ชันนี้ได้สะดวก

โครงสร้างต้นไม้ของเบราว์เซอร์สื่อที่เกี่ยวข้องจะกำหนดไว้ในคลาส BrowseTree คำสั่งการควบคุมการเล่นสามารถส่งได้เช่นเดียวกับแอปอื่นๆ โดยใช้การใช้งาน MediaSession

จัดการคำสั่งการสตรีมสื่อ

หากต้องการใช้การสตรีมสื่อฝั่งเซิร์ฟเวอร์ VIA จะต้องเป็นแหล่งที่มาของสื่อด้วย โดยติดตั้งใช้งาน MediaBrowse และ MediaSession API โปรดดูหัวข้อสร้างแอปสื่อสำหรับรถยนต์ การใช้ API เหล่านี้จะทำให้แอปควบคุมด้วยเสียงสามารถดำเนินการต่อไปนี้ได้ (และอื่นๆ)

  • เข้าร่วมการเลือกแหล่งที่มาของสื่อได้อย่างราบรื่น
  • กลับมาทำงานอีกครั้งโดยอัตโนมัติหลังจากรถรีสตาร์ท
  • ควบคุมการเล่นและการเรียกดูโดยใช้ UI ของ Media Center
  • รับเหตุการณ์ปุ่มสื่อของฮาร์ดแวร์มาตรฐาน

ไม่มีการโต้ตอบกับแอปการนำทางทั้งหมดที่เป็นมาตรฐาน สําหรับการผสานรวมกับ Google Maps โปรดดูGoogle Maps สําหรับ Intent ของ Android Automotive หากต้องการผสานรวมกับแอปอื่นๆ โปรดติดต่อนักพัฒนาแอปโดยตรง ก่อนเปิดใช้งาน Intent กับแอปใดก็ตาม (รวมถึง Google Maps) ให้ตรวจสอบว่า Intent นั้นสามารถแก้ไขได้ (ดูคำขอ Intent) ซึ่งจะเปิดโอกาสให้แจ้งให้ผู้ใช้ทราบในกรณีที่แอปเป้าหมายไม่พร้อมให้บริการ

ดำเนินการตามคำสั่งของยานพาหนะ

สิทธิ์เข้าถึงพร็อพเพอร์ตี้ยานพาหนะทั้งการอ่านและการเขียนมีให้ผ่าน CarPropertyManager ประเภทที่พัก การใช้งาน และรายละเอียดอื่นๆ ของที่พักจะอธิบายไว้ในการกำหนดค่าที่พัก หากต้องการคำอธิบายที่ถูกต้องเกี่ยวกับพร็อพเพอร์ตี้ที่ Android รองรับ คุณควรดูที่ hardware/interfaces/automotive/vehicle/2.0/types.hal โดยตรง รายการ VehicleProperty ที่กําหนดไว้มีทั้งพร็อพเพอร์ตี้มาตรฐานและพร็อพเพอร์ตี้เฉพาะของผู้ให้บริการ ประเภทข้อมูล โหมดการเปลี่ยนแปลง หน่วย และคำจำกัดความการเข้าถึงแบบอ่าน/เขียน

หากต้องการเข้าถึงค่าคงที่เดียวกันเหล่านี้จาก Java คุณสามารถใช้ VehiclePropertyIds และคลาสที่เกี่ยวข้อง พร็อพเพอร์ตี้ต่างๆ มีสิทธิ์เข้าถึง Android ที่ควบคุมการเข้าถึงที่แตกต่างกัน สิทธิ์เหล่านี้จะประกาศใน CarService manifest และการแมประหว่างพร็อพเพอร์ตี้และสิทธิ์ที่อธิบายไว้จะอยู่ใน 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 ต้องจัดการข้อความขาเข้าตามขั้นตอน "แตะเพื่ออ่าน" ที่อธิบายไว้ในฟีเจอร์แตะเพื่ออ่านของ Voice Assistant ซึ่งสามารถจัดการการส่งการตอบกลับกลับไปยังผู้ส่งข้อความขาเข้าได้ (ไม่บังคับ) นอกจากนี้ VIA ยังใช้ SmsManager (เป็นส่วนหนึ่งของแพ็กเกจ android.telephony ) เพื่อเขียนและส่งข้อความ SMS จากรถยนต์หรือผ่านบลูทูธได้โดยตรง

จัดการคำสั่งการโทร

ในทำนองเดียวกัน VIA สามารถใช้ TelephonyManager เพื่อโทรออกและโทรไปยังหมายเลขข้อความเสียงของผู้ใช้ ในกรณีเหล่านี้ VIA จะโต้ตอบกับสแต็กโทรศัพท์โดยตรงหรือกับแอปตัวต่อสายในรถ ไม่ว่าในกรณีใด แอปตัวต่อสายในรถควรเป็นผู้แสดง UI ที่เกี่ยวข้องกับการโทรด้วยเสียงต่อผู้ใช้

ดำเนินการตามคําสั่งอื่นๆ

ดูรายการจุดอื่นๆ ที่เป็นไปได้ในการผสานรวมระหว่าง VIA กับระบบได้จากรายการ Intent ของ Android ที่รู้จักกันดี คำสั่งของผู้ใช้จำนวนมากสามารถแก้ไขได้ฝั่งเซิร์ฟเวอร์ (เช่น การอ่านอีเมลและกิจกรรมในปฏิทินของผู้ใช้) และไม่จำเป็นต้องมีการโต้ตอบกับระบบนอกเหนือจากการโต้ตอบด้วยเสียง

การกระทําที่สมจริง (แสดงเนื้อหาภาพ)

VIA สามารถแสดงเนื้อหาภาพเสริมบนหน้าจอของรถยนต์ได้ในกรณีที่ช่วยเพิ่มการดําเนินการหรือความเข้าใจของผู้ใช้ เนื้อหาดังกล่าวควรเข้าใจง่าย กระชับ และนำไปใช้ได้จริง เพื่อลดสิ่งรบกวนผู้ขับขี่ โปรดดูรายละเอียดเกี่ยวกับหลักเกณฑ์ UI/UX ในการดําเนินการแบบสมจริงที่หัวข้อAssistant ที่โหลดไว้ล่วงหน้า: หลักเกณฑ์ UX

VIA ควรใช้คอมโพเนนต์ไลบรารี UI ของรถยนต์สำหรับองค์ประกอบ UI ส่วนใหญ่เพื่อให้ปรับแต่งได้และสอดคล้องกับการออกแบบส่วนที่เหลือของเครื่องเล่นวิทยุ (HU) โปรดดูรายละเอียดที่หัวข้อการปรับแต่ง