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

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

ดำเนินการตามคำสั่งเกี่ยวกับสื่อ

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

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

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

แหล่งที่มาของสื่อภายนอกกำหนดไว้เป็นแอป Android ที่รองรับ 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สำหรับ การดำเนินการ 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)

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

จัดการวิทยุ

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

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

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

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

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

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

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

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

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

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

ดำเนินการตามคำสั่งเกี่ยวกับรถ

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

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

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

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

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

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

การดำเนินการแบบเต็มหน้าจอ (แสดงเนื้อหาภาพ)

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

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