Memenuhi perintah

Halaman ini menjelaskan cara memenuhi perintah dengan interaksi suara.

Memenuhi perintah media

Perintah terkait media dapat dibagi menjadi tiga grup yang berbeda:

  • Sumber media eksternal (seperti Spotify yang diinstal di AAOS).
  • Sumber media backend (seperti musik yang di-streaming melalui VIA).
  • Sumber media lokal (seperti radio mobil).

Menangani perintah sumber media eksternal

Sumber media eksternal ditentukan sebagai aplikasi Android yang mendukung API MediaSessionCompat dan MediaBrowseCompat (lihat Mem-build aplikasi media untuk mobil untuk mendapatkan penjelasan mendetail tentang penggunaan API ini).

Penting: Agar dapat terhubung ke MediaBrowseService dari semua aplikasi media yang diinstal di sistem, aplikasi asisten harus:

  1. Diinstal sebagai ditandatangani sistem (lihat panduan Pengembangan Aplikasi Media untuk AAOS dan contoh kode PackageValidator).
  2. Memiliki izin hak istimewa sistem android.permission.MEDIA_CONTENT_CONTROL (lihat Memberikan izin hak istimewa sistem).

Selain MediaBrowserCompat dan MediaControllerCompat, AAOS menyediakan hal berikut:

  • CarMediaService memberikan informasi terpusat tentang sumber media yang saat ini dipilih. Hal ini juga digunakan untuk melanjutkan sumber media yang sebelumnya diputar setelah mobil dimatikan-dinyalakan kembali.
  • car-media-common menyediakan metode yang mudah untuk membuat daftar, terhubung, dan berinteraksi dengan aplikasi media.

Berikut adalah panduan khusus untuk penerapan perintah interaksi suara umum.

Mendapatkan daftar sumber media yang diinstal

Sumber media dapat dideteksi menggunakan PackageManager, dan memfilter layanan yang cocok dengan MediaBrowserService.SERVICE_INTERFACE. Di beberapa mobil, mungkin ada beberapa implementasi layanan browser media khusus, yang harus dikecualikan. Berikut adalah contoh logika ini:

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

Perhatikan bahwa sumber media dapat diinstal atau di-uninstal kapan saja. Untuk mempertahankan daftar yang akurat, sebaiknya terapkan instance BroadcastReceiver untuk tindakan intent ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REPLACED, dan ACTION_PACKAGE_REMOVED.

Menghubungkan ke sumber media yang sedang diputar

CarMediaService menyediakan metode untuk mendapatkan sumber media yang saat ini dipilih, dan saat sumber media ini berubah. Perubahan ini dapat terjadi karena pengguna berinteraksi dengan UI secara langsung, atau karena penggunaan tombol hardware di mobil. Di sisi lain, library car-media-common menawarkan cara yang mudah untuk terhubung ke sumber media tertentu. Berikut adalah cuplikan sederhana tentang cara terhubung ke aplikasi media yang saat ini dipilih:

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
    }

    
}

Mengontrol pemutaran sumber media yang sedang diputar

Dengan MediaBrowserCompat yang terhubung, Anda dapat dengan mudah mengirim perintah kontrol transpor ke aplikasi target. Berikut adalah contoh yang disederhanakan:

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

    
}

Menangani perintah sumber media lokal (radio, pemutar CD, Bluetooth, USB)

Sumber media lokal mengekspos fungsinya ke sistem menggunakan MediaSession dan MediaBrowse API yang sama yang dijelaskan di atas. Untuk mengakomodasi keunikan setiap jenis hardware, layanan MediaBrowse ini menggunakan konvensi tertentu untuk mengatur informasi dan perintah medianya.

Menangani radio

MediaBrowseService Radio dapat diidentifikasi oleh filter intent ACTION_PLAY_BROADCASTRADIO. Aplikasi ini diharapkan mengikuti kontrol pemutaran dan struktur penelusuran media yang dijelaskan di Menerapkan radio. AAOS menawarkan library car-broadcastradio-support yang berisi konstanta dan metode untuk membantu OEM membuat implementasi MediaBrowseService untuk layanan radio mereka sendiri yang mengikuti protokol yang ditentukan, dan memberikan dukungan untuk aplikasi yang menggunakan hierarki penjelajahan mereka (misalnya, VIA).

Menangani input tambahan, audio CD, dan media USB

Tidak ada implementasi default untuk sumber media ini sebagai bagian dari AOSP. Pendekatan yang disarankan adalah:

  • Minta OEM menerapkan layanan media untuk setiap aplikasi tersebut. Untuk mengetahui detailnya, lihat Membuat aplikasi media untuk mobil.
  • Implementasi MediaBrowseService ini akan diidentifikasi dan direspons dalam tindakan intent yang ditentukan di Intent pemutaran umum.
  • Layanan ini akan mengekspos hierarki jelajah sesuai dengan panduan yang dijelaskan di Jenis sumber lainnya.

Menangani Bluetooth

