Статьи

Вещи Android: добавление Google Assistant

С ростом Интернета вещей (IoT) разработчикам и инженерам пришлось переосмыслить повседневное взаимодействие пользователей с устройствами.

В то время как экраны хорошо работают для веб-сайтов и большинства приложений, устройства, которые взаимодействуют с реальным миром, могут быть немного более утомительными в работе, если вам приходится использовать несколько кнопок или экран для работы. Одним из способов решения этой проблемы является включение голосового управления на ваших устройствах.

В этом руководстве вы узнаете о Google Assistant и о том, как добавить его на свои устройства Android Things IoT.

Если перед тем, как начать, вам нужно немного рассказать об Android Things, ознакомьтесь с некоторыми другими моими постами здесь на Envato Tuts +.

  • Android
    Введение в Android вещи
  • Android SDK
    Android Things: ваш первый проект

Google Assistant SDK позволяет добавлять голосовые элементы управления с обнаружением ключевых слов, обработкой естественного языка и другими функциями машинного обучения для ваших устройств IoT. С помощью Assistant SDK можно многое сделать, но в этом руководстве основное внимание будет уделено основам: как вы можете включить его в свои устройства Android Things, чтобы задавать вопросы, получать информацию и взаимодействовать со стандартными «из коробки» «Помощник по функционалу.

Что касается требований к оборудованию, у вас есть несколько вариантов. Вы можете использовать Raspberry Pi с Android Things и AIY Voice Kit .

Или вы можете использовать стандартный динамик с разъемом AUX и USB-микрофоном.

Кроме того, вы можете использовать любую другую конфигурацию оборудования I²S. Хотя мы не будем подробно обсуждать I²S в этом руководстве, стоит отметить, что Voice Kit будет использовать этот протокол. После настройки микрофона и динамика вам также необходимо добавить кнопку на свое устройство. Эта кнопка должна отслеживать два состояния: нажатие и отпускание. Это можно сделать с помощью многополюсной аркадной кнопки или стандартной кнопки с резистором, снижающим нагрузку, прикрепленным к одному из полюсов.

Как только вы подключили свое оборудование, пришло время добавить Assistant SDK на ваше устройство. Сначала вам нужно будет создать новый файл учетных данных для вашего устройства. Инструкции по этому вопросу можно найти в документации Google Assistant . Получив файл credentials.json , вам нужно будет поместить его в каталог res / raw вашего модуля Android Things.

файл credentialsjson в каталоге перерисовки

После того, как ваши учетные данные созданы в Google, вам нужно будет указать некоторые разрешения для вашего приложения. Откройте файл AndroidManifest.xml и добавьте следующие строки в тег manifest , но перед тегом application .

1
2
3
4
<uses-permission android:name=»android.permission.RECORD_AUDIO» />
<uses-permission android:name=»android.permission.MODIFY_AUDIO_SETTINGS» />
<uses-permission android:name=»android.permission.INTERNET» />
<uses-permission android:name=»com.google.android.things.permission.MANAGE_AUDIO_DRIVERS» />

Стоит отметить, что вам нужно будет перезагрузить устройство после установки приложения с этими разрешениями, чтобы они были предоставлены.

Затем вам нужно будет скопировать модуль gRPC в ваше приложение для связи с домашним устройством. Это немного сложнее, поэтому лучше всего его получить из образца приложения Google Assistant Android Things, который можно найти в учетной записи Android Things GitHub . Затем вам нужно будет обновить файл settings.gradle, чтобы отразить новый модуль.

1
include ‘:mobile’, ‘:things’, ‘:grpc’

После обновления settings.gradle включите модуль в качестве зависимости в свой модуль вещей , включив следующую строку в файл build.gradle модуля вещей и включив драйвер кнопки Google (он понадобится вам для активации микрофона) и дополнительный драйвер Voice Hat, если Вы используете это оборудование.

