Thực hiện các lệnh

Trang này mô tả cách thực hiện các lệnh bằng tương tác bằng giọng nói.

Thực hiện các lệnh truyền thông

Lệnh liên quan đến phương tiện có thể được chia thành ba nhóm khác nhau:

  • Các nguồn phương tiện bên ngoài (chẳng hạn như Spotify được cài đặt trong AAOS).
  • Các nguồn phương tiện phụ trợ (chẳng hạn như nhạc được truyền qua VIA).
  • Các nguồn phương tiện truyền thông địa phương (chẳng hạn như đài phát thanh xe hơi).

Xử lý các lệnh nguồn phương tiện bên ngoài

Các nguồn phương tiện bên ngoài được định nghĩa là các ứng dụng Android hỗ trợ API MediaSessionCompatMediaBrowseCompat (tham khảo Xây dựng ứng dụng phương tiện cho ô tô để biết giải thích chi tiết về cách sử dụng các API này).

Quan trọng: Để một ứng dụng trợ lý kết nối với MediaBrowseService của tất cả các ứng dụng đa phương tiện đã cài đặt trong hệ thống, ứng dụng đó phải:

  1. Được cài đặt dưới dạng có chữ ký hệ thống (xem hướng dẫn Phát triển ứng dụng phương tiện cho AAOS và mã PackageValidator mẫu).
  2. Giữ quyền đặc quyền của hệ thống android.permission.MEDIA_CONTENT_CONTROL (xem Cấp quyền đặc quyền của hệ thống ).

Ngoài MediaBrowserCompatMediaControllerCompat , AAOS còn cung cấp những tính năng sau:

  • CarMediaService cung cấp thông tin tập trung về nguồn phương tiện hiện được chọn. Điều này cũng được sử dụng để tiếp tục nguồn phương tiện đang phát trước đó sau khi tắt máy-khởi động lại ô tô.
  • car-media-common cung cấp các phương pháp thuận tiện để liệt kê, kết nối và tương tác với các ứng dụng đa phương tiện.

Dưới đây là các hướng dẫn cụ thể để thực hiện các lệnh tương tác bằng giọng nói phổ biến.

Nhận danh sách các nguồn phương tiện được cài đặt

Có thể phát hiện các nguồn phương tiện bằng cách sử dụng PackageManager và lọc các dịch vụ khớp với MediaBrowserService.SERVICE_INTERFACE . Trong một số ô tô có thể có một số triển khai dịch vụ trình duyệt phương tiện đặc biệt cần được loại trừ. Đây là một ví dụ về logic này:

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

Xin lưu ý rằng các nguồn phương tiện có thể được cài đặt hoặc gỡ cài đặt bất kỳ lúc nào. Để duy trì danh sách chính xác, bạn nên triển khai một phiên bản BroadcastReceiver cho các hành động có ý định ACTION_PACKAGE_ADDED , ACTION_PACKAGE_CHANGED , ACTION_PACKAGE_REPLACEDACTION_PACKAGE_REMOVED .

Kết nối với nguồn phương tiện hiện đang phát

CarMediaService cung cấp các phương thức để lấy nguồn phương tiện hiện được chọn và khi nguồn phương tiện này thay đổi. Những thay đổi này có thể xảy ra do người dùng tương tác trực tiếp với giao diện người dùng hoặc do sử dụng các nút phần cứng trên ô tô. Mặt khác, thư viện car-media-common cung cấp những cách thuận tiện để kết nối với một nguồn phương tiện nhất định. Dưới đây là đoạn mã đơn giản về cách kết nối với ứng dụng phương tiện hiện được chọn:

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
    }

    …
}

Kiểm soát phát lại nguồn phương tiện hiện đang phát

Với MediaBrowserCompat được kết nối, thật dễ dàng gửi các lệnh điều khiển truyền tải đến ứng dụng đích. Đây là một ví dụ đơn giản:

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

    …
}

Xử lý các lệnh nguồn phương tiện cục bộ (radio, đầu CD, Bluetooth, USB)

Các nguồn truyền thông địa phương hiển thị chức năng của họ cho hệ thống bằng cách sử dụng cùng các API MediaSession và MediaBrowse được nêu chi tiết ở trên. Để phù hợp với đặc điểm riêng của từng loại phần cứng, các dịch vụ MediaBrowse này sử dụng các quy ước cụ thể để sắp xếp thông tin và lệnh phương tiện của chúng.

Xử lý đài phát thanh

Radio MediaBrowseService có thể được xác định bằng bộ lọc ý định ACTION_PLAY_BROADCASTRADIO . Họ phải tuân theo các điều khiển phát lại và cấu trúc duyệt phương tiện được mô tả trong Thực hiện radio . AAOS cung cấp thư viện car-broadcastradio-support chứa các hằng số và phương thức để giúp OEM tạo triển khai MediaBrowseService cho các dịch vụ vô tuyến của riêng họ tuân theo giao thức đã xác định và cung cấp hỗ trợ cho các ứng dụng sử dụng cây duyệt của họ (ví dụ: VIA).

Xử lý đầu vào phụ, âm thanh CD và phương tiện USB

Không có triển khai mặc định nào cho các nguồn phương tiện này như một phần của AOSP. Cách tiếp cận được đề xuất là:

Xử lý Bluetooth

Nội dung phương tiện Bluetooth được hiển thị thông qua cấu hình Bluetooth AVRCP. Để tạo điều kiện truy cập vào chức năng này, AAOS bao gồm triển khai MediaBrowserService và MediaSession để tóm tắt các chi tiết liên lạc (xem gói/ứng dụng/Bluetooth ).

