Выполнение команд

Узнайте, как выполнять эти типы команд с помощью голосового взаимодействия:

Выполнение медиа-команд

Команды, связанные с мультимедиа, можно разделить на три разные группы:

  • Внешние источники мультимедиа (например, Spotify, установленный в AAOS).
  • Внутренние медиа-источники (например, музыка, транслируемая через VIA).
  • Местные СМИ (например, автомобильное радио).

Обработка команд внешнего источника мультимедиа

Внешние источники мультимедиа определяются как приложения Android, которые поддерживают API-интерфейсы MediaSession и MediaBrowse (подробное объяснение использования этих 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 для следующих действий намерения: ACTION_PACKAGE_ADDED , ACTION_PACKAGE_CHANGED , ACTION_PACKAGE_REPLACED , ACTION_PACKAGE_REMOVED .

Подключиться к воспроизводимому медиа-источнику

CarMediaService предоставляет методы для получения текущего выбранного источника мультимедиа и при изменении этого источника мультимедиа. Эти изменения могли произойти из-за того, что пользователь напрямую взаимодействовал с пользовательским интерфейсом или из-за использования аппаратных кнопок в автомобиле. С другой стороны, библиотека 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;
    }

    …
}

Обработка команд локального источника мультимедиа (радио, проигрыватель компакт-дисков, Bluetooth, USB)

Локальные медиа-источники предоставляют системе свои функциональные возможности с помощью тех же API-интерфейсов MediaSession и MediaBrowse, которые подробно описаны выше. Чтобы учесть особенности каждого типа оборудования, эти службы MediaBrowse используют определенные соглашения для организации своей информации и мультимедийных команд.

Работа с радио

Radio MediaBrowseService можно идентифицировать с помощью фильтра намерений ACTION_PLAY_BROADCASTRADIO . Ожидается, что они будут следовать элементам управления воспроизведением и структуре просмотра мультимедиа, описанным здесь: Реализация радио с мультимедиа . AAOS предлагает библиотеку car-broadcastradio-support , содержащую константы и методы, помогающие OEM-производителям создавать реализации MediaBrowseService для своих собственных радиослужб, которые следуют определенному протоколу, и обеспечивает поддержку приложений, использующих их дерево просмотра (например, VIA).

Работа с дополнительным входом, CD-аудио и USB-носителем

Эти источники мультимедиа не реализованы по умолчанию как часть AOSP. Предлагаемый подход заключается в следующем:

Работа с Bluetooth

Медиа-контент Bluetooth отображается через профиль Bluetooth AVRCP. Чтобы облегчить доступ к этой функциональности, AAOS включает реализацию MediaBrowserService и MediaSession, которая абстрагирует детали связи (см. packages/apps/Bluetooth ).

Соответствующая древовидная структура медиабраузера определяется в классе BrowseTree . Команды управления воспроизведением можно доставлять аналогично любому другому приложению, используя его реализацию MediaSession.

Обработка команд потокового мультимедиа

Для реализации потоковой передачи мультимедиа на стороне сервера VIA должен сам стать источником мультимедиа, реализовав API MediaBrowse и MediaSession. См. раздел Создание мультимедийных приложений для автомобилей . Реализуя эти API, приложение голосового управления сможет (среди прочего):

  • Беспрепятственно участвуйте в выборе медиа-источника
  • Автоматически возобновляться после перезапуска автомобиля
  • Обеспечьте управление воспроизведением и просмотром с помощью пользовательского интерфейса Media Center.
  • Получение стандартных событий аппаратной кнопки мультимедиа

Не существует стандартизированного способа взаимодействия со всеми навигационными приложениями. Информацию об интеграции с Google Maps см. в разделе Google Maps for Android Automotive Intents . Для интеграции с другими приложениями обращайтесь напрямую к разработчикам приложений. Перед запуском намерения для любого приложения (включая Google Maps) убедитесь, что намерение может быть разрешено (см. Запросы намерения ). Это создает возможность информировать пользователя, если целевое приложение недоступно.

Выполнение команд автомобиля

Доступ к свойствам автомобиля для чтения и записи предоставляется через CarPropertyManager . Типы свойств транспортных средств, их реализация и другие детали объясняются здесь: AOSP/Develop/Automotive — Vehicle Properties . Для точного описания свойств, поддерживаемых Android, лучше всего обратиться непосредственно к hardware/interfaces/automotive/vehicle/2.0/types.hal . Перечисление VehicleProperty, определенное там, содержит как стандартные, так и специфичные для производителя свойства, типы данных, режим изменения, единицы измерения и определение доступа для чтения/записи.

Чтобы получить доступ к этим же константам из Java, вы можете использовать VehiclePropertyIds и сопутствующие ему классы. Различные свойства имеют разные разрешения Android, контролирующие их доступ. Эти разрешения объявляются в манифесте 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 должны обрабатывать входящие сообщения в соответствии с потоком «нажмите для чтения», описанным в голосовом помощнике «нажмите для чтения », который может дополнительно обрабатывать отправку ответов обратно отправителю входящего сообщения. Кроме того, VIA могут использовать SmsManager (часть пакета android.telephony ) для составления и отправки SMS-сообщений прямо из автомобиля или по Bluetooth.

Обработка команд вызова

Аналогичным образом VIA могут использовать TelephonyManager для совершения телефонных звонков и звонков на номер голосовой почты пользователя. В этих случаях VIA будут взаимодействовать со стеком телефонии напрямую или с приложением Car Dialer. В любом случае приложение Car Dialer должно отображать для пользователя пользовательский интерфейс, связанный с голосовыми вызовами.

Выполнение других команд

Список других возможных точек интеграции между VIA и системой см. в списке известных намерений Android . Многие пользовательские команды могут выполняться на стороне сервера (например, чтение электронной почты пользователей и событий календаря) и не требуют никакого взаимодействия с системой, кроме самого голосового взаимодействия.

Иммерсивные действия (отображение визуального контента)

Там, где это улучшает действия пользователя или его понимание, VIA может предоставлять дополнительный визуальный контент на экране автомобиля. Чтобы свести к минимуму отвлечение водителя, делайте такой контент простым, кратким и действенным. Дополнительные сведения о рекомендациях по UI/UX для иммерсивных действий см. в разделе Предварительно загруженные помощники: руководство по UX .

Чтобы обеспечить возможность настройки и согласованность с остальной конструкцией головного устройства (HU), VIA должны использовать компоненты Car UI Library для большинства элементов пользовательского интерфейса. Подробнее см. в разделе Настройка .