1
2
3
4
5
compile project(‘:grpc’)
compile ‘com.google.android.things.contrib:driver-button:0.4’
 
//optional
compile ‘com.google.android.things.contrib:driver-voicehat:0.2’

Вам также необходимо включить protobuf в качестве зависимости в файл build.gradle уровня проекта .

1
classpath «com.google.protobuf:protobuf-gradle-plugin:0.8.0»

Далее, давайте включим библиотеку oauth2 в наш проект, открыв файл build.gradle модуля вещи и добавив следующее под узлом dependencies :

1
2
3
compile(‘com.google.auth:google-auth-library-oauth2-http:0.6.0’) {
    exclude group: ‘org.apache.httpcomponents’, module: ‘httpclient’
}

Здесь вы можете столкнуться с конфликтами, если ваш проект имеет зависимость Espresso, с сообщением об ошибке, похожим на это:

1
Warning:Conflict with dependency ‘com.google.code.findbugs:jsr305’ in project ‘:things’.

Если это так, просто удалите зависимость Espresso из build.gradle .

После синхронизации проекта создайте новый класс с именем Credentials.java для доступа к своим учетным данным.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class Credentials {
 static UserCredentials fromResource(Context context, int resourceId)
 throws IOException, JSONException {
        InputStream is = context.getResources().openRawResource(resourceId);
        byte[] bytes = new byte[is.available()];
        is.read(bytes);
        JSONObject json = new JSONObject(new String(bytes, «UTF-8»));
            return new UserCredentials(json.getString(«client_id»),
                json.getString(«client_secret»),
                json.getString(«refresh_token»)
            );
        }
    }
}

Как только ваш класс Credentials.java создан, пришло время создать новый класс с именем EmbeddedAssistant.java . Это вспомогательный класс, изначально созданный инженерами Google для упрощения работы с Google Assistant for Android Things. Хотя этот класс довольно прост в использовании, просто включив его в ваш проект, мы захотим углубиться в него и понять, как он на самом деле работает.

Первое, что вы сделаете, это создадите два внутренних абстрактных класса, которые будут использоваться для обработки обратных вызовов в диалоге и запросов к Assistant API.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class EmbeddedAssistant {
 
    public static abstract class RequestCallback {
        public void onRequestStart() {}
        public void onAudioRecording() {}
        public void onSpeechRecognition(String utterance) {}
    }
 
    public static abstract class ConversationCallback {
        public void onResponseStarted() {}
        public void onResponseFinished() {}
        public void onConversationEvent(EventType eventType) {}
        public void onAudioSample(ByteBuffer audioSample) {}
        public void onConversationError(Status error) {}
        public void onError(Throwable throwable) {}
        public void onVolumeChanged(int percentage) {}
        public void onConversationFinished() {}
    }
}

Как только ваши два внутренних класса написаны, продолжайте и определите следующий набор глобальных значений в верхней части вашего класса. Большинство из них будут инициализированы позже в этом файле. Эти значения используются для отслеживания состояния устройства и взаимодействия с Assistant API.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static final String ASSISTANT_API_ENDPOINT = «embeddedassistant.googleapis.com»;
private static final int AUDIO_RECORD_BLOCK_SIZE = 1024;
 
private RequestCallback mRequestCallback;
private ConversationCallback mConversationCallback;
 
//Used for push-to-talk functionality
private ByteString mConversationState;
private AudioInConfig mAudioInConfig;
private AudioOutConfig mAudioOutConfig;
private AudioTrack mAudioTrack;
private AudioRecord mAudioRecord;
private int mVolume = 100;
 
private UserCredentials mUserCredentials;
 
private MicrophoneMode mMicrophoneMode;
private HandlerThread mAssistantThread;
private Handler mAssistantHandler;
 
// gRPC client and stream observers.
private int mAudioOutSize;
private EmbeddedAssistantGrpc.EmbeddedAssistantStub mAssistantService;
private StreamObserver<ConverseRequest> mAssistantRequestObserver;

