Top Banner
Objetivos Neste capítulo, você vai: O O Usar o mecanismo de conversão de texto em voz do Android para passar instruções em áudio para o usuário. O O Usar o mecanismo de reconhecimento de fala do An- droid para interpretar entradas de voz do usuário. O O Usar o SMSManager para enviar mensagens de texto. O O Enviar objetos Message para um Handler a fim de ga- rantir que as modificações feitas na interface gráfica do usuário ocorram na thread da interface. Aplicativo PHAB`S Pizza Conversão de texto em voz, reconhecimento de fala e telefonia 15
22

Deitel_Android_15.pdf

Jul 19, 2016

Download

Documents

Marlon Janke
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Deitel_Android_15.pdf

ObjetivosNeste capítulo, você vai:

OO Usar o mecanismo de conversão de texto em voz do Android para passar instruções em áudio para o usuário.

OO Usar o mecanismo de reconhecimento de fala do An-droid para interpretar entradas de voz do usuário.

OO Usar o SMSManager para enviar mensagens de texto.OO Enviar objetos Message para um Handler a fim de ga-rantir que as modificações feitas na interface gráfica do usuário ocorram na thread da interface.

Aplicativo PHAB`S Pizza

Conversão de texto em voz, reconhecimento de fala e telefonia

15

Page 2: Deitel_Android_15.pdf

2 Android para Programadores

15.1 IntroduçãoO aplicativo PHAB`S Pizza (Fig. 15.1), para pedir pizza, usa os mecanismos de conversão de texto em voz e de reconhecimento de fala do Android para se comunicar com o usuário falando textos e recebendo a entrada falada do usuário. O aplicativo cria um pedido de pizza solici-tando ao usuário que responda perguntas sobre o tamanho da pizza e a cobertura. O usuário responde falando ao telefone quando solicitado. Se o aplicativo não conseguir entender o usuário ou receber uma resposta inesperada, ele pede para que repita a resposta. Após pro-cessar as respostas do usuário, o aplicativo resume o pedido, pergunta ao usuário se está tudo certo e se o pedido deve ser enviado. Em caso positivo, o aplicativo envia o pedido para um número de telefone móvel (especificado no arquivo strings.xml do aplicativo) na forma de uma mensagem de SMS, usando as APIs de telefonia do Android. Se o usuário quiser mudar o pedido, o aplicativo reinicia e faz as perguntas novamente. Depois de feito o pedido, o usuário tem a opção de sair do aplicativo ou de recomeçar com um novo pedido.

15.2 Testando o aplicativo para pedir PizzaAbrindo e executando o aplicativoAbra o Eclipse e importe o projeto do aplicativo Pizza. Para importar o projeto:

1. Selecione File > Import… para exibir a caixa de diálogo Import. 2. Expanda o nó General, selecione Existing Projects into Workspace e, em seguida, cli-

que em Next >.

Res

umo 15.1 Introdução

15.2 Teste do aplicativo para pedir Pizza

15.3 Visão geral das tecnologias 15.4 Arquivos da interface gráfica do

usuário e de recursos

15.4.1 Criação do projeto 15.4.2 AndroidManifest.xml

15.4.3 main.xml, strings.xml e arrays.xml

15.5 Construção do aplicativo 15.6 Para finalizar

Fig. 15.1 | Aplicativo para pedir Pizza.

Page 3: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 3

3. À direita do campo de texto Select root directory:, clique em Browse… e, em seguida, localize e selecione a pasta Pizza.

4. Clique em Finish para importar o projeto.

Quando este livro estava sendo produzido, os recursos de síntese e reconhecimento de fala e o recurso de envio de mensagens SMS só funcionavam em aparelhos reais e não no emulador do Android. Além disso, é necessária uma conexão de rede (via plano de dados ou WiFi) para que o reconhecimento de voz funcione. Para usar a funcionalidade de en-vio de mensagens SMS, digite o número de seu telefone móvel no recurso phone_number <string> em strings.xml. Certifique-se de ter conectado em seu computador um apare-lho Android com a depuração de USB habilitada (USB debugging), clique com o botão direito do mouse na pasta do projeto e selecione Run As > Android Application para instalar e executar o aplicativo em seu aparelho.

Escolhendo sua pizzaOuça cada pergunta falada pelo aplicativo – para sua comodidade, as perguntas também aparecem na tela. Responda cada pergunta somente depois que o aplicativo pedir para você falar. Certifique-se de falar claramente no microfone de seu aparelho. Se houver muito ruído de fundo, o aplicativo poderá pedir para que você repita certas respostas.

Enviando um pedidoO aplicativo repetirá o pedido completo e depois perguntará se você deseja realmente fazer o pedido. Diga “yes” para fazer o pedido, o que envia uma mensagem SMS para o número de telefone especificado em seu arquivo strings.xml. Se o número de telefone especificado representar um telefone móvel real, esse telefone receberá uma mensagem de texto SMS detalhando seu pedido; caso contrário, a mensagem SMS não será enviada corretamente.

15.3 Visão geral das tecnologiasSíntese de falaO aplicativo fala com o usuário utilizando uma instância da classe TextToSpeech. O me-canismo de conversão de texto em voz exige inicialização assíncrona. Por isso, o elemento TextToSpeech.OnInitListener do aplicativo é notificado quando essa inicialização termi-na. O método speak de TextToSpeech converte objetos String em mensagens de áudio. Um elemento TextToSpeech.OnUtteranceCompletedListener é notificado quando o sinte-tizador de fala acaba de pronunciar uma mensagem de áudio.

Reconhecimento de falaO aplicativo ouve a entrada do usuário lançando um objeto Intent para o elemento RecognizerIntent, usando a constante RecognizerIntent.ACTION_RECOGNIZE_SPEECH. Usa-mos startActivityForResult para receber os resultados de reconhecimento de fala no método onActivityResult da Activity. Um ArrayList de correspondências possíveis da fala do usuário é incluído como um extra no objeto Intent retornado pelo elemen-to RecognizerIntent e passado para onActivityResult. Comparando os elementos desse ArrayList com as opções existentes no menu de pedidos, podemos determinar a opção escolhida pelo usuário e montar o pedido de forma correspondente.

Page 4: Deitel_Android_15.pdf

4 Android para Programadores

Enviando mensagens SMSQuando um pedido é concluído, o aplicativo envia uma mensagem de texto via progra-mação usando a classe SMSManager. O método estático getDefault de SMSManager retorna o objeto SMSManager que seu aplicativo pode usar para enviar uma mensagem. O método sendTextMessage de SMSManager envia uma mensagem SMS para um número de telefone especificado. Um dos argumentos do método sendTextMessage é um PendingIntent que é transmitido quando a mensagem SMS é enviada. Isso nos permite usar BroadcastReceiver para receber a transmissão a fim de determinar se a mensagem SMS foi enviada com êxito.

Usando um Handler para passar Messages entre threadsComo você sabe, todas as modificações feitas na interface gráfica do usuário devem ser realizadas por meio da thread de execução da interface no Android. Neste aplicativo, outras threads que não são da interface gráfica do usuário precisam notificar a thread da interface para exibir texto. Por exemplo, a síntese de fala ocorre em uma thread de execu-ção separada. Quando a síntese de fala termina e precisamos exibir um texto, notificamos a thread da interface gráfica do usuário passando um objeto Message para um Handler que é criado a partir da thread da interface. O método handleMessage de Handler é chamado na thread que criou o Handler.

15.4 Arquivos da interface gráfica do usuário e de recursosNesta seção, criamos o aplicativo para pedir Pizza e discutimos seus arquivos XML.

15.4.1 Criação do projeto

Comece criando um novo projeto Android chamado Pizza. Especifique os seguintes va-lores na caixa de diálogo New Android Project e, em seguida, pressione Finish:

• Build Target: certifique-se de que Android 2.3.3 esteja marcado • Application name: Pizza • Package name: com.deitel.pizza • Create Activity: Pizza • Min SDK Version: 8

15.4.2 AndroidManifest.xmlA Figura 15.2 mostra o arquivo AndroidManifest.xml deste aplicativo. O único recurso novo é a permissão android.permission.SEND_SMS para enviar mensagens SMS (linha 16).

1 <?xml version="1.0" encoding="utf-8"?>2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"3 package="com.deitel.pizza" android:versionCode="1"4 android:versionName="1.0">5 <application android:icon="@drawable/icon" 6 android:label="@string/app_name" android:debuggable="true">7 <activity android:name=".Pizza" android:screenOrientation="portrait"8 android:label="@string/app_name">9 <intent-filter>

10 <action android:name="android.intent.action.MAIN" />

Fig. 15.2 | AndroidManifest.xml.

Page 5: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 5

15.4.3 main.xml, strings.xml e arrays.xml

O layout main.xml desse aplicativo é composto por um LinearLayout vertical, contendo um componente TextView e um componente ImageView. Exibimos as Strings faladas no componente TextView para que o usuário também possa lê-las. As Strings do aplicativo são definidas como recursos <string> em strings.xml e como recursos <string-array> em arrays.xml. Você pode examinar o conteúdo desses arquivos XML abrindo-os no Eclipse.

15.5 Construção do aplicativoA classe Pizza (Figs. 15.3–15.17) é a única Activity do aplicativo. O aplicativo faz per-guntas para determinar o pedido de pizza desejado pelo usuário e, em seguida, envia o pedido como uma mensagem SMS para um número de telefone especificado como um recurso <string> em strings.xml.

Instrução package, instruções import e campos da classe Pizza ActivityA Figura 15.3 contém a instrução package, as instruções import e os campos da classe Pizza. Realçamos as instruções import das novas classes e interfaces que foram apresen-tadas na Seção 15.3. Discutiremos os campos da classe à medida que forem usados. O método loadResources (Fig. 15.7) inicializa a maioria das variáveis de instância da classe usando recursos XML que carregamos a partir de strings.xml e arrays.xml.

1 // Pizza.java2 // Atividade principal do aplicativo Pizza.3 package com.deitel.pizza;45 import java.util.ArrayList;6 import java.util.HashMap;7 import java.util.Locale;89 import android.app.Activity;

10 import android.app.PendingIntent;11 import android.content.ActivityNotFoundException;1213 import android.content.Context;14 import android.content.Intent;1516 import android.content.res.Resources;17 import android.os.Bundle;18 import android.os.Handler;

import android.content.BroadcastReceiver;

import android.content.IntentFilter;

Fig. 15.3 | Instrução package, instruções import e campos da classe Pizza Activity. (continua)

11 <category android:name="android.intent.category.LAUNCHER" />12 </intent-filter>13 </activity>14 </application>15 <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10"/>16 17 </manifest>

<uses-permission android:name="android.permission.SEND_SMS"/>

Fig. 15.2 | AndroidManifest.xml.

Page 6: Deitel_Android_15.pdf

6 Android para Programadores

19 import android.os.Message;202122232425 import android.widget.TextView;26 import android.widget.Toast;2728 public class Pizza extends Activity 29 {30 private String phoneNumber; // número de telefone para o qual o

// pedido é enviado31

32 // String identificador da Intent de transmissão da mensagem SMS enviada33 private static final String BROADCAST_STRING = 34 "com.deitel.pizza.sent_sms";35 36 // Intent de transmissão (broadcast) da mensagem SMS37 3839 // índice baseado em 0 de cada pergunta sobre a pizza40 private static final int PIZZA_SIZE_INDEX = 1;41 private static final int PEPPERONI_INDEX = 2;42 private static final int MUSHROOM_INDEX = 3;43 private static final int ORDER_SUMMARY_INDEX = 4;44 45 // IDs de mensagem para diferenciar entre uma 46 // mensagem normal e a última mensagem47 private final static int UPDATE_TEXT_ID = 15;48 private final static int FINAL_UPDATE_TEXT_ID = 16;49 private final static int DISPLAY_TOAST_ID = 17;50 51 // identificadores de String para restaurar o estado da instância52 private final static String INDEX_ID = "index";53 private final static String ORDER_ID = "order";54 private final static String LISTENING_ID = "listening";55 56 57 private int currentMessageIndex; // índice da mensagem atual58 59 private boolean waitingForResponse; // esperando resposta do usuário?60 private boolean listening; // esperando resultado da Activity?61 private TextView messageText; // usado para exibir a mensagem atual62 private String order; // o pedido de pizza63 64 private String[] audioMessages; // mensagens faladas pelo aplicativo65 private String[] displayMessages; // mensagens exibidas pelo aplicativo66 67 private String errorMessageString; // mensagem para resposta inesperada68 private String finalMessageString; // mensagem quando o aplicativo

// finaliza o pedido 69

import android.speech.RecognizerIntent; import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.OnInitListener; import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;import android.telephony.SmsManager;

private BroadcastReceiver textMessageStatusBroadcastReceiver;

private TextToSpeech textToSpeech; // converte texto em fala

Fig. 15.3 | Instrução package, instruções import e campos da classe Pizza Activity.

Page 7: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 7

Sobrescrevendo o método onCreate de ActivityO método onCreate (Fig. 15.4) configura a Activity Pizza. As linhas 89–115 criam um novo objeto TextToSpeech e configuram seus receptores (listeners). Usaremos esse objeto para pronunciar comandos e perguntas para o usuário durante o processo de pedido de pizza. O primeiro argumento do construtor de TextToSpeech é o elemento Context no qual o objeto será usado. O segundo argumento é o TextToSpeech.OnInitListener (linhas 90–114) que será notificado quando a inicialização do mecanismo TextToSpeech terminar.

70 // escolhas possíveis para cada uma das cinco opções de pedido71 private String[][] choices = new String[6][];7273 private String positiveResponseString; // "Yes"74 private String negativeResponseString; // "No"75 76 private Resources resources; // usado para acessar os recursos do aplicativo 77 private boolean quitInProgress; 78 79 private HashMap<String, String> ttsParams; // parâmetros de TextToSpeech80

Fig. 15.3 | Instrução package, instruções import e campos da classe Pizza Activity.

81 // Chamado quando a Activity é criada82 @Override83 public void onCreate(Bundle savedInstanceState) 84 {85 super.onCreate(savedInstanceState);86 setContentView(R.layout.main); // configura o layout da Activity87888990919293949596979899100101102103104105106107108109110

// inicializa o mecanismo TextToSpeech e registra o seu OnInitListenertextToSpeech = new TextToSpeech(this,

new OnInitListener() {

// chamado quando TextToSpeech é inicializado @Override

public void onInit(int status) { // fala em inglês dos EUA textToSpeech.setLanguage(Locale.US);

// configura o listener que responde aos eventos gerados // quando as mensagens são concluídas textToSpeech.setOnUtteranceCompletedListener(

new OnUtteranceCompletedListener() { @Override public void onUtteranceCompleted(String id) { utteranceCompleted(); } // fim do método onUtteranceCompleted } // fim da classe anônima interna ); // fim da chamada de setOnUtteranceCompletedListener

Fig. 15.4 | Sobrescrevendo o método onCreate de Activity. (continua)

Page 8: Deitel_Android_15.pdf

8 Android para Programadores

O método onInit de TextToSpeech.OnInitListener é chamado quando a iniciali-zação do objeto TextToSpeech termina. A linha 97 usa o método setLanguage de Text-ToSpeech para especificar que o aplicativo falará em inglês dos Estados Unidos (Locale.US). A classe Locale fornece constantes para muitas localidades, mas não é garantido que todas sejam suportadas em cada aparelho. Você pode usar o método isLanguageAvailable para verificar se um Locale específico está disponível, antes de usá-lo. As linhas 101–110 definem OnUtteranceCompletedListener do objeto TextToSpeech, que é notificado quando o objeto TextToSpeech acaba de pronunciar uma mensagem. Quando isso ocorre, o méto-do onUtteranceCompleted da rotina de tratamento de eventos (handler) (linhas 104–108) chama nosso método utteranceCompleted (Fig. 15.9) para processar esse evento.

As linhas 119–120 criam e configuram o HashMap ttsParams que será usado como último argumento em cada chamada do método speak do objeto TextToSpeech. Para garantir que OnUtteranceCompletedListener seja notificado quando a fala terminar, o HashMap deve conter a chave TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID com um valor que não seja uma string vazia. O valor associado a essa chave é passado para o método onUtteranceCompleted de OnUtteranceCompletedListener e pode ser usado no método para determinar o texto que o mecanismo TTS acabou de falar, para que você possa executar tarefas específicas com base nessa informação. Não usamos o argumento do método onUtteranceCompleted neste aplicativo.

A variável de instância currentMessageIndex (linha 122) mantém o índice de um array de Strings das mensagens e perguntas que o aplicativo fala para o usuário. O valor boolean waitingForResponse indica se o aplicativo está ou não esperando que o usuário responda antes de continuar com o pedido – o aplicativo ainda não falou nenhum texto, de modo que o valor é inicializado como false (linha 123). A linha 128 chama nosso mé-todo loadResources (Fig. 15.7) para carregar os valores String dos arquivos strings.xml e arrays.xml do aplicativo.

111112113114115116117118119120121122 currentMessageIndex = 1; // começa na primeira mensagem123 waitingForResponse = false; // não está esperando pela resposta

// do usuário124125 // obtém o elemento TextView da Activity126 messageText = (TextView) findViewById(R.id.mainText);127128 loadResources(); // carrega recursos String de xml129 } // fim do método onCreate130

playFirstMessage(); } // fim do método onInit } // fim da classe interna anônima que implementa OnInitListener ); // fim da chamada do construtor de TextToSpeech

// usado nas chamadas do método speak de TextToSpeech para garantir que// OnUtteranceCompletedListener seja notificado quando a fala terminarttsParams = new HashMap<String, String>(); ttsParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "speak");

Fig. 15.4 | Sobrescrevendo o método onCreate de Activity.

Page 9: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 9

Sobrescrevendo o método onResume de ActivityQuando o usuário conclui o pedido, o aplicativo pergunta se o pedido deve ser enviado como uma mensagem SMS. Para garantir que a mensagem SMS seja enviada, podemos re-gistrar um BroadcastReceiver para verificar o resultado do Intent que enviou a mensagem. O método onResume (Fig. 15.5) cria e registra o textMessageStatusBroadcastReceiver. Quando o método onReceive de BroadcastReceiver é chamado, verificamos se o código do resultado não é Activity.RESULT_OK (linha 144), no caso em que exibimos uma mensagem de erro no aplicativo. O BroadcastReceiver é notificado de forma assíncrona, de modo que precisamos exibir o erro a partir da thread da interface gráfica do usuário, o que fazemos passando um objeto Message para o método sendMessage de um Handler (linhas 146–148). O viewUpdateHandler está definido na Fig. 15.15 e é usado por toda a Activity Pizza.

131 // chamado quando essa Activity é retomada132 @Override133 public void onResume()134 {135 super.onResume();136137 // cria um BroadcastReceiver para receber transmissão de

// status de mensagem SMS138 textMessageStatusBroadcastReceiver = 139 {140 @Override141 142 {143 // se a mensagem não foi enviada144 if (getResultCode() != Activity.RESULT_OK)145 {146147148149 } // fim do if150 } // fim do método onReceive151 }; // fim da classe interna anônima BroadcastReceiver152153 // registra o listener154155156 } // fim do método onResume157

new BroadcastReceiver()

public void onReceive(Context context, Intent intent)

viewUpdateHandler.sendMessage( viewUpdateHandler.obtainMessage(Pizza.DISPLAY_TOAST_ID, R.string.text_error_message, 0, null));

registerReceiver(textMessageStatusBroadcastReceiver, new IntentFilter(Pizza.BROADCAST_STRING));

Fig. 15.5 | Sobrescrevendo o método onResume de Activity.

O método handleMessage de um Handler é executado na thread a partir da qual o Handler foi criado e recebe o objeto Message enviado pelo método sendMessage do Handler. Como viewUpdateHandler é uma variável de instância da classe Pizza da Activi-ty, viewUpdateHandler é criada na thread de execução da interface gráfica do usuário. Isso nos ajuda a garantir que as modificações feitas na interface gráfica do usuário aconteçam na thread da interface.

O Android mantém um pool global de objetos Message reutilizáveis, de modo que, em vez de criar objetos Message com o construtor padrão, as linhas 147–148 criam o objeto Message passado para viewUpdateHandler chamando o método obtainMessage de Handler. A versão de obtainMessage usada aqui exige quatro argumentos —um identificador inteiro

Page 10: Deitel_Android_15.pdf

10 Android para Programadores

que indica o objetivo do objeto Message (usado para decidir como vai processá-lo), dois valores inteiros arbitrários e um Object arbitrário que pode ser usado no tratamento da mensagem. Em nosso caso, o segundo argumento é um ID de recurso do tipo String para a mensagem de erro que exibiremos. Passamos 0 e null para os dois últimos argumentos, pois não os utilizamos neste aplicativo.

As linhas 154–155 passam o BroadcastReceiver e um novo IntentFilter para o método registerReceiver da Activity para permitir que o aplicativo receba objetos In-tent de transmissão (broadcast). O argumento String do construtor IntentFilter é uma String específica do aplicativo que permite a ele receber as transmissões destinadas ao aplicativo. Quando enviamos a mensagem SMS (Fig. 15.14), nos preparamos para rece-ber um objeto Intent via broadcast com uma String de ação que usa a mesma constante Pizza.BROADCAST_STRING.

Sobrescrevendo o método onPause de ActivityQuando a Activity está em pausa, não há necessidade de receber objetos Intent via broadcast, de modo que sobrescrevemos onPause (Fig. 15.6) para desfazer o registro de nosso BroadcastReceiver, passando-o para o método unregisterReceiver da Activity.

158 // chamado quando essa Activity entra em pausa159 @Override160 public void onPause()161 {162 super.onPause();163164 // se o BroadcastReceiver não for null, desfaz seu registro165 if (textMessageStatusBroadcastReceiver != null)166167168 textMessageStatusBroadcastReceiver = null;169 } // fim do método onPause170

unregisterReceiver(textMessageStatusBroadcastReceiver);

Fig. 15.6 | Sobrescrevendo o método onPause de Activity.

Método loadResources de PizzaO método loadResources (Fig. 15.7) é chamado a partir de onCreate (linha 128 da Fig. 15.4) e carrega os recursos de String do aplicativo usando os métodos getString e getStringAr-ray do objeto Resource da Activity. O array de Strings bidimensional choices contém as respostas possíveis para cada pergunta feita pelo aplicativo. Por exemplo, o array de Strings no índice PEPPERONI_INDEX contém todas as respostas aceitáveis para a pergunta: “Do you want

pepperoni?” – neste caso, “Yes” e “No”. Esses objetos String são carregados no array bina-ryChoices (linhas 194–195) e reutilizados em várias perguntas.

171 // carrega recursos de String a partir do XML172 private void loadResources()173 {174 resources = getResources(); // obtém os recursos do aplicativo175 phoneNumber = resources.getString(176 R.string.phone_number); // carrega o número do telefone

Fig. 15.7 | Método loadResources de Pizza.

Page 11: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 11

Método playFirstMessage de PizzaO método playFirstMessage (Fig. 15.8) é chamado (Fig. 15.4, linha 112) após o mecanismo TextToSpeech ser inicializado. O método fala a mensagem de boas-vin-das do aplicativo (armazenada em audioMessages[0]), chamando o método speak de TextToSpeech com três argumentos – o objeto String a ser falado, o modo de enfi-leiramento e um HashMap de parâmetros para o mecanismo TextToSpeech. O modo de enfileiramento pode ser TextToSpeech.QUEUE_FLUSH ou TextToSpeech.QUEUE_ADD. O modo QUEUE_FLUSH esvazia a fila de fala (a lista de objetos String que estão esperando para serem falados) para que o novo objeto String possa ser falado imediatamente. O modo QUEUE_ADD adiciona o novo texto a ser falado no final da fila de fala.

Método utteranceCompleted de PizzaO método utteranceCompleted (Fig. 15.9) é chamado pela rotina de tratamento de even-to (handler) onUtteranceCompleted do objeto TextToSpeech (Fig. 15.4, linhas 104–108) e

177 audioMessages = resources.getStringArray(178 R.array.audio_messages); // carrega mensagens de áudio179 displayMessages = resources.getStringArray(180 R.array.display_messages); // carrega as mensagens de exibição181 errorMessageString = resources.getString(182 R.string.error_message); // mensagem de erro183 finalMessageString = resources.getString(184 R.string.final_message); // mensagem final185 positiveResponseString = resources.getString(186 R.string.positive_response); // "Yes"187 negativeResponseString = resources.getString(188 R.string.negative_response); // "No"189190 // inicializa o pedido de pizza191 order = resources.getString(R.string.initial_order); 192193 // carrega as respostas válidas do usuário194 String[] binaryChoices = 195 resources.getStringArray(R.array.binary_choices);196 choices[PIZZA_SIZE_INDEX] = 197 resources.getStringArray(R.array.size_choices); 198 choices[PEPPERONI_INDEX] = binaryChoices;199 choices[MUSHROOM_INDEX] = binaryChoices;200 choices[ORDER_SUMMARY_INDEX] = binaryChoices;201 } // fim do método loadResources202

Fig. 15.7 | Método loadResources de Pizza.

203 // fala a primeira mensagem 204 private void playFirstMessage()205 {206 // fala a primeira mensagem207208209 } // fim do método playFirstMessage210

textToSpeech.speak( audioMessages[0], TextToSpeech.QUEUE_FLUSH, ttsParams);

Fig. 15.8 | Método playFirstMessage de Pizza.

Page 12: Deitel_Android_15.pdf

12 Android para Programadores

quando o aplicativo precisa passar para a próxima mensagem a ser falada. Primeiramen-te, obtemos do objeto ttsParams o valor da chave TextToSpeech.Engine.KEY_PARAM_UT-TERANCE_ID para podermos determinar se o usuário resolveu fechar o aplicativo (linhas 220–225). Se isso aconteceu, encerramos (com shutDown) o mecanismo TextToSpeech para liberar seus recursos e encerrar o aplicativo chamando o método finish da Activity.

Em seguida, determinamos se o pedido foi concluído (linhas 228–229). Em caso positivo, chamamos o método allowUserToQuit para permitir que o usuário encerre o aplicativo ou inicie um novo pedido. Se não estivermos esperando uma resposta do

211 // método utilitário chamado quando a fala termina e 212 // quando é hora de passar para a próxima mensagem213 private void utteranceCompleted()214 {215 // se TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID 216 // contém “quit”, termina o aplicativo217 String quit = 218 ttsParams.get(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID);219220 if (quit.equals("quit")) // verifica se o usuário deseja encerrar221 {222223 finish();224 return;225 } // fim do if226227 // permite que o usuário encerre228 if (currentMessageIndex >= displayMessages.length && 229 !quitInProgress) 230 {231 allowUserToQuit();232 } // fim do if 233 else if (!waitingForResponse) // se não estamos esperando uma resposta234 {235 // atualiza TextView236237238239 String words = ""; 240241 // resume o pedido242 if (currentMessageIndex == ORDER_SUMMARY_INDEX) 243 {244 words = resources.getString(R.string.order_summary_prefix);245 words += order.substring(order.indexOf(':') + 1);246 } // fim do if247248 words += audioMessages[currentMessageIndex]; // próxima mensagem249 words = words.replace(resources.getString(R.string.pepperoni), 250 resources.getString(R.string.pepperoni_speech));251 words = words.replace(resources.getString(R.string.pizza), 252 resources.getString(R.string.pizza_speech));253

textToSpeech.shutdown(); // encerra TextToSpeech

viewUpdateHandler.sendMessage( viewUpdateHandler.obtainMessage(UPDATE_TEXT_ID));

Fig. 15.9 | Método utteranceCompleted de Pizza.

Page 13: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 13

usuário (linha 233), passamos um objeto Message para viewUpdateHandler a fim de que ele possa atualizar o texto de TextView. As linhas 239–252 configuram a String words, que conterá a representação de String das palavras a serem faladas para o usuário. Se estivermos na última das mensagens que o aplicativo fala para o usuário (linha 242), as linhas 244–245 resumem o pedido. A linha 248 anexa o objeto String atual do ar-ray audioMessages em words. As linhas 249–250 substituem as palavras “pepperoni” e “pizza” por strings que permitem ao mecanismo TextToSpeech falar essas palavras com uma pronúncia melhor – como “pehperohnee” para “pepperoni.” Em seguida, a linha 255 fala a mensagem através do método speak de TextToSpeech. Também configuramos waitingForResponse como true. Se estivermos esperando uma resposta do usuário (linha 258), chamamos o método listen (Fig. 15.10) para iniciar um Intent para a Activity de reconhecimento de fala.

Método listen de PizzaO método listen (Fig. 15.10) usa um objeto Intent (270–271) para iniciar uma Acti-vity que recebe entrada de áudio do usuário. A constante RecognizerIntent.ACTION_RE-COGNIZE_SPEECH representa a Activity de reconhecimento de fala. Ativamos o objeto In-tent usando startActivityForResult (linha 276) para que possamos receber resultados no método sobrescrito onActivityResult da Activity Pizza. Capturamos uma exceção ActivityNotFoundException que será lançada por um AVD ou por qualquer equipamento que não tenha recursos de reconhecimento de fala. Se isso acontecer, enviamos uma mensagem para viewUpdateHandler a fim de exibir um Toast explicando por que esse apli-cativo não funcionará.

254 // fala a próxima mensagem255256 waitingForResponse = true; // estamos esperando uma resposta257 } // fim do if258 else if (!listening && currentMessageIndex > 0)259 {260 listen(); // captura a resposta do usuário261 } // fim do else if262 } // fim do método utteranceCompleted263

textToSpeech.speak(words, TextToSpeech.QUEUE_FLUSH, ttsParams);

Fig. 15.9 | Método utteranceCompleted de Pizza.

264 // recebe a resposta do usuário265 private void listen() 266 {267 listening = true; // agora estamos escutando268269 // cria um Intent para Activity de reconhecimento de fala270271272273 // tenta ativar a Activity de reconhecimento de fala274 try275 {

Intent speechRecognitionIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

Fig. 15.10 | Método listen de Pizza. (continua)

Page 14: Deitel_Android_15.pdf

14 Android para Programadores

Sobrescrevendo o método onActivityResult de ActivityA Activity Pizza sobrescreve o método onActivityResult (Fig. 15.11) para processar os resultados da Activity de reconhecimento de fala. Passamos RecognizerIntent.EXTRA_RE-SULTS para o método getStringArrayListExtra do Intent recebido (linhas 296–298) a fim de obter um ArrayList contendo representações de String das interpretações da Activity de reconhecimento de fala da entrada falada pelo usuário. O reconhecimento de fala não é exato; portanto, se qualquer um desses objetos String coincidir com uma resposta esperada pelo aplicativo, assumiremos que o usuário falou essa resposta e agir de forma correspon-dente. As linhas 316–327 iteram pelas escolhas válidas, comparando-as com cada uma das correspondências possíveis para a entrada de fala do usuário. Salvamos a primeira corres-pondência em result (linha 323). Se não houver correspondência alguma, chamamos o método playError para pedir ao usuário que repita a resposta (linha 330). Caso contrário, as linhas 331–418 processam a resposta do usuário. As linhas 331–371 encerram ou conti-nuam o aplicativo. As linhas 373–387 enviam o pedido de pizza ou começam de novo. As linhas 388–412 continuam o processo de pedido – chamamos o método utteranceComple-ted (linha 411) com o objeto String vazio para falar a próxima mensagem para o usuário. As linhas 414–418 processam o caso no qual o usuário cancela a entrada de fala.

276277 } // fim do try278 catch (ActivityNotFoundException exception) 279 {280281282 } // fim do catch283 } // fim do método listen284

startActivityForResult(speechRecognitionIntent, 0);

viewUpdateHandler.sendMessage(viewUpdateHandler.obtainMessage( Pizza.DISPLAY_TOAST_ID, R.string.no_speech_message, 0, null));

Fig. 15.10 | Método listen de Pizza.

285 // chamado quando a Activity de reconhecimento de fala retorna286 @Override287 protected void onActivityResult(int requestCode, int resultCode, 288 Intent dataIntent) 289 {290 listening = false;291292 // se não houve erro algum293 if (requestCode == 0 && resultCode == RESULT_OK) 294 {295 // obtém a lista das correspondências possíveis para a

// fala do usuário296297298299300 // obtém a lista atual de escolhas válidas possíveis301 String[] validResponses;302303 if (!quitInProgress) 304 validResponses = choices[currentMessageIndex];

ArrayList<String> possibleMatches = dataIntent.getStringArrayListExtra( RecognizerIntent.EXTRA_RESULTS);

Fig. 15.11 | Sobrescrevendo o método onActivityResult de Activity.

Page 15: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 15

305 else 306 validResponses = 307 resources.getStringArray(R.array.binary_choices);308309 if (validResponses == null) 310 return;311 312 String result = null;313314 // para cada escolha válida possível, compara com a fala do usuário 315 // para determinar se ele pronunciou uma dessas escolhas316 checkForMatch: 317 for (String validResponse : validResponses)318 {319 for (String match : possibleMatches) 320 {321 if (validResponse.compareToIgnoreCase(match) == 0) 322 {323 result = validResponse; // armazena a resposta do usuário324 break checkForMatch; // para de verificar as respostas possíveis325 } // fim do if326 } // fim do for327 } // fim do for328329 if (result == null) // não houve correspondência alguma330 playError(); // pede para o usuário repetir a resposta331 else if (quitInProgress) 332 {333 quitInProgress = false;334 335 // o usuário disse para encerrar336 if (result.equalsIgnoreCase(positiveResponseString)) 337 {338 if (currentMessageIndex >= displayMessages.length) 339 {340 reset(); // redefine o pedido341 return; // retorna342 } // fim do if343 else 344 {345 346 347 348 // fala a mensagem final349 350 351 352 } // fim do else353 } // fim do if354 else // o usuário quer retornar355 {356 if (currentMessageIndex >= displayMessages.length) 357 {

ttsParams.put( TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "quit");

textToSpeech.speak( resources.getString(R.string.quit_message), TextToSpeech.QUEUE_FLUSH, ttsParams);

Fig. 15.11 | Sobrescrevendo o método onActivityResult de Activity. (continua)

Page 16: Deitel_Android_15.pdf

16 Android para Programadores

358359360361 // fala a mensagem final362363364365 } // fim do if366 else 367 {368 listen(); 369 } // fim do else370 } // fim do else371 } // fim do else if372 // não houve correspondência e essa é a última mensagem373 else if (currentMessageIndex == displayMessages.length - 1) 374 {375 // o usuário disse para enviar o pedido376 if (result.equalsIgnoreCase(positiveResponseString)) 377 {378 waitingForResponse = false; 379 ++currentMessageIndex;380 sendMessage(); // envia o pedido como uma mensagem de texto381 } // fim do if382 else // o usuário cancelou o pedido383 {384 reset(); // redefine o pedido385 return; // retorna386 } // fim do else387 } // fim do else if388 else // não houve correspondência e essa não é a última mensagem 389 {390 // o usuário respondeu positivamente391 if (result.equalsIgnoreCase(positiveResponseString)) 392 {393 // se a pergunta anterior quis saber se o usuário deseja pepperoni394 if (currentMessageIndex == PEPPERONI_INDEX )395 {396 // acrescenta pepperoni no pedido de pizza397 order += resources.getString(R.string.pepperoni );398 } // fim do if399 else if (currentMessageIndex == MUSHROOM_INDEX )400 {401 // acrescenta cogumelos (mushrooms) no pedido de pizza402 order += resources.getString(R.string.mushrooms );403 } // else if404 } // fim do if405 else if (!result.equalsIgnoreCase(negativeResponseString))406 order += ", " + result; // atualiza o pedido 407408 waitingForResponse = false;409 ++currentMessageIndex; // vai para a próxima pergunta410

ttsParams.put( TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "quit");

textToSpeech.speak( resources.getString(R.string.leave_message), TextToSpeech.QUEUE_FLUSH, ttsParams);

Fig. 15.11 | Sobrescrevendo o método onActivityResult de Activity.

Page 17: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 17

Método playError de PizzaO método playError (Fig. 15.12, linhas 425–429) é chamado por onActivityResult quando o mecanismo de reconhecimento de fala do Android não consegue reconhecer a resposta falada pelo usuário. As linhas 427–428 usam o método speak do objeto text-ToSpeech para pedir que o usuário tente novamente. O método reset (linhas 432–441) é chamado por onActivityResult quando o usuário decide reiniciar o processo de pedido.

424 // chamado quando o usuário pronuncia uma resposta inesperada425 private void playError() 426 {427 428 429 } // fim do método playError430431 // inicia um novo pedido432 private void reset() 433 {434 // reconfigura as variáveis de instância associadas a um pedido435 currentMessageIndex = 1; 436 order = resources.getString(R.string.initial_order);437 waitingForResponse = false;438 listening = false;439 440 playFirstMessage(); 441 } // fim do método reset442

textToSpeech.speak(errorMessageString, // reproduz a mensagem de erro TextToSpeech.QUEUE_FLUSH, ttsParams);

Fig. 15.12 | Métodos playError e reset de Pizza.

Sobrescrevendo os métodos onSaveInstanceState e onRestoreInstanceState de ActivityOs métodos onSaveInstanceState e onRestoreInstanceState da Activity (Fig. 15.13) salvam e restauram os valores das variáveis de instância currentMessageIndex, order e listening da Activity Pizza para o caso da atividade ser colocada em segundo plano e trazida de volta para o primeiro plano.

411 utteranceCompleted(); // passa para a próxima mensagem412 } // fim do else413 } // fim do if414 else if ((currentMessageIndex > 0 && !listening) || 415 resultCode == Activity.RESULT_CANCELED)416 {417 allowUserToQuit(); // ouve a entrada do usuário418 } // fim do else419 420 // chama o método super421 super.onActivityResult(requestCode, resultCode, dataIntent);422 } // fim do método onActivityResult423

Fig. 15.11 | Sobrescrevendo o método onActivityResult de Activity.

Page 18: Deitel_Android_15.pdf

18 Android para Programadores

Método sendMessage de PizzaO método sendMessage (Fig. 15.14) é chamado por onActivityResult para enviar o ob-jeto String de pedido final como uma mensagem de texto SMS. Para fazer isso, criamos um novo Intent (linha 469) com um objeto String de ação correspondente ao que usa-mos para registrar o textMessageStatusBroadcastReceiver. Então, usamos esse Intent para criar um PendingIntent (linhas 470–471), chamando o método estático getBro-adcast de PendingIntent. Lembre-se, do Capítulo 14, que PendingIntent representa um Intent e uma ação a ser executada com esse Intent. Quando PendingIntent termina, ele transmite o Intent especificado como o terceiro argumento de getBroadcast – esse é o Intent que BroadcastReceiver (Fig. 15.5) recebe, indicando se a mensagem SMS foi enviada com sucesso.

A linha 474 obtém o SMSManager chamando o método static getDefault de SMS-Manager. O método sendTextMessage de SMSManager (linhas 477–478) envia a mensagem

443 // salva o estado do pedido444 @Override445 public void onSaveInstanceState(Bundle savedStateBundle)446 {447 // armazena os valores de currentMessageIndex, order e listening448 savedStateBundle.putInt(INDEX_ID, currentMessageIndex); 449 savedStateBundle.putString(ORDER_ID, order); 450 savedStateBundle.putBoolean(LISTENING_ID, listening); 451452 super.onSaveInstanceState(savedStateBundle);453 } // fim do método onSaveInstanceState454455 // restaura o estado do pedido456 @Override457 public void onRestoreInstanceState(Bundle savedStateBundle)458 {459 // recupera os valores de currentMessageIndex, order e listening460 currentMessageIndex = savedStateBundle.getInt(INDEX_ID); 461 order = savedStateBundle.getString(ORDER_ID); 462 listening = savedStateBundle.getBoolean(LISTENING_ID);463 super.onRestoreInstanceState(savedStateBundle);464 } // fim do método onRestoreInstanceState465

Fig. 15.13 | Sobrescrevendo os métodos onSaveInstanceState e onRestoreInstanceState de Activity.

466 // envia o pedido como uma mensagem de texto467 private void sendMessage() 468 {469470471472473 // obtém o SMSManager padrão474475

Intent broadcastIntent = new Intent(Pizza.BROADCAST_STRING);PendingIntent messageSentPendingIntent = PendingIntent.getBroadcast(this, 0, broadcastIntent, 0);

SmsManager smsManager = SmsManager.getDefault();

Fig. 15.14 | Método sendMessage de Pizza.

Page 19: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 19

SMS. O primeiro argumento é o número de telefone para o qual a mensagem será en-viada. O segundo argumento, null, indica que a central de SMS padrão (SMSC) deve ser usada para encaminhar a mensagem SMS ao seu destino. O terceiro argumento é a mensagem a ser enviada. O PendingIntent no quarto argumento é transmitido quando a mensagem é enviada – o código resultante de PendingIntent indicará se o envio da mensagem SMS foi bem-sucedido ou falhou. O último argumento (se não for null) é outro PendingIntent, que é transmitido quando a mensagem SMS é entregue para o destinatário. As linhas 481–482 enviam um objeto Message para viewUpdateHandler a fim de exibir uma mensagem de pedido concluído para o usuário e para falar essa mensagem.

viewUpdateHandler para atualizar a interface gráfica do usuárioO viewUpdateHandler (Fig. 15.15) é chamado em toda a Activity Pizza para atualizar a interface gráfica do usuário de acordo com o estado atual do pedido e para exibir mensa-gens de erro. As linhas 489–519 sobrescrevem o método handleMessage de Handler, o qual recebe um objeto Message como argumento e atualiza a interface gráfica do usuário com base no conteúdo desse objeto Message. As linhas 492–518 processam o objeto Message com base no identificador contido em receivedMessage.what. Para Pizza.UPDATE_TEXT_ID, exibimos a próxima mensagem em displayMessages, para que o usuário possa ver o mesmo texto que o aplicativo está falando. Para Pizza.FINAL_UPDATE_TEXT_ID, exibimos e falamos finalMessageString. Para Pizza.DISPLAY_TOAST_ID, exibimos um Toast contendo o valor que foi armazenado na variável de instância arg1 de Message quando o objeto Mes-sage foi enviado – essa variável de instância contém o texto a ser exibido no Toast.

476 // envia o pedido para PHONE_NUMBER477478479480 // exibe a mensagem final481 viewUpdateHandler.sendMessage(482 viewUpdateHandler.obtainMessage(FINAL_UPDATE_TEXT_ID)); 483 } // fim do método sendMessage484

smsManager.sendTextMessage(phoneNumber, null, order, messageSentPendingIntent, null);

Fig. 15.14 | Método sendMessage de Pizza.

485 // atualiza a interface do usuário486 private Handler viewUpdateHandler = new Handler() 487 {488 // exibe a próxima mensagem dada489 490 {491 // processa a Message com base no identificador armazenado

// em receivedMessage.what492 switch )( 493 {494 case Pizza.UPDATE_TEXT_ID: // se não for a última mensagem495 // exibe a mensagem496 String text = "";497

public void handleMessage(Message receivedMessage)

receivedMessage.what

Fig. 15.15 | viewUpdateHandler para atualizar a interface gráfica do usuário. (continua)

Page 20: Deitel_Android_15.pdf

20 Android para Programadores

Método allowUserToQuit de PizzaO método allowUserToQuit (Fig. 15.16) é chamado pelos métodos utteranceCompleted e onActivityResult para perguntar se o usuário deseja sair do aplicativo Pizza. Se já con-cluímos um pedido (linha 529), perguntamos ao usuário se deseja sair do aplicativo ou começar outro pedido (linhas 531–533); caso contrário, perguntamos se ele deseja sair ou continuar o pedido atual.

522 // permite ao usuário sair do aplicativo523 private void allowUserToQuit() 524 {525 quitInProgress = true;526 waitingForResponse = true;527 528 // se o pedido estiver concluído, pergunta se vai sair

// ou iniciar um novo pedido529 if (currentMessageIndex >= displayMessages.length) 530 {531 532 533 534 } // fim do if535 else // pergunta se vai sair ou continuar o pedido536 {

textToSpeech.speak( resources.getString(R.string.leave_question), TextToSpeech.QUEUE_FLUSH, ttsParams);

Fig. 15.16 | Método allowUserToQuit de Pizza.

498 // se a próxima mensagem é a última 499 if (currentMessageIndex == displayMessages.length - 1)500 text = order;501502 text += displayMessages[currentMessageIndex];503 messageText.setText(text);504 break;505 case Pizza.FINAL_UPDATE_TEXT_ID: // se o pedido estiver concluído506 // exibe e reproduz a mensagem final507 messageText.setText(finalMessageString);508509 // fala a mensagem final510511512 break;513 case DISPLAY_TOAST_ID:514 // se o reconhecimento de fala não está disponível nesse aparelho515 // informa o usuário utilizando um Toast516 Toast.makeText(Pizza.this , ,517 Toast.LENGTH_LONG).show();518 } // fim da instrução switch519 } // fim do método handleMessage520 }; // fim do Handler521

textToSpeech.speak(finalMessageString, TextToSpeech.QUEUE_FLUSH, ttsParams);

receivedMessage.arg1

Fig. 15.15 | viewUpdateHandler para atualizar a interface gráfica do usuário.

Page 21: Deitel_Android_15.pdf

Capítulo 15 Aplicativo PHAB`S Pizza 21

