Чак Норрис и Google Glass — что общего, а вот…

от автора

Недавно мне (несказанно поперло) выпал шанс купить Google Glass по Explorers Program. Стоило только заказать, как на следующее утро почтальон разбудил меня стуком в деверь — вам посылка. Не умываясь и не чистя зубы…

Под вечер возникла возможность попрограммировать. Изучив пару-тройку примеров и настроив окружение — я взялся за дело. Возникла идея написать приложение которое будет вытаскивать шутку про Чака Норриса из веб-сервиса, парсить 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *