Выполнять команды

На этой странице описано, как выполнять команды с помощью голосового взаимодействия.

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

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

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

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

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

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 используют определенные соглашения для организации своей информации и мультимедийных команд.

Ручка радио

Радио 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 см. в разделе Карты Google для Android Automotive Intents . По вопросам интеграции с другими приложениями обращайтесь напрямую к разработчикам приложения. Прежде чем запускать намерение в любом приложении (включая Карты Google), убедитесь, что намерение может быть разрешено (см. Запросы намерений ). Это создает возможность информировать пользователя, если целевое приложение недоступно.

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

Доступ к свойствам автомобиля как для чтения, так и для записи предоставляется через CarPropertyManager . Типы свойств транспортных средств, их реализация и другие подробности описаны в разделе «Конфигурации свойств» . Для точного описания свойств, поддерживаемых 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 улучшает действия или понимание пользователя, он может предоставлять дополнительный визуальный контент на экране автомобиля. Чтобы свести к минимуму отвлечение водителя, сделайте такой контент простым, кратким и действенным. Подробные сведения о рекомендациях по пользовательскому интерфейсу и пользовательскому интерфейсу для иммерсивных действий см. в разделе «Предварительно загруженные помощники: руководство по пользовательскому интерфейсу» .

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