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 về nội dung nghe nhìn
Lệnh liên quan đến nội dung nghe nhìn có thể được chia thành 3 nhóm:
- Nguồn nội dung nghe nhìn bên ngoài (chẳng hạn như Spotify được cài đặt trong AAOS).
- Các nguồn nội dung nghe nhìn phụ trợ (chẳng hạn như nhạc phát trực tuyến qua VIA).
- Nguồn nội dung nghe nhìn tại địa phương (chẳng hạn như đài phát trên ô tô).
Xử lý các lệnh của nguồn nội dung nghe nhìn bên ngoài
Nguồn nội dung nghe nhìn bên ngoài được xác định là các ứng dụng Android hỗ trợ API MediaSessionCompat và MediaBrowseCompat (hãy tham khảo bài viết Tạo ứng dụng đa 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: Để kết nối với MediaBrowseService của tất cả ứng dụng đa phương tiện đã cài đặt trong hệ thống, ứng dụng trợ lý phải:
- Được cài đặt dưới dạng hệ thống đã ký (xem Nguyên tắc phát triển ứng dụng đa phương tiện cho AAOS và mã mẫu
PackageValidator). - Giữ quyền
android.permission.MEDIA_CONTENT_CONTROLcó đặc quyền hệ thống (xem phần Cấp quyền có đặc quyền hệ thống).
Ngoài MediaBrowserCompat và MediaControllerCompat, AAOS còn cung cấp những nội dung sau:
CarMediaServicecung cấp thông tin tập trung về nguồn nội dung nghe nhìn hiện được chọn. Đây cũng là cách để tiếp tục phát một nguồn nội dung nghe nhìn đang phát trước đó sau khi xe tắt rồi khởi động lại.car-media-commoncung cấp các phương thức thuận tiện để liệt kê, kết nối và tương tác với các ứng dụng nghe nhìn.
Dưới đây là các nguyên tắc dành riêng cho việc triển khai các lệnh tương tác bằng giọng nói phổ biến.
Lấy danh sách các nguồn nội dung nghe nhìn đã cài đặt
Bạn có thể phát hiện các nguồn nội dung nghe nhì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ố cách triển khai dịch vụ trình duyệt nội dung nghe nhìn đặc biệt mà bạn nên loại trừ. Sau đâ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 bạn có thể cài đặt hoặc gỡ cài đặt các nguồn nội dung nghe nhìn bất cứ lúc nào. Để duy trì danh sách chính xác, bạn nên triển khai một thực thể BroadcastReceiver cho các thao tác theo ý định ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REPLACED và ACTION_PACKAGE_REMOVED.
Kết nối với nguồn nội dung nghe nhìn đang phát
CarMediaService cung cấp các phương thức để lấy nguồn nội dung đa phương tiện hiện được chọn và khi nguồn nội dung đa 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 nội dung nghe nhìn nhất định. Sau đây là một đoạn mã đơn giản về cách kết nối với ứng dụng đa 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 } … }
Điều khiển chế độ phát của nguồn nội dung nghe nhìn hiện đang phát
Khi có MediaBrowserCompat đã kết nối, bạn có thể dễ dàng gửi lệnh điều khiển hoạt động truyền dữ liệu đến ứng dụng đích. Dưới đâ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 nội dung nghe nhìn cục bộ (đài phát, đầu đĩa CD, Bluetooth, USB)
Các nguồn nội dung nghe nhìn cục bộ cung cấp chức năng của chúng cho hệ thống bằng cách sử dụng cùng một MediaSession và MediaBrowse API như được trình bày chi tiết ở trên. Để đáp ứng những đặ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 về nội dung nghe nhìn.
Handle radio
Bạn có thể xác định Radio MediaBrowseService bằng bộ lọc ý định ACTION_PLAY_BROADCASTRADIO. Các ứng dụng này phải tuân theo cấu trúc duyệt xem nội dung nghe nhìn và các nút điều khiển chế độ phát được mô tả trong phần Triển khai đài phát. AAOS cung cấp thư viện car-broadcastradio-support chứa các hằng số và phương thức giúp các OEM tạo các phương thức triển khai MediaBrowseService cho dịch vụ đài phát của riêng họ theo giao thức đã xác định, đồng thời hỗ trợ các ứng dụng sử dụng cây duyệt qua của chúng (ví dụ: VIA).
Xử lý đầu vào phụ, âm thanh CD và nội dung nghe nhìn trên USB
Không có cách triển khai mặc định nào cho các nguồn nội dung nghe nhìn này trong AOSP. Phương pháp được đề xuất là:
- Yêu cầu các OEM triển khai dịch vụ đa phương tiện cho từng dịch vụ. Để biết thông tin chi tiết, hãy xem bài viết Tạo ứng dụng đa phương tiện cho ô tô.
- Những cách triển khai MediaBrowseService này sẽ được xác định và phản hồi trong các thao tác theo ý định được xác định tại Ý định chung để phát.
- Các dịch vụ này sẽ hiển thị một cây duyệt qua theo các nguyên tắc được mô tả tại Các loại nguồn khác.
Xử lý Bluetooth
Nội dung nghe nhìn qua Bluetooth được hiển thị thông qua hồ sơ Bluetooth AVRCP. Để tạo điều kiện truy cập vào chức năng này, AAOS có một phương thức triển khai MediaBrowserService và MediaSession giúp trừu tượng hoá thông tin chi tiết về giao tiếp (xem packages/apps/Bluetooth).
Cấu trúc cây trình duyệt nội dung đa phương tiện tương ứng được xác định tại lớp BrowseTree. Các lệnh điều khiển chế độ phát có thể được gửi tương tự như mọi ứng dụng khác, bằng cách sử dụng việc triển khai MediaSession của ứng dụng đó.
Xử lý các lệnh phát trực tuyến nội dung nghe nhìn
Để triển khai tính năng truyền trực tuyến nội dung nghe nhìn phía máy chủ, VIA phải tự trở thành một nguồn nội dung nghe nhìn, triển khai MediaBrowse và MediaSession API. Tham khảo bài viết Tạo ứng dụng đa phương tiện cho ô tô. Bằng cách triển khai các API này, một ứng dụng điều khiển bằng giọng nói có thể (trong số những việc khác):
- Tham gia liền mạch vào quá trình chọn nguồn nội dung đa phương tiện
- Tự động tiếp tục sau khi xe khởi động lại
- Cung cấp chế độ kiểm soát việc phát và duyệt xem bằng giao diện người dùng Media Center
- Nhận các sự kiện liên quan đến nút đa phương tiện trên phần cứng tiêu chuẩn
Thực hiện các lệnh điều hướng
Không có cách tương tác tiêu chuẩn với tất cả các ứng dụng chỉ đường. Để biết thông tin về việc tích hợp với Google Maps, hãy xem bài viết Ý định của Google Maps cho Android Automotive. Để 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 chạy một ý định cho bất kỳ ứng dụng nào (kể cả Google Maps), hãy xác minh rằng ý định đó có thể được phân giải (xem Ý định của Google Maps). Đ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 đích không có sẵn.
Thực hiện các lệnh về 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 xe, cách triển khai và các thông tin chi tiết khác được giải thích trong phần Cấu hình thuộc tính. Để có nội dung mô tả chính xác về các thuộc tính mà Android hỗ trợ, tốt nhất là 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ả các thuộc tính tiêu chuẩn và dành riêng cho nhà cung cấp, các loại dữ liệu, chế độ thay đổi, đơn vị và định nghĩa về quyền truy cập đọc/ghi.
Để 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à mối liên kết giữa các thuộc tính và quyền được mô tả trong Javadoc VehiclePropertyIds và được thực thi trong PropertyHalServiceIds.
Đọc một thuộc tính của xe
Sau đây là ví dụ minh hoạ cách đọc tốc độ 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 một thuộc tính của xe
Sau đây là ví dụ minh hoạ cách bật và tắt điều hoà không khí 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 quy trình "nhấn để đọc" được mô tả trong phần Trợ lý thoại Nhấn để đọc. VIA có thể xử lý việc gửi câu trả lời cho người gửi tin nhắn đến (không bắt buộc).
Ngoài ra, VIA có thể dùng SmsManager (một phần của gói android.telephony) để soạn và gửi tin nhắn SMS ngay từ ô tô hoặc qua Bluetooth.
Xử lý lệnh gọi
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 ngăn xếp điện thoại hoặc với ứng dụng Car Dialer. 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
Để xem 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 xem danh sách các ý định đã biết của Android. Nhiều lệnh của người dùng có thể được phân giải ở phía máy chủ (ví dụ: đọc email và sự kiện trên lịch của người dùng) và không yêu cầu bất kỳ hoạt động tương tác nào với hệ thống ngoài chính hoạt động tương tác bằng giọng nói.
Hành động sống động (hiển thị nội dung trực quan)
Khi nâng cao hành động hoặc mức độ hiểu biết của người dùng, VIA có thể cung cấp nội dung trực quan bổ sung trên màn hình ô tô. Để giảm thiểu tình trạng người lái xe mất tập trung, hãy giữ cho nội dung như vậy đơn giản, ngắn gọn và có thể hành động. Để biết thông tin chi tiết về các nguyên tắc về giao diện người dùng/trải nghiệm người dùng đối với các thao tác sống động, hãy xem phần Trợ lý được tải sẵn: Hướng dẫn về trải nghiệm người dùng.
Để cho phép tuỳ chỉnh và đảm bảo tính nhất quán với phần còn lại của thiết kế thiết bị trên ô tô (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 phần tử trên giao diện người dùng. Để biết thông tin chi tiết, hãy xem phần Tuỳ chỉnh.