Konten media Bluetooth ditampilkan melalui profil Bluetooth AVRCP. Untuk memfasilitasi akses ke fungsi ini, AAOS menyertakan implementasi MediaBrowserService dan MediaSession yang memisahkan detail komunikasi (lihat packages/apps/Bluetooth).

Struktur hierarki browser media masing-masing ditentukan di class BrowseTree. Perintah kontrol pemutaran dapat dikirimkan dengan cara yang sama seperti aplikasi lain, dengan menggunakan penerapan MediaSession-nya.

Menangani perintah media streaming

Untuk menerapkan streaming media sisi server, VIA harus menjadi sumber media itu sendiri, yang menerapkan MediaBrowse dan MediaSession API. Lihat Membuat aplikasi media untuk mobil. Dengan menerapkan API ini, aplikasi kontrol suara akan dapat (di antara hal lainnya):

  • Berpartisipasi dengan lancar dalam pemilihan sumber media
  • Otomatis dilanjutkan setelah mobil dimulai ulang
  • Memberikan kontrol pemutaran dan penjelajahan menggunakan UI Media Center
  • Menerima peristiwa tombol media hardware standar

Tidak ada cara standar untuk berinteraksi dengan semua aplikasi navigasi. Untuk integrasi dengan Google Maps, lihat Intent Google Maps untuk Android Automotive. Untuk integrasi dengan aplikasi lain, hubungi developer aplikasi secara langsung. Sebelum meluncurkan intent ke aplikasi apa pun (termasuk Google Maps), pastikan intent dapat di-resolve (lihat Permintaan intent). Hal ini menciptakan peluang untuk memberi tahu pengguna jika aplikasi target tidak tersedia.

Memenuhi perintah kendaraan

Akses ke properti kendaraan untuk operasi baca dan tulis disediakan melalui CarPropertyManager. Jenis properti kendaraan, penerapannya, dan detail lainnya dijelaskan dalam Konfigurasi properti. Untuk deskripsi yang akurat tentang properti yang didukung oleh Android, sebaiknya lihat langsung hardware/interfaces/automotive/vehicle/2.0/types.hal. Enum VehicleProperty yang ditentukan di sana berisi properti standar dan khusus vendor, jenis data, mode perubahan, satuan, dan definisi akses baca/tulis.

Untuk mengakses konstanta yang sama dari Java, Anda dapat menggunakan VehiclePropertyIds dan class pendampingnya. Properti yang berbeda memiliki izin Android yang berbeda yang mengontrol aksesnya. Izin ini dideklarasikan dalam manifes CarService, dan pemetaan antara properti dan izin yang dijelaskan dalam Javadoc VehiclePropertyIds dan diterapkan di PropertyHalServiceIds.

Membaca properti kendaraan

Berikut adalah contoh yang menunjukkan cara membaca kecepatan kendaraan:

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

    ...
}

Menetapkan properti kendaraan

Berikut adalah contoh yang menunjukkan cara mengaktifkan dan menonaktifkan AC depan.

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

    …
}

Memenuhi perintah komunikasi

Menangani perintah pesan

VIA harus menangani pesan masuk dengan mengikuti alur "ketuk untuk membaca" yang dijelaskan dalam Asisten suara Ketuk untuk Membaca, yang secara opsional dapat menangani pengiriman balasan kembali kepada pengirim pesan masuk. Selain itu, VIA dapat menggunakan SmsManager (bagian dari paket android.telephony) untuk menulis dan mengirim pesan SMS langsung dari mobil atau melalui Bluetooth.

Menangani perintah panggilan

Dengan cara yang sama, VIA dapat menggunakan TelephonyManager untuk melakukan panggilan telepon dan menelepon ke nomor pesan suara pengguna. Dalam hal ini, VIA akan berinteraksi dengan stack telephony secara langsung atau dengan aplikasi Pemanggil Mobil. Apa pun yang terjadi, aplikasi Pemanggil Mobil haruslah yang menampilkan UI terkait panggilan suara kepada pengguna.

Memenuhi perintah lainnya

Untuk mengetahui daftar kemungkinan titik integrasi lainnya antara VIA dan sistem, periksa daftar intent Android yang terkenal. Banyak perintah pengguna dapat diselesaikan di sisi server (misalnya, membaca email dan acara kalender pengguna) dan tidak memerlukan interaksi apa pun dengan sistem selain interaksi suara itu sendiri.

Tindakan imersif (menampilkan konten visual)

Jika dapat meningkatkan tindakan atau pemahaman pengguna, VIA dapat menyediakan konten visual tambahan di layar mobil. Untuk meminimalkan gangguan pengemudi, buat konten tersebut tetap sederhana, singkat, dan dapat ditindaklanjuti. Untuk mengetahui detail tentang panduan UI/UX tentang tindakan imersif, lihat Asisten yang Dimuat Sebelumnya: Panduan UX.

Untuk mengaktifkan penyesuaian dan konsistensi dengan desain head unit (HU) lainnya, VIA harus menggunakan komponen Library UI Mobil untuk sebagian besar elemen UI. Untuk mengetahui detailnya, lihat Penyesuaian.