Под вечер возникла возможность попрограммировать. Изучив пару-тройку примеров и настроив окружение — я взялся за дело. Возникла идея написать приложение которое будет вытаскивать шутку про Чака Норриса из веб-сервиса, парсить JSON, и читать вслух.
Голосовая активация:
Наша шутилка будет активироваться голосовой командой. для этого определяем сервис в манифесте:
<service android:name="com.chucknorris.JokeService" android:icon="@drawable/icon" android:label="@string/app_name" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.google.android.glass.action.VOICE_TRIGGER"/> </intent-filter> <meta-data android:name="com.google.android.glass.VoiceTrigger" android:resource="@xml/voice_trigger_start"/> </service>
Ключевым элементом здесь, в отличие от обычных андроид приложений, является VoiceTrigger.
xml/voice_trigger_start выглядит просто:
<?xml version="1.0" encoding="utf-8"?> <trigger keyword="@string/chuck_norris_joke" />
Ну и наконец, строка которая задает фразу, по которой наш сервис запустится:
<string name="chuck_norris_joke">say Chuck Norris joke</string>
Примечание: согласно правилам, которые Google выдвигает по отношению к активационным фразам — они должны начинаться с глагола. Мне лично это правило не очень нравится, но сапожник не вправе судить выше сапога. Просто уж больно много фраз будет начинаться с «show», «get», «say», «start»
Более подробно здесь
Теперь, когда мы определили VoiceTrigger, наш сервис будет стартовать, если мы скажем «ok glass, say Chuck Norris joke»
К слову, Activity тоже можно запустить с помощью голосовой команды. Кусочек манифеста будет таковым:
<activity android:name="com.google.android.glass.sample.waveform.WaveformActivity" > <intent-filter> <action android:name="com.google.android.glass.action.VOICE_TRIGGER" /> </intent-filter> <meta-data android:name="com.google.android.glass.VoiceTrigger" android:resource="@xml/voice_trigger_start" /> </activity>
Начинка сервиса
Очень хочется рассказать о Card и LiveCard, но как-нибудь в другой раз. Наша шутилка не будет показывать ничего, просто будет шутить вслух.
Раз шутку нужно вытащить из интернет-ресурса, то не забудем задекларировать сие намерение в AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
В UI Thread лазить в интернет воспрещается, заведем для этой цели AsyncTask
doInBackground выполняет запрос на веб сервис:
HttpClient client = new DefaultHttpClient(); HttpGet getRequest = new HttpGet(); try { // construct a URI object getRequest.setURI(new URI(urls[0])); } catch (URISyntaxException e) { Log.e("URISyntaxException", e.toString()); } // buffer reader to read the response BufferedReader in = null; // the service response HttpResponse response = null; try { // execute the request response = client.execute(getRequest); } catch (ClientProtocolException e) { Log.e("ClientProtocolException", e.toString()); } catch (IOException e) { Log.e("IO exception", e.toString()); } try { in = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); } catch (IllegalStateException e) { Log.e("IllegalStateException", e.toString()); } catch (IOException e) { Log.e("IO exception", e.toString()); } StringBuffer buff = new StringBuffer(""); String line = ""; try { while ((line = in.readLine()) != null) { buff.append(line); } } catch (IOException e) { Log.e("IO exception", e.toString()); return e.getMessage(); } try { in.close(); } catch (IOException e) { Log.e("IO exception", e.toString()); }
затем парсит JSON:
String joke = ""; try { JSONObject jObject = new JSONObject(buff.toString()); joke = jObject.getJSONObject("value").getString("joke"); } catch (JSONException e) { Log.e("JSON exception", e.toString()); }
ok glass скажи шось
AsyncTask предоставляет метод onPostExecute. Стало быть, когда запрос выполнен и шутка получена, вытащена из JSON оболочки — самое время произнести ее вслух. Android любезно помогает нам не только распознать речь но и произнести текст с помощью TextToSpeech.
TextToSpeech инициализируем в сервисе и передаем в AsyncTask через параметр конструктора.
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener(){ @Override public void onInit(int i) { //TODO по-хорошему надо бы подождать инициализации } });
в onPostExecute вызываем новый метод readOutLoud
protected void onPostExecute(String joke) { if (exception != null) { return; } readOutLoud(joke); }
чтение шутки вслух:
private void readOutLoud(String joke) { tts.speak(joke, TextToSpeech.QUEUE_FLUSH, null); }
Отладка и загладка
Теперь осталось подключить очки через USB, собрать apk, align, sign, deploy (IDE все сделает за нас). Никакой Activity запускать не нужно.
итак, барабанная дробь
— «ok glass, say Chuck Norris Joke»
— «Chuck Norris can read from an input stream.»
Вместо послесловия
Пользуясь случаем, хочу поблагодарить www.icndb.com/. Я думаю надо бы сделать похожий сервис с шутками про Штирлица. Если будет время — займусь.
Насчет голосового воспроизведения
наверняка есть дополнительные настройки скорости чтения и/или голоса. На OS X они есть.
Возвращаясь к пункту «а что показывать»
небольшой фрагмент, который позволяет создать статическую карточку с картинкой слева и текстом шутки:
private void publishJokeCard(String joke) { Card card = new Card(ctx); card.setText(joke); card.addImage(R.drawable.chuck); TimelineManager.from(ctx).insert(card); }
Немного больше о карточках — в следующий раз, надо немного разобраться. Пока писал одно приложение — задеплоил на очки — а там уже новая версия SDK. Благо кто-то на форуме поделился опытом, что вышло обновление день назад (на тот момент). Как выглядят карточки можно посмотреть здесь
Картинки
Иконку подготовить не трудно — основное требование — белая, на прозрачном фоне и 50х50 пикселей.
Картинка для карточки — ну в принципе любая, но лучше — где-то треть размера ширины карточки и полный размер высоты карточки.
На последок, пожалуй скажу, что не все шутки одинаково приличны. Чтоб не ввести пользователя в конфуз — лучше добавить к URL "?type=nerdy".
Код
Если кому интересно, код целиком
Официальные примеры здесь
ссылка на оригинал статьи http://habrahabr.ru/post/207716/
Добавить комментарий