Sobrescrevendo o método onDestroy de ActivityO método onDestroy (Fig. 15.17) é chamado quando essa Activity é destruída. Cha-mamos o método shutdown de TextToSpeech para liberar os recursos nativos do Android usados pelo mecanismo TextToSpeech.

543 // quando o aplicativo é encerrado544 @Override545 public void onDestroy() 546 {547 super.onDestroy(); // chama o método super548 549 } // fim do método onDestroy550 } // fim da classe Pizza

textToSpeech.shutdown(); // encerra o TextToSpeech

Fig. 15.17 | Sobrescrevendo o método onDestroy de Activity.

15.6 Para finalizarO aplicativo PHAB`S Pizza usou os mecanismos de conversão de texto em voz e de reconhe-cimento de fala do Android para se comunicar com o usuário falando texto e recebendo a entrada falada pelo usuário. Quando um pedido era concluído, o aplicativo o enviava para um número de telefone celular como uma mensagem SMS, usando as APIs de tele-fonia do Android.

O aplicativo usou um objeto TextToSpeech para falar texto. Como o mecanismo de conversão de texto em voz é inicializado de forma assíncrona, você usou TextToSpe-ech.OnInitListener para que o aplicativo pudesse ser notificado quando a inicialização terminava. Você converteu texto em mensagens faladas chamando o método speak de TextToSpeech e determinou como proceder no aplicativo quando a fala era concluída implementando TextToSpeech.OnUtteranceCompletedListener. Você recebeu entrada do usuário ativando um RecognizerIntent com a constante ACTION_RECOGNIZE_SPEECH e, en-tão, respondeu aos resultados do reconhecimento de fala no método onActivityResult da Activity Pizza. O RecognizerIntent retornava um ArrayList de possíveis correspondên-cias para a fala do usuário. Comparando os elementos desse ArrayList com as opções de pedido do aplicativo, você determinou qual opção foi escolhida pelo usuário e processou o pedido de forma correspondente.

Quando um pedido foi concluído, você enviou uma mensagem SMS via progra-mação, com o SMSManager que você obteve com o método estático getDefault de SMS-Manager. Você enviou a mensagem SMS chamando o método sendTextMessage de SMS-Manager. Usou um PendingIntent para receber uma notificação indicando se a mensagem SMS foi enviada com sucesso e tratou a notificação com um BroadcastReceiver.

537538539540 } // fim do else541 } // fim do método allowUserToQuit542

textToSpeech.speak( resources.getString(R.string.quit_question), TextToSpeech.QUEUE_FLUSH, ttsParams);

Fig. 15.16 | Método allowUserToQuit de Pizza.

Page 22: Deitel_Android_15.pdf

22 Android para Programadores

Para garantir que todas as modificações feitas na interface gráfica do usuário fossem realizadas a partir da thread de execução da interface, você passou um objeto Message para um Handler criado a partir da thread da interface. O método handleMessage do Han-dler foi chamado na thread que criou o Handler – neste aplicativo, a thread da interface gráfica do usuário.

No Capítulo 16, apresentaremos o aplicativo Voice Recorder, que permite ao usuá-rio gravar sons usando o microfone do telefone e salvar os arquivos de áudio para repro-dução posterior.