Хотя в приведенном выше есть объект StreamObserver<ConverseRequest> для запросов к Assistant API, он вам также понадобится для ответов. Этот объект будет состоять из оператора switch, который проверяет состояние ответа, а затем обрабатывает его соответствующим образом.

1
2
3
4
5
private StreamObserver<ConverseResponse> mAssistantResponseObserver =
   new StreamObserver<ConverseResponse>() {
       @Override
       public void onNext(ConverseResponse value) {
           switch (value.getConverseResponseCase()) {

В первом случае проверяется конец разговора пользователя и используется ConversationCallback чтобы остальная часть класса знала, что ответ неизбежен.

1
2
3
4
5
6
case EVENT_TYPE:
   mConversationCallback.onConversationEvent(value.getEventType());
   if (value.getEventType() == EventType.END_OF_UTTERANCE) {
       mConversationCallback.onResponseStarted();
   }
   break;

В следующем случае будет проверяться и обновляться разговор, громкость и состояние микрофона.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
case RESULT:
   // Update state.
   mConversationState = value.getResult().getConversationState();
    
   // Update volume.
   if (value.getResult().getVolumePercentage() != 0) {
       int volumePercentage = value.getResult().getVolumePercentage();
       mVolume = volumePercentage;
       mAudioTrack.setVolume(AudioTrack.getMaxVolume()
               * volumePercentage / 100.0f);
       mConversationCallback.onVolumeChanged(volumePercentage);
   }
    
   if (value.getResult().getSpokenRequestText() != null &&
           !value.getResult().getSpokenRequestText().isEmpty()) {
       mRequestCallback.onSpeechRecognition(value.getResult()
               .getSpokenRequestText());
   }
    
   // Update microphone mode.
   mMicrophoneMode = value.getResult().getMicrophoneMode();
   break;

Третий случай возьмет аудио результат и воспроизведет его для пользователя.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
case AUDIO_OUT:
   if (mAudioOutSize <= value.getAudioOut().getSerializedSize()) {
       mAudioOutSize = value.getAudioOut().getSerializedSize();
   } else {
       mAudioOutSize = 0;
       onCompleted();
   }
    
   final ByteBuffer audioData =
           ByteBuffer.wrap(value.getAudioOut().getAudioData().toByteArray());
   mAudioTrack.write(audioData, audioData.remaining(),
           AudioTrack.WRITE_BLOCKING);
   mConversationCallback.onAudioSample(audioData);
   break;

В последнем случае будут просто перенаправлены ошибки, возникшие в процессе разговора.

1
2
3
case ERROR:
   mConversationCallback.onConversationError(value.getError());
   break;

Последние два метода в этом потоке обрабатывают состояния ошибок и очищают по завершении диалога.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Override
public void onError(Throwable t) {
    mConversationCallback.onError(t);
}
 
@Override
public void onCompleted() {
    mConversationCallback.onResponseFinished();
    if (mMicrophoneMode == MicrophoneMode.DIALOG_FOLLOW_ON) {
        // Automatically start a new request
        startConversation();
    } else {
        // The conversation is done
        mConversationCallback.onConversationFinished();
    }
}

Затем вам нужно создать Runnable который будет обрабатывать потоковое аудио в другом потоке.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private Runnable mStreamAssistantRequest = new Runnable() {
    @Override
    public void run() {
        ByteBuffer audioData = ByteBuffer.allocateDirect(AUDIO_RECORD_BLOCK_SIZE);
        int result = mAudioRecord.read(audioData, audioData.capacity(),
                AudioRecord.READ_BLOCKING);
        if (result < 0) {
            return;
        }
        mRequestCallback.onAudioRecording();
        mAssistantRequestObserver.onNext(ConverseRequest.newBuilder()
                .setAudioIn(ByteString.copyFrom(audioData))
                .build());
        mAssistantHandler.post(mStreamAssistantRequest);
    }
};

Теперь, когда ваши глобальные значения определены, пришло время перейти к структуре для создания EmbeddedAssistant . Вам нужно будет иметь возможность получить учетные данные для вашего приложения, используя класс Credentials.java, который был создан ранее.

1
2
3
4
public static UserCredentials generateCredentials(Context context, int resourceId)
        throws IOException, JSONException {
    return Credentials.fromResource(context, resourceId);
}

Чтобы создать экземпляр этого класса, этот класс использует закрытый конструктор и шаблон построителя.

1
2
3
4
5
6
7
8
9
private EmbeddedAssistant() {}
 
public static class Builder {
    private EmbeddedAssistant mEmbeddedAssistant;
    private int mSampleRate;
 
    public Builder() {
        mEmbeddedAssistant = new EmbeddedAssistant();
    }

Внутренний класс Builder содержит несколько методов для инициализации значений в классе EmbeddedAssistant , таких как частота дискретизации, объем и учетные данные пользователя. После build() метода build() все определенные значения будут установлены в EmbeddedAssistant , будут настроены глобальные объекты, необходимые для работы, и будет выдано сообщение об ошибке, если какие-либо необходимые данные будут отсутствовать.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public Builder setRequestCallback(RequestCallback requestCallback) {
        mEmbeddedAssistant.mRequestCallback = requestCallback;
        return this;
    }
 
    public Builder setConversationCallback(ConversationCallback responseCallback) {
        mEmbeddedAssistant.mConversationCallback = responseCallback;
        return this;
    }
 
    public Builder setCredentials(UserCredentials userCredentials) {
        mEmbeddedAssistant.mUserCredentials = userCredentials;
        return this;
    }
 
    public Builder setAudioSampleRate(int sampleRate) {
        mSampleRate = sampleRate;
        return this;
    }
 
    public Builder setAudioVolume(int volume) {
        mEmbeddedAssistant.mVolume = volume;
        return this;
    }
 
    public EmbeddedAssistant build() {
        if (mEmbeddedAssistant.mRequestCallback == null) {
            throw new NullPointerException(«There must be a defined RequestCallback»);
        }
        if (mEmbeddedAssistant.mConversationCallback == null) {
            throw new NullPointerException(«There must be a defined ConversationCallback»);
        }
        if (mEmbeddedAssistant.mUserCredentials == null) {
            throw new NullPointerException(«There must be provided credentials»);
        }
        if (mSampleRate == 0) {
            throw new NullPointerException(«There must be a defined sample rate»);
        }
        final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
 
        // Construct audio configurations.
        mEmbeddedAssistant.mAudioInConfig = AudioInConfig.newBuilder()
                .setEncoding(AudioInConfig.Encoding.LINEAR16)
                .setSampleRateHertz(mSampleRate)
                .build();
        mEmbeddedAssistant.mAudioOutConfig = AudioOutConfig.newBuilder()
                .setEncoding(AudioOutConfig.Encoding.LINEAR16)
                .setSampleRateHertz(mSampleRate)
                .setVolumePercentage(mEmbeddedAssistant.mVolume)
                .build();
 
        // Construct AudioRecord & AudioTrack
        AudioFormat audioFormatOutputMono = new AudioFormat.Builder()
                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                .setEncoding(audioEncoding)
                .setSampleRate(mSampleRate)
                .build();
        int outputBufferSize = AudioTrack.getMinBufferSize(audioFormatOutputMono.getSampleRate(),
                audioFormatOutputMono.getChannelMask(),
                audioFormatOutputMono.getEncoding());
        mEmbeddedAssistant.mAudioTrack = new AudioTrack.Builder()
                .setAudioFormat(audioFormatOutputMono)
                .setBufferSizeInBytes(outputBufferSize)
                .build();
        mEmbeddedAssistant.mAudioTrack.setVolume(mEmbeddedAssistant.mVolume *
                AudioTrack.getMaxVolume() / 100.0f);
        mEmbeddedAssistant.mAudioTrack.play();
 
        AudioFormat audioFormatInputMono = new AudioFormat.Builder()
                .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
                .setEncoding(audioEncoding)
                .setSampleRate(mSampleRate)
                .build();
        int inputBufferSize = AudioRecord.getMinBufferSize(audioFormatInputMono.getSampleRate(),
                audioFormatInputMono.getChannelMask(),
                audioFormatInputMono.getEncoding());
        mEmbeddedAssistant.mAudioRecord = new AudioRecord.Builder()
                .setAudioSource(AudioSource.VOICE_RECOGNITION)
                .setAudioFormat(audioFormatInputMono)
                .setBufferSizeInBytes(inputBufferSize)
                .build();
 
        return mEmbeddedAssistant;
    }
}

После создания EmbeddedAssistant необходимо будет вызвать метод connect() , чтобы подключиться к Assistant API.

1
2
3
4
5
6
7
8
9
public void connect() {
    mAssistantThread = new HandlerThread(«assistantThread»);
    mAssistantThread.start();
    mAssistantHandler = new Handler(mAssistantThread.getLooper());
 
    ManagedChannel channel = ManagedChannelBuilder.forTarget(ASSISTANT_API_ENDPOINT).build();
    mAssistantService = EmbeddedAssistantGrpc.newStub(channel)
            .withCallCredentials(MoreCallCredentials.from(mUserCredentials));
}

После подключения к API вы будете использовать два метода для запуска и остановки разговоров. Эти методы будут публиковать объекты Runnable в mAssistantHandler для передачи объектов состояния диалога в потоки запросов и ответов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public void startConversation() {
    mAudioRecord.startRecording();
    mRequestCallback.onRequestStart();
    mAssistantHandler.post(new Runnable() {
        @Override
        public void run() {
            mAssistantRequestObserver = mAssistantService.converse(mAssistantResponseObserver);
            ConverseConfig.Builder converseConfigBuilder = ConverseConfig.newBuilder()
                    .setAudioInConfig(mAudioInConfig)
                    .setAudioOutConfig(mAudioOutConfig);
            if (mConversationState != null) {
                converseConfigBuilder.setConverseState(ConverseState.newBuilder()
                        .setConversationState(mConversationState)
                        .build());
            }
            mAssistantRequestObserver.onNext(
                    ConverseRequest.newBuilder()
                            .setConfig(converseConfigBuilder.build())
                            .build());
        }
    });
    mAssistantHandler.post(mStreamAssistantRequest);
}
 
public void stopConversation() {
    mAssistantHandler.post(new Runnable() {
        @Override
        public void run() {
            mAssistantHandler.removeCallbacks(mStreamAssistantRequest);
            if (mAssistantRequestObserver != null) {
                mAssistantRequestObserver.onCompleted();
                mAssistantRequestObserver = null;
            }
        }
    });
 
    mAudioRecord.stop();
    mAudioTrack.play();
    mConversationCallback.onConversationFinished();
}

Наконец, метод destroy() будет использоваться для разрыва, когда ваше приложение закрывается, и ему больше не требуется доступ к Assistant API.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public void destroy() {
    mAssistantHandler.post(new Runnable() {
        @Override
        public void run() {
            mAssistantHandler.removeCallbacks(mStreamAssistantRequest);
        }
    });
    mAssistantThread.quitSafely();
    if (mAudioRecord != null) {
        mAudioRecord.stop();
        mAudioRecord = null;
    }
    if (mAudioTrack != null) {
        mAudioTrack.stop();
        mAudioTrack = null;
    }
}

Как только ваши вспомогательные классы исчерпаны, пришло время их использовать. Вы сделаете это, отредактировав свой класс Android Things MainActivity для взаимодействия с EmbeddedAssistant и оборудованием для управления Google Assistant. Сначала добавьте интерфейс Button.OnButtonEventListener в свою Activity .

1
public class MainActivity extends Activity implements Button.OnButtonEventListener {

Затем вам нужно будет добавить переменные-члены и константы, которые потребуются вашему приложению. Эти значения будут управлять отменой нажатия кнопки, которая запускает Assistant, а также громкостью, форматом аудио, классом UserCredentials который вы создали ранее, и аппаратным обеспечением для вашего устройства.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
private static final int BUTTON_DEBOUNCE_DELAY_MS = 20;
private static final String PREF_CURRENT_VOLUME = «current_volume»;
private static final int SAMPLE_RATE = 16000;
private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int DEFAULT_VOLUME = 100;
 
private int initialVolume = DEFAULT_VOLUME;
 
private static final AudioFormat AUDIO_FORMAT_STEREO =
        new AudioFormat.Builder()
                .setChannelMask(AudioFormat.CHANNEL_IN_STEREO)
                .setEncoding(ENCODING)
                .setSampleRate(SAMPLE_RATE)
                .build();
 
// Hardware peripherals.
private VoiceHat mVoiceHat;
private Button mButton;
private EmbeddedAssistant mEmbeddedAssistant;
private UserCredentials userCredentials;

Как только вы определили свои константы, вам нужно будет создать несколько объектов обратного вызова, которые будут использоваться для разговоров и запросов с помощником.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private ConversationCallback mConversationCallback = new ConversationCallback() {
    @Override
    public void onConversationEvent(EventType eventType) {}
 
    @Override
    public void onAudioSample(ByteBuffer audioSample) {}
 
    @Override
    public void onConversationError(Status error) {}
 
    @Override
    public void onError(Throwable throwable) {}
 
    @Override
    public void onVolumeChanged(int percentage) {
        SharedPreferences.Editor editor = PreferenceManager
                .getDefaultSharedPreferences(AssistantActivity.this)
                .edit();
        editor.putInt(PREF_CURRENT_VOLUME, percentage);
        editor.apply();
    }
 
    @Override
    public void onConversationFinished() {}
};
 
private RequestCallback mRequestCallback = new RequestCallback() {
    @Override
    public void onRequestStart() {
        //starting assistant request, enable microphones
    }
 
    @Override
    public void onSpeechRecognition(String utterance) {}
};

В mConversationCallback вы заметите, что мы сохраняем процент изменения объема в общем предпочтении. Это позволяет вашему устройству поддерживать постоянный уровень громкости даже при перезагрузке.

Поскольку помощник работает асинхронно на вашем устройстве, вы инициализируете все для использования Assistant API в onCreate() , вызывая набор вспомогательных методов, которые мы определим в оставшейся части этого урока.

01
02
03
04
05
06
07
08
09
10
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    initVoiceHat();
    initButton();
    initVolume();
    initUserCredentials();
    initEmbeddedAssistant();
}

Первый вспомогательный метод — initVoiceHat() . Если экран Voice Hat подключен к Raspberry Pi, этот метод инициализирует устройство, чтобы пользователи могли использовать подключенный микрофон и динамик. Если Voice Hat не подключен, то можно использовать стандартный динамик AUX и USB-микрофон, и они будут автоматически подключены. Voice Hat использует I2S для обработки аудиоустройств на шине и упакован классом драйверов, написанным Google.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private void initVoiceHat() {
    PeripheralManagerService pioService = new PeripheralManagerService();
    List<String> i2sDevices = pioService.getI2sDeviceList();
    if (i2sDevices.size() > 0) {
        try {
            mVoiceHat = new VoiceHat(
                    BoardDefaults.getI2SDeviceForVoiceHat(),
                    BoardDefaults.getGPIOForVoiceHatTrigger(),
                    AUDIO_FORMAT_STEREO
            );
            mVoiceHat.registerAudioInputDriver();
            mVoiceHat.registerAudioOutputDriver();
        } catch (IllegalStateException e) {}
    }
}

Помощник будет отвечать только в этом примере, пока нажата кнопка запуска. Эта кнопка инициализируется и настраивается следующим образом:

1
2
3
4
5
6
7
8
private void initButton() {
    try {
        mButton = new Button(BoardDefaults.getGPIOForButton(),
                Button.LogicState.PRESSED_WHEN_LOW);
        mButton.setDebounceDelay(BUTTON_DEBOUNCE_DELAY_MS);
        mButton.setOnButtonEventListener(this);
    } catch( IOException e ) {}
}

Когда кнопка нажата, помощник начнет слушать новый разговор.

1
2
3
4
5
6
@Override
public void onButtonEvent(Button button, boolean pressed) {
    if (pressed) {
        mEmbeddedAssistant.startConversation();
    }
}

Вы можете найти больше информации о GPIO и Android Things в моем уроке о вводе и выводе с Android Things .

  • Android SDK
    Android Things: Периферийный ввод / вывод

Поскольку мы сохранили информацию о томе в SharedPreferences нашего устройства, мы можем получить к ней доступ напрямую для инициализации тома устройства.

1
2
3
4
private void initVolume() {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    initialVolume = preferences.getInt(PREF_CURRENT_VOLUME, DEFAULT_VOLUME);
}

Assistant SDK требует аутентификации для использования. К счастью, мы создали метод в классе EmbeddedAssistant ранее в этом руководстве специально для этой ситуации.

1
2
3
4
5
6
private void initUserCredentials() {
    userCredentials = null;
    try {
        userCredentials = EmbeddedAssistant.generateCredentials(this, R.raw.credentials);
    } catch (IOException | JSONException e) {}
}

Последний вспомогательный метод, который был вызван в onCreate() , инициализирует объект EmbeddedAssistant и подключит его к API.

01
02
03
04
05
06
07
08
09
10
11
private void initEmbeddedAssistant() {
    mEmbeddedAssistant = new EmbeddedAssistant.Builder()
            .setCredentials(userCredentials)
            .setAudioSampleRate(SAMPLE_RATE)
            .setAudioVolume(currentVolume)
            .setRequestCallback(mRequestCallback)
            .setConversationCallback(mConversationCallback)
            .build();
 
    mEmbeddedAssistant.connect();
}

Последнее, что вам нужно сделать, — это правильно отключить периферийные устройства, обновив метод onDestroy() в вашем Activity .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
@Override
protected void onDestroy() {
    super.onDestroy();
    if (mButton != null) {
        try {
            mButton.close();
        } catch (IOException e) {}
 
        mButton = null;
    }
 
    if (mVoiceHat != null) {
        try {
            mVoiceHat.unregisterAudioOutputDriver();
            mVoiceHat.unregisterAudioInputDriver();
            mVoiceHat.close();
        } catch (IOException e) {}
        mVoiceHat = null;
    }
    mEmbeddedAssistant.destroy();
}

После всего этого вы сможете взаимодействовать со своим устройством Android Things, как если бы это было Google Home!

В этом руководстве вы узнали о помощнике Google и о том, как его можно добавить в приложения Android Things. Эта функция дает вашим пользователям новый способ взаимодействия с вашим устройством и управления им, а также доступ ко многим функциям, доступным в Google. Это только часть фантастических функций, которые можно использовать в приложении Android Things и которые позволяют создавать новые удивительные устройства для ваших пользователей.

Пока вы здесь, ознакомьтесь с некоторыми другими моими постами на Android Things на Envato Tuts +!

  • Android вещи
    Android вещи и машинное обучение
  • Android SDK
    Android Things: понимание и написание драйверов
  • Android вещи
    Вещи Android: создание облачного сервисного швейцара