Cấu trúc cây trình duyệt phương tiện tương ứng được xác định tại lớp BrowserTree . Các lệnh điều khiển phát lại có thể được phân phối tương tự như bất kỳ ứng dụng nào khác bằng cách sử dụng triển khai MediaSession của ứng dụng đó.

Xử lý các lệnh truyền phát phương tiện

Để triển khai truyền phát phương tiện phía máy chủ, VIA phải trở thành nguồn phương tiện, triển khai API MediaBrowse và MediaSession. Tham khảo Xây dựng ứng dụng đa phương tiện cho ô tô . Bằng cách triển khai các API này, ứng dụng điều khiển bằng giọng nói sẽ có thể (trong số những thứ khác):

  • Tham gia liền mạch vào việc lựa chọn nguồn phương tiện
  • Được tự động tiếp tục sau khi khởi động lại xe
  • Cung cấp khả năng kiểm soát phát lại và duyệt web bằng giao diện người dùng Media Center
  • Nhận các sự kiện nút phương tiện phần cứng tiêu chuẩn

Không có cách tương tác chuẩn hóa với tất cả các ứng dụng điều hướng. Để tích hợp với Google Maps, hãy xem Google Maps dành cho Android Automotive Intents . Để tích hợp với các ứng dụng khác, hãy liên hệ trực tiếp với nhà phát triển ứng dụng. Trước khi triển khai ý định cho bất kỳ ứng dụng nào (bao gồm cả Google Maps), hãy xác minh rằng ý định đó có thể được giải quyết (xem Yêu cầu ý định ). Điều này tạo cơ hội thông báo cho người dùng trong trường hợp ứng dụng mục tiêu không có sẵn.

Thực hiện các lệnh của xe

Quyền truy cập vào các thuộc tính của xe để đọc và ghi được cung cấp thông qua CarPropertyManager . Các loại thuộc tính phương tiện, cách triển khai và các chi tiết khác được giải thích trong Cấu hình thuộc tính . Để có mô tả chính xác về các thuộc tính được Android hỗ trợ, tốt nhất bạn nên tham khảo trực tiếp hardware/interfaces/automotive/vehicle/2.0/types.hal . Enum VehicleProperty được xác định ở đó chứa cả thuộc tính, kiểu dữ liệu, chế độ thay đổi, đơn vị và định nghĩa truy cập đọc/ghi cụ thể của nhà cung cấp và tiêu chuẩn.

Để truy cập các hằng số tương tự này từ Java, bạn có thể sử dụng VehiclePropertyIds và các lớp đồng hành của nó. Các thuộc tính khác nhau có các quyền Android khác nhau kiểm soát quyền truy cập của chúng. Các quyền này được khai báo trong tệp kê khai CarService và ánh xạ giữa các thuộc tính và quyền được mô tả trong VehiclePropertyIds Javadoc và được thực thi trong PropertyHalServiceIds .

Đọc thuộc tính xe

Sau đây là ví dụ minh họa cách đọc tốc độ của xe:

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);
    }

    ...
}

Đặt thuộc tính xe

Sau đây là ví dụ minh họa cách bật và tắt điều hòa phía trước.

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"));
    }

    …
}

Thực hiện các lệnh giao tiếp

Xử lý các lệnh nhắn tin

VIA phải xử lý các tin nhắn đến theo luồng "chạm để đọc" được mô tả trong Trợ lý giọng nói Chạm để đọc , luồng này có thể tùy ý xử lý việc gửi trả lời lại cho người gửi tin nhắn đến. Ngoài ra, VIA có thể sử dụng SmsManager (một phần của gói android.telephony ) để soạn và gửi tin nhắn SMS trực tiếp từ ô tô hoặc qua Bluetooth.

Xử lý lệnh gọi

Theo cách tương tự, VIA có thể sử dụng TelephonyManager để thực hiện cuộc gọi điện thoại và gọi đến số thư thoại của người dùng. Trong những trường hợp này, VIA sẽ tương tác trực tiếp với hệ thống điện thoại hoặc với ứng dụng Trình quay số ô tô. Trong mọi trường hợp, ứng dụng Car Dialer phải là ứng dụng hiển thị giao diện người dùng liên quan đến cuộc gọi thoại cho người dùng.

Thực hiện các lệnh khác

Để biết danh sách các điểm tích hợp có thể có khác giữa VIA và hệ thống, hãy kiểm tra danh sách các ý định phổ biến của Android . Nhiều lệnh của người dùng có thể được giải quyết phía máy chủ (ví dụ: đọc email và sự kiện lịch của người dùng) và không yêu cầu bất kỳ tương tác nào với hệ thống ngoài chính tương tác bằng giọng nói.

Hành động nhập vai (hiển thị nội dung trực quan)

Khi nâng cao hành động hoặc hiểu biết của người dùng, VIA có thể cung cấp nội dung hình ảnh bổ sung trên màn hình ô tô. Để giảm thiểu sự mất tập trung của người lái xe, hãy giữ nội dung đó đơn giản, ngắn gọn và có thể hành động được. Để biết chi tiết về nguyên tắc UI/UX về các hành động sống động, hãy xem Trợ lý được tải trước: Hướng dẫn UX .

Để cho phép tùy chỉnh và nhất quán với phần còn lại của thiết kế bộ phận chính (HU), VIA nên sử dụng các thành phần Thư viện giao diện người dùng ô tô cho hầu hết các thành phần giao diện người dùng. Để biết chi tiết, hãy xem Tùy chỉnh .