Penuhi perintah

Halaman ini menjelaskan cara memenuhi perintah dengan interaksi suara.

Penuhi perintah media

Perintah terkait media dapat dibagi menjadi tiga kelompok berbeda:

  • Sumber media eksternal (seperti Spotify yang dipasang di AAOS).
  • Sumber media backend (seperti musik yang dialirkan melalui VIA).
  • Sumber media lokal (seperti radio mobil).

Menangani perintah sumber media eksternal

Sumber media eksternal didefinisikan sebagai aplikasi Android yang mendukung API MediaSessionCompat dan MediaBrowseCompat (lihat Membuat aplikasi media untuk mobil untuk penjelasan mendetail tentang penggunaan API ini).

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

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

Selain MediaBrowserCompat dan MediaControllerCompat , AAOS menyediakan hal berikut:

  • CarMediaService memberikan informasi terpusat pada sumber media yang dipilih saat ini. Ini juga digunakan untuk melanjutkan sumber media yang diputar sebelumnya setelah mobil dimatikan dan dihidupkan ulang.
  • car-media-common menyediakan metode mudah untuk membuat daftar, menghubungkan, dan berinteraksi dengan aplikasi media.

Di bawah ini adalah pedoman khusus untuk penerapan perintah interaksi suara yang umum.

Dapatkan daftar sumber media yang diinstal

Sumber media dapat dideteksi menggunakan PackageManager , dan memfilter layanan yang cocok dengan MediaBrowserService.SERVICE_INTERFACE . Di beberapa mobil mungkin terdapat penerapan 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;
}

Ketahuilah bahwa sumber media mungkin dipasang atau dihapus instalasinya kapan saja. Untuk menjaga daftar yang akurat, disarankan untuk mengimplementasikan instance BroadcastReceiver untuk tindakan maksud ACTION_PACKAGE_ADDED , ACTION_PACKAGE_CHANGED , ACTION_PACKAGE_REPLACED , dan ACTION_PACKAGE_REMOVED .

Hubungkan ke sumber media yang sedang diputar

CarMediaService menyediakan metode untuk mendapatkan sumber media yang dipilih saat ini, dan kapan sumber media ini berubah. Perubahan ini bisa terjadi karena pengguna berinteraksi dengan UI secara langsung, atau karena penggunaan tombol perangkat keras di dalam mobil. Di sisi lain, perpustakaan umum media mobil menawarkan cara mudah untuk terhubung ke sumber media tertentu. Berikut cuplikan sederhana tentang cara menyambung 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
    }

    …
}

Kontrol pemutaran sumber media yang sedang diputar

Dengan MediaBrowserCompat yang terhubung, pengiriman perintah kontrol transportasi ke aplikasi target menjadi mudah. 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 kekhasan masing-masing jenis perangkat keras, layanan MediaBrowse ini menggunakan konvensi khusus untuk mengatur informasi dan perintah medianya.

Menangani radio

Radio MediaBrowseService dapat diidentifikasi dengan filter maksud ACTION_PLAY_BROADCASTRADIO . Mereka diharapkan mengikuti kontrol pemutaran dan struktur penelusuran media yang dijelaskan dalam Menerapkan radio . AAOS menawarkan pustaka 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 menyediakan dukungan untuk aplikasi yang menggunakan pohon penelusuran mereka (misalnya, VIA).

Menangani input tambahan, audio CD, dan media USB

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

  • Minta OEM menerapkan layanan media untuk masing-masing OEM. Untuk mengetahui detailnya, lihat Membuat aplikasi media untuk mobil .
  • Implementasi MediaBrowseService ini akan diidentifikasi dan ditanggapi dalam tindakan maksud yang ditentukan di Niat bermain umum .
  • Layanan ini akan menampilkan pohon penelusuran dengan mengikuti panduan yang dijelaskan di Jenis sumber lainnya .

Tangani Bluetooth

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

Struktur pohon browser media masing-masing ditentukan di kelas BrowserTree . Perintah kontrol pemutaran dapat disampaikan serupa dengan aplikasi lain, dengan menggunakan implementasi MediaSession-nya.

Menangani perintah media streaming

Untuk mengimplementasikan streaming media sisi server, VIA harus menjadi sumber media, mengimplementasikan MediaBrowse dan MediaSession API. Lihat Membuat aplikasi media untuk mobil . Dengan mengimplementasikan API ini, aplikasi kontrol suara akan mampu (antara lain):

  • Berpartisipasilah dengan lancar dalam pemilihan sumber media
  • Secara otomatis dilanjutkan setelah mobil dinyalakan kembali
  • Menyediakan kontrol pemutaran dan penjelajahan menggunakan UI Pusat Media
  • Menerima peristiwa tombol media perangkat keras standar

Tidak ada cara standar untuk berinteraksi dengan semua aplikasi navigasi. Untuk integrasi dengan Google Maps, lihat Google Maps untuk Android Automotive Intents . Untuk integrasi dengan aplikasi lain, hubungi pengembang aplikasi secara langsung. Sebelum meluncurkan maksud ke aplikasi apa pun (termasuk Google Maps), verifikasi bahwa maksud tersebut dapat diselesaikan (lihat Permintaan maksud ). Hal ini menciptakan peluang untuk memberi tahu pengguna jika aplikasi target tidak tersedia.

Memenuhi perintah kendaraan

Akses ke properti kendaraan untuk membaca dan menulis disediakan melalui CarPropertyManager . Jenis properti kendaraan, implementasinya, dan detail lainnya dijelaskan dalam Konfigurasi properti . Untuk penjelasan akurat tentang properti yang didukung oleh Android, sebaiknya rujuk langsung ke hardware/interfaces/automotive/vehicle/2.0/types.hal . Enum VehicleProperty yang ditentukan di sana berisi properti standar dan khusus vendor, tipe data, mode perubahan, unit, dan definisi akses baca/tulis.

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

Membaca properti kendaraan

Berikut ini contoh 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);
    }

    ...
}

Tetapkan properti kendaraan

Berikut contoh cara menghidupkan dan mematikan 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 perpesanan

VIA harus menangani pesan masuk mengikuti alur "ketuk untuk membaca" yang dijelaskan dalam Asisten suara Ketuk untuk Membaca , yang secara opsional dapat menangani pengiriman balasan kembali ke 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 serupa, VIA dapat menggunakan TelephonyManager untuk melakukan panggilan telepon dan menghubungi nomor pesan suara pengguna. Dalam kasus ini, VIA akan berinteraksi dengan tumpukan telepon secara langsung atau dengan aplikasi Car Dialer. Bagaimanapun, aplikasi Car Dialer haruslah yang menampilkan UI terkait panggilan suara kepada pengguna.

Penuhi perintah lainnya

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

Tindakan imersif (menampilkan konten visual)

Jika hal ini meningkatkan tindakan atau pemahaman pengguna, VIA dapat memberikan konten visual tambahan di layar mobil. Untuk meminimalkan gangguan pengemudi, buatlah konten yang sederhana, singkat, dan dapat ditindaklanjuti. Untuk detail tentang pedoman UI/UX mengenai tindakan imersif, lihat Asisten yang Dimuat Sebelumnya: Panduan UX .

Untuk memungkinkan penyesuaian dan konsistensi dengan desain head unit (HU) lainnya, VIA harus menggunakan komponen Car UI Library untuk sebagian besar elemen UI. Untuk detailnya, lihat Kustomisasi .