O Usar Bluetooth para estabelecer uma conexo peer-to- -peer entre dois aparelhos e transferir informaes de um para outro. O Usar a classe BluetoothAdapter para verificar se o Bluetooth est habilitado em um aparelho. O Iniciar um Intent que pede permisso ao usurio para habilitar o Bluetooth, se ainda no estiver habilitado. O Iniciar um Intent que pede permisso ao usurio para tornar um aparelho detectvel via Bluetooth, podendo receber uma conexo. O Usar a classe BluetoothServerSocket para esperar uma conexo de outro aparelho. O Usar a classe BluetoothDevice para estabelecer uma conexo com outro aparelho. O Usar a classe BluetoothSocket para transmitir dados entre aparelhos. O Usar objetos JSONObject para criar e analisar dados formatados em JSON. Aplicativo Address Book melhorado Conectando dispositivos via Bluetooth, usando JSONObject para criar e analisar dados formatados em JSON 17 2 Android para Programadores 17.1 Introduo O aplicativo Enhanced Address Book adiciona verso do aplicativo do Captulo 10 a capa- cidade de os usurios transferirem contatos entre aparelhos usando redes Bluetooth. Voc vai usar vrias classes do pacote android.bluetooth para determinar se o Bluetooth est ha- bilitado e para estabelecer uma conexo entre dois aparelhos. Uma vez estabelecida a co- nexo, voc vai obter um OutputStream no aparelho remetente para enviar os dados e um InputStream no aparelho receptor para l-los. Os dados sero transferidos no formato JSON voc vai formatar os dados enviados e analisar (via parsing) os dados recebidos usando objetos JSONObject. Tambm vai usar objetos Intent para disparar atividades internas do Android que pedem ao usurio permisso para habilitar o adaptador Bluetooth de um aparelho e para tornar o aparelho detectvel por outros aparelhos, para que uma conexo possa ser estabeleci- da. Para mais detalhes sobre desenvolvimento de Bluetooth no Android visite: http://developer.android.com/guide/topics/wireless/bluetooth.html 17.2 Teste do aplicativo Enhanced Address Book [Observao: voc deve testar este aplicativo com dois aparelhos, pois o emulador do Android no suportava Bluetooth quando este livro foi produzido.] Abrindo e executando o aplicativo Abra o Eclipse e importe o projeto do aplicativo Enhanced Address Book. Para importar o projeto: 1. Selecione File > Import para exibir a caixa de dilogo Import. 2. Expanda o n General, selecione Existing Projects into Workspace e, em seguida, cli- que em Next >. 3. direita do campo de texto Select root directory:, clique em Browse e, em seguida, localize e selecione a pasta Enhanced Address Book. 4. Clique em Finish para importar o projeto. 5. Ative o aplicativo Enhanced Address Book. No Eclipse, clique com o boto direito do mouse no projeto EnhancedAddressBook na janela Package Explorer e, em se- guida, selecione Run As > Android Application no menu que aparece. Voc precisar repetir esse passo para cada aparelho em que executar este aplicativo para teste. R e s u m o 17.1 Introduo 17.2 Teste do aplicativo Enhanced Address Book 17.3 Viso geral das tecnologias 17.4 Construo dos arquivos de interface do usurio e de recursos 17.4.1 Criao do projeto 17.4.2 AndroidManifest.xml 17.4.3 addressbook_menu.xml: menu da Activity AddressBook 17.4.4 view_contact_menu.xml: menu da Activity ViewContact 17.4.5 device_chooser.xml: layout da ListActivity DeviceChooser 17.4.6 device_layout.xml: layout dos itens de ListView do ListView da ListActivity DeviceChooser 17.5 Construo do aplicativo 17.5.1 Activity AddressBook atualizada 17.5.2 Activity ViewContact atualizada 17.5.3 Activity DeviceChooser 17.6 Para finalizar Captulo 17 Aplicativo Address Book melhorado 3 Preparando um aparelho para receber um contato Para receber um contato de outro aparelho que esteja executando o aplicativo Enhanced Address Book, abra o menu enquanto v a lista de contatos e, em seguida, toque no item de menu Receive Contact do aparelho (Fig. 17.1(a)). Uma caixa de dilogo pede sua per- misso para tornar o aparelho detectvel. Toque em Yes para torn-lo detectvel por 120 segundos (Fig. 17.1(b)). Agora, outro aparelho poder se conectar com esse e enviar um contato. Quando um contato for recebido, aparecer um Toast no aparelho receptor e o novo contato ser exibido na lista de contatos. a) Toque em Receive Contact no menu para receber um contato de outro aparelho. b) Toque em Yes na caixa de dilogo para tornar o aparelho detectvel e permitir que ele receba um contato. Fig. 17.1 | A seleo do item de menu Receive Contact no aparelho receptor faz o Android perguntar se o aparelho deve ficar detectvel por 120 segundos. Enviando um contato Para enviar um contato para outro aparelho que esteja executando o aplicativo Enhan- ced Address Book, primeiramente selecione o contato que voc deseja enviar, para ver seus detalhes. Na tela de detalhes do contato, abra o menu e toque em Transfer Contact (Fig. 17.2). Isso exibe a Activity DeviceChooser (Fig. 17.3), a qual procura imediata- mente aparelhos Bluetooth prximos se o Bluetooth estiver habilitado no aparelho que ativou DeviceChooser. medida que aparelhos so descobertos, eles aparecem no ListView da Activity DeviceChooser. Voc tambm pode recomear a busca de ou- tros aparelhos, tocando no boto Scan for compatible devices, na parte inferior da tela. Quando vir outro aparelho que esteja executando o aplicativo Enhanced Address Book, toque no nome do aparelho a fim de enviar o contato para ele. Os dois aparelhos preci- sam estar executando o aplicativo para que a transferncia ocorra. A Figura 17.4 mostra a lista de contatos do aparelho receptor antes e depois que o contato recebido. 4 Android para Programadores Toque em Transfer Contact para enviar as informaes desse contato para outro aparelho Fig. 17.2 | Selecionando o item de menu Transfer Contact no aparelho remetente. Toque no nome de um aparelho para transferir um contato para ele Toque neste boto para procurar aparelhos prximos novamente Fig. 17.3 | Lista de aparelhos Bluetooth prximos exibida no aparelho remetente. Captulo 17 Aplicativo Address Book melhorado 5 17.3 Viso geral das tecnologias Esta seo apresenta as novas tecnologias utilizadas no aplicativo Enhanced Address Book. As classes Bluetooth esto localizadas no pacote android.bluetooth. Obtendo uma referncia para o adaptador Bluetooth Neste aplicativo, usamos o mtodo esttico getDefaultAdapter de BluetoothAdapter para obter um objeto que representa o adaptador Bluetooth do aparelho. Voc vai usar esse objeto para determinar se o Bluetooth est habilitado, para captar conexes de outros aparelhos e para localizar outros aparelhos Bluetooth. Detectando outros aparelhos Bluetooth Para procurar aparelhos Bluetooth prximos, voc vai usar o mtodo startDiscovery de BluetoothAdapter. O Android executar a tarefa de descoberta para voc e o notificar quando forem encontrados aparelhos e quando a tarefa de descoberta estiver concluda. Recebendo objetos Intent via broadcast para aparelhos descobertos e concluso de descoberta Para obter os resultados da descoberta, voc se registrar para receber dois objetos Intent via broadcast: O objeto Intent da ao BluetoothAdapter.ACTION_FOUND transmitido para cada aparelho Bluetooth descoberto. O Intent inclui como extra um objeto Bluetooth- Device representando um aparelho com capacidade para Bluetooth. O objeto Intent da ao BluetoothAdapter.ACTION_DISCOVERY_FINISHED transmi- tido quando a busca de aparelhos est concluda. Contato recentemente recebido a) Lista de contatos antes de receber o novo contato b) Lista de contatos aps receber o novo contato Fig. 17.4 | Contatos do aparelho receptor antes e depois de receber o novo contato. 6 Android para Programadores Captando conexes Voc vai usar uma thread separada para captar pedidos de conexo recebidos de outros apa- relhos que estejam executando o aplicativo Enhanced Address Book. Nessa thread, voc vai chamar o mtodo listenUsingRfcommWithServiceRecord de BluetoothAdapter, o qual retor- na um BluetoothServerSocket que pode ser usado para comear a captar conexes. O m- todo accept de BluetoothServerSocket bloqueia a thread a partir da qual chamado e capta os pedidos de conexo Bluetooth recebidos. Quando uma conexo recebida, retornado um objeto BluetoothSocket. Esse objeto contm um elemento InputStream e um elemento OutputStream para comunicao com o outro aparelho. Conforme voc ver, o mtodo listenUsingRfcommWithServiceRecord recebe como argumento um identificador exclusivo que outros aparelhos devem usar para se conectar com este aplicativo no aparelho receptor. Conectando-se com outro aparelho Para se conectar com outro aparelho, voc vai usar o mtodo createRfcommSocket- ToServiceRecord da classe BluetoothDevice, o qual retorna um BluetoothSocket para comunicao com o aparelho remoto. Esse mtodo exibe como argumento um iden- tificador globalmente exclusivo correspondente ao passado para o mtodo listenUsing- RfcommWithServiceRecord de BluetoothAdapter isso ajuda a garantir que a conexo seja estabelecida somente com outro aparelho que esteja executando o aplicativo Enhanced Address Book. Se o BluetoothSocket for recuperado, chamamos seu mtodo connect para abrir uma conexo com o aparelho remoto. Transferindo um contato Depois que estabelecemos uma conexo, usamos o BluetoothSocket para obter um InputStream no aparelho receptor para ler o contato e um OutputStream no aparelho remetente para enviar o contato. O mtodo read de InputStream usado para obter o contato recebido do aparelho remoto. Para enviar um contato, passamos para o mtodo send de OutputStream um buffer de bytes representando as informaes desse contato. Usando objetos JSONObject para transmitir e receber dados Usamos um JSONObject (pacote org.json) para formatar os dados de um contato como um objeto String a fim de enviar e extrair os dados do contato no aparelho receptor. 17.4 Construo dos arquivos de interface do usurio e de recursos Nesta seo voc vai criar os arquivos de layout da interface grfica do usurio do aplicativo Enhanced Address Book e modificar alguns daqueles que criou no aplicativo Address Book do Captulo 10. Nas subsees a seguir, mostramos somente os arquivos novos e alterados. 17.4.1 Criao do projeto Comece excluindo de seu espao de trabalho Eclipse o projeto EnhancedAddressBook que foi testado na Seo 17.2. Para fazer isso, no Package Explorer, clique com o boto direito do mouse no projeto e selecione Delete. Na caixa de dilogo que aparece, certifique-se de que Delete project contents on disk no esteja marcado e, em seguida, clique em OK. En- to, no sistema de arquivos de seu computador, copie a pasta AddressBook do aplicativo Captulo 17 Aplicativo Address Book melhorado 7 Address Book, mude o nome da cpia para EnhancedAddressBook e, em seguida, importe o projeto EnhancedAddressBook para seu espao de trabalho usando os passos da Seo 17.2. 17.4.2 AndroidManifest.xml A Figura 17.5 mostra o arquivo AndroidManifest.xml deste aplicativo. Existem trs itens novos nesse arquivo. As linhas 67 especificam dois elementos <uses-permission>: android.permission.BLUETOOTH (linha 6) permite que o aplicativo se conecte com ou- tros aparelhos com Bluetooth habilitado que estejam conectados com esse aparelho. android.permission.BLUETOOTH_ADMIN (linha 7) permite que o aplicativo descubra outros aparelhos com Bluetooth habilitado e se conecte com eles. As linhas 2223 especificam um elemento <activity> para a nova Activity DeviceChooser. 1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.deitel.addressbook" android:versionCode="1" 4 android:versionName="1.0"> 5 <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10"/> 6 7 8 9 <application android:icon="@drawable/icon" 10 android:label="@string/app_name"> 11 <activity android:name=".AddressBook" 12 android:label="@string/app_name"> 13 <intent-filter> 14 <action android:name="android.intent.action.MAIN" /> 15 <category android:name="android.intent.category.LAUNCHER" /> 16 </intent-filter> 17 </activity> 18 <activity android:name=".AddEditContact" 19 android:label="@string/app_name"></activity> 20 <activity android:name=".ViewContact" 21 android:label="@string/app_name"></activity> 22 23 24 </application> 25 </manifest> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <activity android:name=".DeviceChooser" android:label="@string/app_name"></activity> Fig. 17.5 | AndroidManifest.xml. 17.4.3 addressbook_menu.xml: menu da Activity AddressBook A Figura 17.6 mostra o menu de AddressBook atualizado. O item receiveContactItem (linhas 812) permite ao usurio dizer ao aplicativo para que receba um contato de outro aparelho. Selecionar esse item faz com que o aplicativo pergunte ao usurio se pode tornar esse aparelho detectvel. Se o usurio disser que sim, ento o aplicativo tentar captar uma conexo de outro aparelho. Se receber uma, ler o contato do outro aparelho. 1 <?xml version="1.0" encoding="utf-8"?> 2 <menu xmlns:android="http://schemas.android.com/apk/res/android"> 3 <item android:id="@+id/addContactItem" Fig. 17.6 | Menu da Activity AddressBook. 8 Android para Programadores 17.4.4 view_contact_menu.xml: menu da Activity ViewContact A Figura 17.7 mostra o menu atualizado da Activity ViewContact. O item de menu transferItem (linhas 1317) permite ao usurio enviar um contato para outro aparelho. Selecionar esse item de menu faz com que o aplicativo exiba a Activity DeviceChooser (Seo 17.5.3), a qual procura e exibe uma lista de aparelhos Bluetooth prximos. Ento, o usurio pode escolher o aparelho para o qual vai transferir o contato, o que faz a Acti- vity ViewContact enviar o contato em uma thread separada. 1 <?xml version="1.0" encoding="utf-8"?> 2 <menu xmlns:android="http://schemas.android.com/apk/res/android"> 3 <item android:id="@+id/editItem" 4 android:title="@string/menuitem_edit_contact" 5 android:orderInCategory="1" android:alphabeticShortcut="e" 6 android:titleCondensed="@string/menuitem_edit_contact" 7 android:icon="@android:drawable/ic_menu_edit"></item> 8 <item android:id="@+id/deleteItem" 9 android:title="@string/menuitem_delete_contact" 10 android:orderInCategory="2" android:alphabeticShortcut="d" 11 android:titleCondensed="@string/menuitem_delete_contact" 12 android:icon="@android:drawable/ic_delete"></item> 13 <item android:id="@+id/transferItem" 14 android:title="@string/menuitem_transfer_contact" 15 android:orderInCategory="3" android:alphabeticShortcut="t" 16 android:titleCondensed="@string/menuitem_transfer_contact" 17 android:icon="@android:drawable/stat_sys_data_bluetooth"></item> 18 </menu> Fig. 17.7 | Menu da Activity ViewContact. 17.4.5 device_chooser_layout.xml: layout da ListActivity DeviceChooser A Figura 17.8 mostra o layout personalizado da subclasse DeviceChooser de ListActi- vity. Como sempre, ao criar um layout personalizado para uma ListActivity, certifi- que-se de incluir um elemento ListView e de configurar seu atributo android:id como @android:id/list. 4 android:title="@string/menuitem_add_contact" 5 android:icon="@drawable/ic_menu_add" 6 android:titleCondensed="@string/menuitem_add_contact" 7 android:alphabeticShortcut="a"></item> 8 <item android:id="@+id/receiveContactItem" 9 android:title="@string/menuitem_receive_contact" 10 android:icon="@drawable/ic_volume_bluetooth_in_call" 11 android:titleCondensed="@string/menuitem_receive_contact" 12 android:alphabeticShortcut="e"></item> 13 </menu> Fig. 17.6 | Menu da Activity AddressBook. Captulo 17 Aplicativo Address Book melhorado 9 17.4.6 device_layout.xml: layout dos itens de ListView do ListView da ListActivity DeviceChooser A Figura 17.9 mostra o componente TextView personalizado que usado para exibir cada item do ListView da Activity DeviceChooser. 1 <?xml version="1.0" encoding="utf-8"?> 2 <TextView xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" android:textSize="16sp" 5 android:padding="4dp" /> Fig. 17.9 | Layout do item de ListView do ListView da Activity DeviceChooser. 17.5 Construo do aplicativo Nesta seo, discutiremos apenas as classes modificadas e a nova classe do aplicativo Enhan- ced Address Book. As classes AddEditContact e DatabaseConnector no mudaram em relao ao Captulo 10; portanto, no so mostradas aqui. Para as classes AddressBook (Seo 17.5.1) e ViewContact (Seo 17.5.2), mostramos somente as partes que foram alteradas as defini- es de classe completas pode ser vistas abrindo-se os arquivos de cdigo-fonte do projeto. Mostramos a classe DeviceChooser completa (Seo 17.5.3), a qual ativada pela ListActi- vity ViewContact quando o usurio opta por enviar um contato para outro aparelho. 17.5.1 Activity AddressBook atualizada A classe AddressBook (Figs. 17.1017.15) contm as atualizaes que permitem ao apli- cativo habilitar o adaptador Bluetooth do aparelho caso ainda no esteja habilitado e receber um novo contato de outro aparelho. Instruo package, instrues import e campos A Figura 17.10 contm a instruo package, as instrues import e os campos da classe. Discutiremos as novas classes e interfaces medida que as encontrarmos ao longo da 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 <TextView android:layout_width="match_parent" 6 android:layout_height="wrap_content" 7 android:text="@string/title_other_devices" 8 android:background="#666666" android:textColor="#FFFFFF" 9 android:paddingLeft="4dp" /> 10 <ListView android:id="@android:id/list" 11 android:layout_width="match_parent" 12 android:layout_height="0dp" android:layout_weight="1" /> 13 <Button android:id="@+id/scanButton" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:text="@string/button_scan" /> 17 </LinearLayout> Fig. 17.8 | Layout da subclasse DeviceChooser de ListActivity. 10 Android para Programadores classe. As linhas 3940 declaram uma constante do tipo UUID (identificador universal- mente exclusivo). Usamos uma conexo Bluetooth peer-to-peer entre dois aparelhos que esto executando esse aplicativo e que esto prximos entre si (normalmente, em torno de 9 a 90 metros, dependendo do aparelho). Conforme voc vai ver, esse UUID ajuda a garantir que a conexo entre os aparelhos seja proveniente desse aplicativo em execuo em outro aparelho. Existem muitos geradores de UUID na Web usamos o que est em: www.guidgenerator.com A linha 43 declara um nome constante que associado ao UUID. Quando o usu- rio selecionar o item de menu Receive Contact dessa Activity, a Activity registrar o par nome/UUID no servidor SDP (Service Discovery Protocol) do aparelho, o qual atribuir um canal de comunicao que usado por aparelhos remotos para se conec- tarem. Ento, um aparelho remoto pode usar o UUID para consultar o servidor SDP no aparelho que est esperando para receber um contato. Se o servidor SDP encontrar o UUID, retornar o canal de comunicao apropriado para que o aparelho remoto possa iniciar uma conexo peer-to-peer. As constantes nas linhas 4647 so passadas para startActivityForResult ao se iniciar objetos Intent que habilitam o adaptador Bluetooth do aparelho e permitem que ele seja descoberto por outro aparelho Bluetooth, respectivamente. A linha 50 de- clara a varivel BluetoothAdapter que usada para interagir com o adaptador Bluetooth do aparelho. O Handler (linha 52) usado a partir de threads que no so da interface grfica do usurio nessa Activity para garantir que objetos Toast sejam exibidos na thread da interface grfica do usurio. As variveis de instncia restantes so da classe AddressBook do Captulo 10. 1 // AddressBook.java 2 // Atividade principal do aplicativo Address Book. 3 package com.deitel.addressbook; 4 5 import java.io.IOException; 6 import java.io.InputStream; 7 8 9 10 11 12 import android.app.ListActivity; 13 14 15 16 import android.content.Context; 17 import android.content.Intent; 18 import android.database.Cursor; 19 import android.os.AsyncTask; 20 import android.os.Bundle; 21 import android.os.Handler; 22 import android.util.Log; 23 import android.view.Menu; 24 import android.view.MenuInflater; 25 import android.view.MenuItem; import java.util.UUID; import org.json.JSONException; import org.json.JSONObject; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; Fig. 17.10 | Instruo package, instrues import e campos de AddressBook. Captulo 17 Aplicativo Address Book melhorado 11 Mtodos onCreate e onResume atualizados da Activity As linhas 74 e 76 do mtodo onCreate (Fig. 17.11, linhas 5977) so novas neste aplica- tivo. A linha 74 chama o mtodo esttico getDefaultAdapter da classe BluetoothAdapter para obter uma referncia para o adaptador Bluetooth padro dos aparelhos. A linha 76 cria o Handler para exibir os objetos Toast na thread da interface grfica do usurio. 26 import android.view.View; 27 import android.widget.AdapterView; 28 import android.widget.AdapterView.OnItemClickListener; 29 import android.widget.CursorAdapter; 30 import android.widget.ListView; 31 import android.widget.SimpleCursorAdapter; 32 import android.widget.Toast; 33 34 public class AddressBook extends ListActivity 35 { 36 private static String TAG = AddressBook.class.getName(); 37 38 39 40 41 42 // nome desse servio para descoberta de servio 43 private static final String NAME = "AddressBookBluetooth"; 44 45 // constantes passadas para startActivityForResult 46 private static final int ENABLE_BLUETOOTH = 1; 47 private static final int REQUEST_DISCOVERABILITY = 2; 48 49 // BluetoothAdapter d acesso aos recursos Bluetooth 50 51 private boolean userAllowedBluetooth = true; 52 private Handler handler; // para exibir Toasts de threads que no so // da interface grfica do usurio 53 54 public static final String ROW_ID = "row_id"; // chave extra do Intent 55 private ListView contactListView; // ListView da ListActivity 56 private CursorAdapter contactAdapter; // adaptador para ListView 57 // UUID de aplicativo exclusivo, gerado em http://www.guidgenerator.com/ public static final UUID MY_UUID = UUID.fromString("6acc0a73-afc3-4483-a3a8-94be2c0dfc52"); private BluetoothAdapter bluetoothAdapter = null; Fig. 17.10 | Instruo package, instrues import e campos de AddressBook. 58 // chamado quando a atividade criada 59 @Override 60 public void onCreate(Bundle savedInstanceState) 61 { 62 super.onCreate(savedInstanceState); // chama onCreate de super 63 contactListView = getListView(); // obtm o componente ListView interno 64 contactListView.setOnItemClickListener(viewContactListener); 65 66 // mapeia o nome de cada contato em um TextView no layout do ListView 67 String[] from = new String[] { "name" }; Fig. 17.11 | Mtodos onCreate e onResume atualizados da Activity. (continua) 12 Android para Programadores O mtodo onResume (linhas 8097) chama o mtodo isEnabled de BluetoothAdapter (linha 86) para determinar se o Bluetooth est habilitado. Se no estiver, o objeto Intent de BluetoothAdapter.ACTION_REQUEST_ENABLE (linhas 8990) passado para startActivityFor- Result a fim de pedir para o sistema ativar a Activity usada para habilitar comunicao com Bluetooth. O mtodo onActivityResult (Fig. 17.13) receber o resultado dessa Activity. Mtodo onOptionsItemSelected atualizado da Activity Atualizamos o mtodo onOptionsItemSelected com um novo caso para o item de menu Receive Contact (Fig. 17.12, linhas 159174). Se o BluetoothAdapter estiver habilitado, as linhas 163166 perguntam ao usurio se podem tornar esse aparelho detectvel por outros aparelhos. O objeto Intent de BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE (linhas 163164) passado para startActivityForResult a fim de pedir para o sistema ativar a Activity usada para tornar o aparelho detectvel por outros aparelhos. O mtodo onActivityResult (Fig. 17.13) receber o resultado dessa Activity. Se o usurio permitir que o aplicativo torne o aparelho detectvel, ele ficar detectvel por 120 segundos, por padro. Isso pode ser personalizado adicionando-se um extra ao Intent para a chave BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION com um valor em segundos (0 significa que o aparelho permanece sempre detectvel). Se o BluetoothAdapter no estiver habili- tado, simplesmente exibimos um Toast indicando que o Bluetooth no est habilitado. 68 int[] to = new int[] { R.id.contactTextView }; 69 contactAdapter = new SimpleCursorAdapter( 70 AddressBook.this, R.layout.contact_list_item, null, from, to); 71 setListAdapter(contactAdapter); // configura o adaptador de // contactView 72 73 // obtm o adaptador Bluetooth padro 74 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 75 76 handler = new Handler(); // para exibir Toasts na thread da // interface grfica 77 } // fim do mtodo onCreate 78 79 // chamado quando essa Activity retorna do segundo plano 80 @Override 81 protected void onResume() 82 { 83 super.onResume(); // chama o mtodo onResume de super 84 85 // pede para que o Bluetooth seja habilitado, caso ainda no esteja 86 if (!bluetoothAdapter.isEnabled()) 87 { 88 // cria e inicia um Intent para pedir que o usurio habilite // o Bluetooth 89 90 91 92 93 } // fim do if 94 95 // cria uma nova GetContactsTask e a executa 96 new GetContactsTask().execute((Object[]) null); 97 } // fim do mtodo onResume Intent enableBluetoothIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBluetoothIntent, ENABLE_BLUETOOTH); Fig. 17.11 | Mtodos onCreate e onResume atualizados da Activity. Captulo 17 Aplicativo Address Book melhorado 13 Sobrescrevendo o mtodo onActivityResult de Activity Quando o mtodo onActivityResult (Fig. 17.13) chamado em resposta ao Intent BluetoothAdapter.ACTION_REQUEST_ENABLE ativado em onResume (Fig. 17.11), as linhas 208219 exibem um Toast indicando se o Bluetooth foi habilitado. Quando o mtodo chamado em resposta ao Intent BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE em onOptionsItemSelected (Fig. 17.12), se o usurio permitiu que o aparelho se tornasse descobrvel, a linha 225 chama listenForContact (Fig. 17.14) para captar (esperar por) uma conexo de outro aparelho, em uma thread separada; caso contrrio, exibimos um Toast indicando que o aparelho no descobrvel. 147 // trata a escolha do menu de opes 148 @Override 149 public boolean onOptionsItemSelected(MenuItem item) 150 { 151 switch (item.getItemId()) 152 { 153 case R.id.addContactItem: 154 // cria novo Intent para ativar a Activity AddEditContact 155 Intent addNewContact = 156 new Intent(AddressBook.this, AddEditContact.class); 157 startActivity(addNewContact); // inicia a Activity AddEditContact 158 break; 159 case R.id.receiveContactItem: 160 if ) ( 161 { 162 163 164 165 166 167 } // fim do if 168 else // o usurio no permitiu que o adaptador Bluetooth fosse // habilitado 169 { 170 Toast.makeText(this, 171 R.string.no_bluetooth, 172 Toast.LENGTH_LONG).show(); 173 } // fim do else 174 break; 175 } // fim do switch 176 177 return super.onOptionsItemSelected(item); // chama o mtodo de super 178 } // fim do mtodo onOptionsItemSelected bluetoothAdapter.isEnabled() // ativa Intent para pedir capacidade de descoberta por 120 // segundos Intent requestDiscoverabilityIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); startActivityForResult(requestDiscoverabilityIntent, REQUEST_DISCOVERABILITY); Fig. 17.12 | Mtodo onOptionsItemSelected atualizado da Activity. 198 // chamado com o resultado de startActivityForResult 199 @Override 200 protected void onActivityResult(int requestCode, int resultCode, 201 Intent data) 202 { 203 super.onActivityResult(requestCode, resultCode, data); Fig. 17.13 | Sobrescrevendo o mtodo onActivityResult de Activity. (continua) 14 Android para Programadores Mtodo listenForContact e classe aninhada ReceiveContactTask O mtodo listenForContact e a classe aninhada ReceiveContactTask (Fig. 17.14) so novas neste aplicativo. O mtodo listenForContact (linhas 238244) simplesmente ativa a AsyncTask ReceiveContactTask (linhas 247347) a qual comea a esperar por uma conexo de outro aparelho que esteja executando o aplicativo Enhanced Address Book e, se receber uma, l as novas informaes de contato desse outro aparelho. 204 205 switch (requestCode) // processa o resultado com base em requestCode 206 { 207 case ENABLE_BLUETOOTH: // tentou habilitar Bluetooth 208 if (resultCode == RESULT_OK) // Bluetooth foi habilitado 209 { 210 Toast.makeText(this, 211 R.string.bluetooth_enabled, 212 Toast.LENGTH_LONG).show(); 213 } // fim do if 214 else // Bluetooth no foi habilitado 215 { 216 userAllowedBluetooth = false; 217 Toast.makeText(this, R.string.no_bluetooth, 218 Toast.LENGTH_LONG).show(); 219 } // fim do else 220 break; 221 // tentou tornar o aparelho descobrvel 222 case REQUEST_DISCOVERABILITY: 223 if (resultCode != RESULT_CANCELED) // o usurio deu permisso 224 { 225 listenForContact(); // comea a esperar por uma conexo 226 } // fim do if 227 else // o usurio no permitiu a capacidade de descoberta 228 { 229 Toast.makeText(this, 230 R.string.no_discoverability, 231 Toast.LENGTH_LONG).show(); 232 } // fim do else 233 break; 234 } // fim do switch 235 } // fim do mtodo onActivityResult 236 Fig. 17.13 | Sobrescrevendo o mtodo onActivityResult de Activity. 237 // comea a esperar por um contato enviado de outro aparelho 238 private void listenForContact() 239 { 240 // inicia tarefa de segundo plano para esperar uma conexo 241 // e receber um contato 242 ReceiveContactTask task = new ReceiveContactTask(); 243 task.execute((Object[]) null); 244 } // fim do mtodo listenForContact Fig. 17.14 | Mtodo listenForContact e classe aninhada ReceiveContactTask. Captulo 17 Aplicativo Address Book melhorado 15 245 246 // thread que capta pedidos de conexo recebidos 247 private class ReceiveContactTask 248 extends AsyncTask<Object, Object, Object> 249 { 250 251 252 253 // espera conexo, recebe contato e atualiza lista de contatos 254 @Override 255 protected Object doInBackground(Object... params) 256 { 257 try 258 { 259 260 261 262 263 264 displayToastViaHandler(AddressBook.this, handler, 265 R.string.waiting_for_contact); 266 267 268 269 270 271 272 273 // cria um array de bytes para conter as informaes de // contato recebidas 274 byte[] buffer = new byte[1024]; 275 int bytes; // nmero de bytes lidos 276 277 278 279 280 if (bytes != -1) // um contato foi recebido 281 { 282 DatabaseConnector databaseConnector = null; 283 284 // converte readMessage em JSONObject 285 try 286 { 287 288 289 290 291 // cria novo DatabaseConnector 292 databaseConnector = 293 new DatabaseConnector(getBaseContext()); 294 295 // abre o banco de dados e adiciona o contato nele 296 databaseConnector.open(); // conecta ao banco de dados 297 private BluetoothServerSocket serverSocket; // espera conexo private BluetoothSocket socket; // usado para processar conexo // obtm BluetoothServerSocket de bluetoothAdapter serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord( NAME, MY_UUID); // espera conexo BluetoothSocket socket = serverSocket.accept(); // obtm InputStream para receber contato InputStream inputStream = socket.getInputStream(); // l do InputStream e armazena os dados em buffer bytes = inputStream.read(buffer); // cria JSONObject a partir dos bytes lidos JSONObject contact = new JSONObject(new String(buffer, 0, buffer.length)); Fig. 17.14 | Mtodo listenForContact e classe aninhada ReceiveContactTask. (continua) 16 Android para Programadores 298 // adiciona o contato 299 300 301 302 303 304 305 // atualiza a lista de contatos 306 new GetContactsTask().execute((Object[]) null); 307 displayToastViaHandler(AddressBook.this, handler, 308 R.string.contact_received); 309 } // fim do try 310 catch (JSONException e) // problema na formatao JSON 311 { 312 displayToastViaHandler(AddressBook.this, handler, 313 R.string.contact_not_received); 314 Log.e(TAG, e.toString()); 315 } // fim do catch 316 finally // garante que a conexo com o banco de dados // seja fechada 317 { 318 if (databaseConnector != null) 319 databaseConnector.close(); // fecha a conexo 320 } // fim do finally 321 } // fim do if 322 } // fim do try 323 catch (IOException e) 324 { 325 Log.e(TAG, e.toString()); 326 } // fim do catch 327 finally // garante que BluetoothServerSocket e BluetoothSocket // sejam fechados 328 { 329 try 330 { 331 // se BluetoothServerSocket no for null, fecha-o 332 if (serverSocket != null) 333 334 335 // se BluetoothSocket no for null, fecha-o 336 if (socket != null) 337 338 } // fim do try 339 catch (IOException e) // problema ao fechar um socket 340 { 341 Log.e(TAG, e.toString()); 342 } // fim do catch 343 } // fim do finally 344 345 return null; 346 } // fim do mtodo doInBackround 347 } // fim da classe aninhada ReceiveContactTask 348 databaseConnector.insertContact( contact.getString("name"), contact.getString("email"), contact.getString("phone"), contact.getString("street"), contact.getString("city")); serverSocket.close(); socket.close(); Fig. 17.14 | Mtodo listenForContact e classe aninhada ReceiveContactTask. Captulo 17 Aplicativo Address Book melhorado 17 A ligao em rede com Bluetooth semelhante baseada em sockets padro. A linha 250 declara um BluetoothServerSocket, que ser usado para esperar uma conexo de outro aparelho. A linha 251 declara um BluetoothSocket se recebemos uma conexo, ento pode- mos usar os mtodos de BluetoothSocket para obter referncias para o InputStream, a fim de obter leitura de outro aparelho, e para o OutputStream, a fim de escrever em outro aparelho. O mtodo doInBackground de AsyncTask (linhas 254346) primeiramente chama o mtodo listenUsingRfcommWithServiceRecord de BluetoothAdapter com as constantes decla- radas nas linhas 3940 e 43 (Fig. 17.10), para registrar o aplicativo no servidor SDP (Service Discovery Protocol) do Android e obter um objeto BluetoothServerSocket. Em seguida, a li- nha 268 chama o mtodo accept em BluetoothServerSocket para comear a esperar por uma conexo de outro aparelho. Quando uma conexo recebida, esse mtodo retorna um Blue- toothSocket que pode ser usado para se comunicar com o outro aparelho. Nessa Activity, simplesmente precisamos ler um contato do outro aparelho, de modo que a linha 271 obtm o InputStream do BluetoothSocket. A linha 278 l os bytes do contato do aparelho remetente. Neste aplicativo, usamos dados formatados em JSON (apresentados no Captulo 14) para transferir o contato entre os aparelhos. As linhas 288289 convertem os bytes do conta- to em um objeto String e, ento, criam um JSONObject a partir desse objeto String. Usamos a classe DatabaseConnector do Captulo 10 para inserir o novo contato no banco de dados. As linhas 298303 usam o mtodo getString de JSONObject para extrair os dados do contato e pass-los para o mtodo insertContact de DatabaseConnector. Ento, a linha 306 inicia uma AsyncTask para atualizar a lista de contatos. O bloco finally nas linhas 327343 garan- te que BluetoothServerSocket e BluetoothSocket sejam fechados depois que a transferncia estiver concluda (ou se ocorrer um problema durante a transferncia). [Observao: possvel que a linha 278 leia apenas uma parte dos dados embora seja improvvel neste aplicativo. Para garantir a leitura de todos os dados, leia os bytes em um loop at que o mtodo read retorne -1 para indicar que no existem mais bytes. Cada iterao converteria os bytes em uma String e concatenaria os caracteres no final do que foi lido anteriormente.] Mtodo utilitrio displayToastViaHandler O mtodo displayToastViaHandler (Fig. 17.15) chamado a partir de outras threads nessa Activity para exibir objetos Toast na thread da interface grfica do usurio usando um Handler. 349 // usa handler para exibir um Toast na thread da interface grfica // do usurio com a mensagem especificada 350 public static void displayToastViaHandler(final Context context, 351 Handler handler, final int stringID) 352 { 353 handler.post( 354 new Runnable() 355 { 356 public void run() 357 { 358 Toast.makeText(context, stringID, 359 Toast.LENGTH_SHORT).show(); 360 } // fim do mtodo run 361 } // fim de Runnable 362 ); // fim da chamada do mtodo post do handler 363 } // fim do mtodo displayToastViaHandler Fig. 17.15 | Mtodo utilitrio displayToastViaHandler. 18 Android para Programadores 17.5.2 Activity ViewContact atualizada A classe ViewContact (Figs. 17.1617.20) contm as atualizaes que permitem ao usu- rio do aplicativo enviar um contato para outro aparelho. Instruo package, instrues import e campos A Figura 17.16 contm a instruo package, as instrues import e os campos da classe. Discutiremos as novas classes e interfaces medida que as encontrarmos ao longo da clas- se. A constante na linha 34 passada para startActivityForResult ao se ativar a Activity DeviceChooser para que o usurio possa escolher o aparelho para o qual vai enviar um contato. BluetoothAdapter (linha 36) usado para determinar se o Bluetooth est habi- litado e para obter um objeto BluetoothDevice para o aparelho selecionado pelo usurio na Activity DeviceChooser. O handler (linha 37) usado a partir de threads que no so da interface grfica do usurio nessa Activity, para garantir que os objetos Toast sejam exibidos na thread da interface. 1 // ViewContact.java 2 // Activity para ver um contato. 3 package com.deitel.addressbook; 4 5 import java.io.IOException; 6 import java.io.OutputStream; 7 8 9 10 11 import android.app.Activity; 12 import android.app.AlertDialog; 13 14 15 16 import android.content.DialogInterface; 17 import android.content.Intent; 18 import android.database.Cursor; 19 import android.os.AsyncTask; 20 import android.os.Bundle; 21 import android.os.Handler; 22 import android.util.Log; 23 import android.view.Menu; 24 import android.view.MenuInflater; 25 import android.view.MenuItem; 26 import android.widget.TextView; 27 import android.widget.Toast; 28 29 public class ViewContact extends Activity 30 { 31 private static final String TAG = ViewContact.class.getName(); 32 33 // cdigo de solicitao do Intent usado para iniciar uma // Activity que retorna um resultado 34 private static final int REQUEST_CONNECT_DEVICE = 1; 35 import org.json.JSONException; import org.json.JSONObject; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; Fig. 17.16 | Instruo package, instrues import e campos de ViewContact. Captulo 17 Aplicativo Address Book melhorado 19 Mtodo onCreate atualizado de Activity Os nicos recursos novos no mtodo onCreate (Fig. 17.17) esto nas linhas 65 e 67, as quais obtm o BluetoothAdapter padro e criam um Handler, respectivamente. 46 // chamado quando a Activity criada 47 @Override 48 public void onCreate(Bundle savedInstanceState) 49 { 50 super.onCreate(savedInstanceState); 51 setContentView(R.layout.view_contact); // infla a interface // grfica do usurio 52 53 // obtm componentes EditText 54 nameTextView = (TextView) findViewById(R.id.nameTextView); 55 phoneTextView = (TextView) findViewById(R.id.phoneTextView); 56 emailTextView = (TextView) findViewById(R.id.emailTextView); 57 streetTextView = (TextView) findViewById(R.id.streetTextView); 58 cityTextView = (TextView) findViewById(R.id.cityTextView); 59 60 // obtm o ID da linha do contato selecionado 61 Bundle extras = getIntent().getExtras(); 62 rowID = extras.getLong(AddressBook.ROW_ID); 63 64 65 66 67 handler = new Handler(); // cria o Handler 68 } // fim do mtodo onCreate // obtm o adaptador Bluetooth padro bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); Fig. 17.17 | Mtodo onCreate atualizado de Activity. Mtodo onOptionsItemSelected atualizado de Activity Atualizamos o mtodo onOptionsItemSelected com um novo caso para o item de menu Transfer Contact (Fig. 17.18, linhas 156171). Se BluetoothAdapter estiver ha- bilitado, as linhas 161164 ativam a Activity DeviceChooser para que o usurio possa selecionar o aparelho para o qual o contato deve ser enviado. O resultado dessa Acti- vity o endereo do aparelho selecionado, o qual retornado para o mtodo onActi- vityResult (Fig. 17.19). 36 37 private Handler handler; // para exibir Toasts na thread da interface // grfica 38 39 private long rowID; // ID da linha no banco de dados do contato selecionado 40 private TextView nameTextView; // exibe o nome do contato 41 private TextView phoneTextView; // exibe o telefone do contato 42 private TextView emailTextView; // exibe o email do contato 43 private TextView streetTextView; // exibe o endereo do contato 44 private TextView cityTextView; // exibe cidade/estado/CEP do contato 45 private BluetoothAdapter bluetoothAdapter = null; // adaptador Bluetooth Fig. 17.16 | Instruo package, instrues import e campos de ViewContact. 20 Android para Programadores Sobrescrevendo o mtodo onActivityResult de Activity O mtodo onActivityResult (Fig. 17.19) chamado quando a Activity DeviceChooser retorna. Se a Activity retorna um aparelho, as linhas 236237 iniciam uma SendCon- tactTask para enviar o contato para outro aparelho. O parmetro data do mtodo onAc- tivityResult contm um extra representando o endereo do aparelho para o qual o con- tato deve ser enviado. Esse endereo passado como o nico elemento de um array de Strings para o mtodo execute de AsyncTask, o qual ativa a AsyncTask e passa o array de Strings para o mtodo doInBackground da tarefa. 133 // trata a escolha do menu de opes 134 @Override 135 public boolean onOptionsItemSelected(MenuItem item) 136 { 137 switch (item.getItemId()) // switch baseado no ID do // MenuItem selecionado 138 { 139 case R.id.editItem: // item de menu Edit Contact selecionado 140 // cria um Intent para ativar a Activity AddEditContact 141 Intent addEditContact = 142 new Intent(this, AddEditContact.class); 143 144 // passa os dados do contato selecionado como extras com o Intent 145 addEditContact.putExtra(AddressBook.ROW_ID, rowID); 146 addEditContact.putExtra("name", nameTextView.getText()); 147 addEditContact.putExtra("phone", phoneTextView.getText()); 148 addEditContact.putExtra("email", emailTextView.getText()); 149 addEditContact.putExtra("street", streetTextView.getText()); 150 addEditContact.putExtra("city", cityTextView.getText()); 151 startActivity(addEditContact); // inicia a Activity 152 break; 153 case R.id.deleteItem: // item de menu Delete Contact selecionado 154 deleteContact(); // exclui o contato exibido 155 break; 156 case R.id.transferItem: // item de menu Transfer Contact selecionado 157 // se ainda no estamos conectados 158 if ) ( 159 { 160 // ativa DeviceChooser para que o usurio possa // escolher um aparelho prximo 161 Intent serverIntent = 162 new Intent(this, DeviceChooser.class); 163 startActivityForResult( 164 serverIntent, REQUEST_CONNECT_DEVICE); 165 } // fim do if 166 else // indica que o Bluetooth no est habilitado 167 { 168 Toast.makeText(this, 169 R.string.no_bluetooth, Toast.LENGTH_LONG).show(); 170 } // fim do else 171 break; 172 } // fim do switch 173 174 return super.onOptionsItemSelected(item); 175 } // fim do mtodo onOptionsItemSelected bluetoothAdapter.isEnabled() Fig. 17.18 | Mtodo onOptionsItemSelected atualizado de Activity. Captulo 17 Aplicativo Address Book melhorado 21 Classe aninhada SendContactTask A classe SendContactTask (Fig. 17.20) envia um contato para outro aparelho usando AsyncTask. Quando o mtodo doInBackground executado, as linhas 256257 usam o primeiro elemento do array params que contm um objeto String representando o en- dereo de um aparelho no mtodo getRemoteDevice de BluetoothAdapter. Isso retorna um BluetoothDevice representando o aparelho que est nesse endereo. Em seguida, as linhas 268269 chamam o mtodo createRfcommSocketToServiceRecord de BluetoothDe- vice com AddressBook.UUID como argumento. Essa chamada se comunica com o aparelho remoto para criar uma conexo segura, supondo que exista um aplicativo com o mesmo UUID esperando conexes no aparelho remoto. Se assim for, o mtodo retorna um Blue- toothSocket que pode ser usado para se comunicar com o aplicativo no aparelho remoto. Ento, a linha 270 chama o mtodo connect de BluetoothSocket para abrir a conexo. Nesta Activity, enviamos um contato para o aparelho remoto, de modo que obtemos apenas o OutputStream do BluetoothSocket (linha 273). As linhas 276281 criam um objeto JSONObject contendo as informaes do contato como pares chave/valor. A linha 284 obtm a representao de String de JSONObject e, ento, obtm os bytes do String e escreve esses bytes no OutputStream. Isso envia o contato para o aparelho remoto. O bloco finally nas linhas 301313 garante que BluetoothSocket seja fechado depois que a transferncia estiver concluda (ou se ocorrer um problema durante a transferncia). 226 // chamado quando uma Activity ativada a partir desta usando 227 // startActivityForResult termina 228 public void onActivityResult(int requestCode, int resultCode, 229 Intent data) 230 { 231 // se a conexo foi estabelecida 232 if (resultCode == Activity.RESULT_OK) 233 { 234 // obtm o endereo MAC do aparelho remoto e passa-o para 235 // o mtodo execute de SendContactTas 236 new SendContactTask().execute(new String[] { 237 data.getExtras().getString(DeviceChooser.DEVICE_ADDRESS)}); 238 } // fim do if 239 else // houve um erro de conexo 240 { 241 // exibe Toast de erro de conexo 242 Toast.makeText(this, 243 R.string.connection_error, Toast.LENGTH_LONG); 244 } // fim do else 245 } // fim do mtodo onActivityResult 246 Fig. 17.19 | Sobrescrevendo o mtodo onActivityResult de Activity. 247 // Tarefa para enviar um contato em uma thread de segundo plano 248 private class SendContactTask extends AsyncTask<String, Object, Object> 249 { Fig. 17.20 | A classe aninhada SendContactTask envia um contato para um aparelho remoto usando uma thread de segundo plano. 22 Android para Programadores 250 // obtm o BluetoothDevice do endereo especificado, 251 // conecta ao aparelho e envia o contato 252 @Override 253 protected Object doInBackground(String... params) 254 { 255 256 257 258 259 // para enviar o contato 260 261 // estabelece a conexo com o aparelho remoto e envia o contato 262 try 263 { 264 AddressBook.displayToastViaHandler(ViewContact.this, handler, 265 R.string.sending_contact); 266 267 268 269 270 271 272 // obtm fluxos (streams) para comunicao via BluetoothSocket 273 ; = m a e r t S t u p t u o m a e r t S t u p t u O 274 275 // cria JSONObject representando o contato 276 final JSONObject contact = new JSONObject(); 277 contact.put("name", nameTextView.getText().toString()); 278 contact.put("phone", phoneTextView.getText().toString()); 279 contact.put("email", emailTextView.getText().toString()); 280 contact.put("street", streetTextView.getText().toString()); 281 contact.put("city", cityTextView.getText().toString()); 282 283 // envia um array de bytes contendo as informaes do contato 284 285 286 AddressBook.displayToastViaHandler(ViewContact.this, handler, 287 R.string.contact_sent); 288 } // fim do try 289 catch (IOException e) // problema ao enviar o contato 290 { 291 AddressBook.displayToastViaHandler(ViewContact.this, handler, 292 R.string.transfer_failed); 293 Log.e(TAG, e.toString()); 294 } // fim do catch 295 catch (JSONException e) // problema na formatao de dados em JSON 296 { 297 AddressBook.displayToastViaHandler(ViewContact.this, handler, 298 R.string.transfer_failed); 299 Log.e(TAG, e.toString()); 300 } // fim do catch // obtm um objeto BluetoothDevice representando o aparelho remoto BluetoothDevice device = bluetoothAdapter.getRemoteDevice(params[0]); BluetoothSocket bluetoothSocket = null; // obtm BluetoothSocket e, em seguida, conecta-se com o // outro aparelho bluetoothSocket = device.createRfcommSocketToServiceRecord( AddressBook.MY_UUID); bluetoothSocket.connect(); // estabelece a conexo bluetoothSocket.getOutputStream() outputStream.write(contact.toString().getBytes()); outputStream.flush(); Fig. 17.20 | A classe aninhada SendContactTask envia um contato para um aparelho remoto usando uma thread de segundo plano. Captulo 17 Aplicativo Address Book melhorado 23 17.5.3 Activity DeviceChooser A subclasse DeviceChooser de ListActivity (Fig. 17.2117.26) ativada pela Activity ViewContact quando o usurio tenta enviar um contato para um aparelho remoto. Instruo package, instrues import e campos A Figura 17.21 contm a instruo package, as instrues import e os campos da clas- se. A linha 30 define uma constante que representa o nmero de caracteres em um endereo MAC (Media Access Control). Um endereo MAC um identificador de rede exclusivo de determinado aparelho. Usamos o endereo MAC de um aparelho remoto para conectar esse aparelho na Activity ViewContact. A linha 33 define uma constante que usada como chave ao armazenarmos o endereo MAC do aparelho selecionado em um Intent que retornado para a Activity ViewContact. O ArrayAdapter (linha 36) ser usado para preencher o componente ListView dessa Activity com uma lista de aparelhos Bluetooth prximos. 301 finally // garante que BluetoothSocket seja fechado 302 { 303 try 304 { 305 306 } // fim do try 307 catch (IOException e) // problema ao fechar BluetoothSocket 308 { 309 Log.e(TAG, e.toString()); 310 } // fim do catch 311 312 bluetoothSocket = null; 313 } // fim do finally 314 315 return null; 316 } // fim do mtodo doInBackground 317 } // fim da classe SendContactTask bluetoothSocket.close(); // fecha BluetoothSocket Fig. 17.20 | A classe aninhada SendContactTask envia um contato para um aparelho remoto usando uma thread de segundo plano. 1 // DeviceChooser.java 2 // Activity para escolher um aparelho para conectar. 3 package com.deitel.addressbook; 4 5 import java.util.Set; 6 7 import android.app.Activity; 8 import android.app.ListActivity; 9 10 11 import android.content.BroadcastReceiver; 12 import android.content.Context; 13 import android.content.Intent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; Fig. 17.21 | Instruo package, instrues import e campos de DeviceChooser. (continua) 24 Android para Programadores Sobrescrevendo o mtodo onCreate de Activity O mtodo onCreate (Fig. 17.22) configura essa Activity. A linha 46 chama o m- todo requestWindowFeature da Activity (o que deve ocorrer antes da chamada de setContentView) para indicar que a janela dessa Activity deve mostrar um cone de pro- gresso indeterminado o processo de descoberta do aparelho pode levar algum tempo e pode haver um nmero arbitrrio de aparelhos prximos (e provvel que esse nmero aumente medida que mais e mais aparelhos Bluetooth apaream), de modo que no podemos mostrar um status de progresso exato. 14 import android.content.IntentFilter; 15 import android.os.Bundle; 16 import android.view.View; 17 import android.view.View.OnClickListener; 18 import android.view.Window; 19 import android.widget.AdapterView; 20 import android.widget.ArrayAdapter; 21 import android.widget.Button; 22 import android.widget.ListView; 23 import android.widget.TextView; 24 import android.widget.AdapterView.OnItemClickListener; 25 import android.widget.Toast; 26 27 public class DeviceChooser extends ListActivity 28 { 29 // comprimento de um endereo MAC em caracteres 30 private static final int MAC_ADDRESS_LENGTH = 17; 31 32 // chave para armazenar o endereo MAC do aparelho // selecionado como um extra de Intent 33 public static final String DEVICE_ADDRESS = "device_address"; 34 35 // o Bluetooth Adapter 36 private ArrayAdapter<String> foundDevicesAdapter; // dados de ListView 37 private ListView newDevicesListView; // ListView que mostra os aparelhos 38 private BluetoothAdapter bluetoothAdapter; Fig. 17.21 | Instruo package, instrues import e campos de DeviceChooser. 39 // chamado quando essa Activity criada 40 @Override 41 protected void onCreate(Bundle savedInstanceState) 42 { 43 super.onCreate(savedInstanceState); 44 45 // mostra uma barra de progresso enquanto a atividade carregada 46 47 48 // configura o layout da Activity 49 setContentView(R.layout.device_chooser_layout); 50 51 // configura o cdigo do resultado a retornar para a Activity anterior 52 // se o usurio tocar no componente Button Cancel requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); Fig. 17.22 | Sobrescrevendo o mtodo onCreate de Activity. Captulo 17 Aplicativo Address Book melhorado 25 53 setResult(Activity.RESULT_CANCELED); 54 55 // cria componente Button para iniciar a descoberta 56 Button scanButton = (Button) findViewById(R.id.scanButton); 57 scanButton.setOnClickListener( 58 new OnClickListener() 59 { 60 // chamado quando scanButton clicado 61 public void onClick(View v) 62 { 63 startDiscovery(); // comea a procurar aparelhos 64 } // fim do mtodo onClick 65 } // fim de OnClickListener 66 ); // fim da chamada de setOnClickListener 67 68 // inicializa o adaptador da lista de aparelhos encontrados 69 foundDevicesAdapter = 70 new ArrayAdapter<String>(this, R.layout.device_layout); 71 72 // inicializa o ListView que exibir os aparelhos // recentemente encontrados 73 newDevicesListView = getListView(); 74 newDevicesListView.setAdapter(foundDevicesAdapter); 75 newDevicesListView.setOnItemClickListener( 76 deviceListItemClickListener); 77 78 // capta Intents de transmisso alertando que um aparelho Bluetooth 79 // foi encontrado nas proximidades 80 81 82 83 84 // capta Intents de transmisso alertando que a busca por 85 // aparelhos prximos est concluda 86 87 88 89 90 // obtm BluetoothAdapter local 91 92 93 // obtm um conjunto de todos os aparelhos com que j nos conectamos 94 95 96 97 // adiciona o nome de cada aparelho conectado em nosso ListView 98 for (BluetoothDevice device : pairedDevices) 99 { 100 foundDevicesAdapter.add( + "\n" + 101 ; ) 102 } // fim do for 103 } // fim do mtodo onCreate 104 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(deviceChooserReceiver, filter); filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); registerReceiver(deviceChooserReceiver, filter); bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices(); device.getName() device.getAddress() Fig. 17.22 | Sobrescrevendo o mtodo onCreate de Activity. 26 Android para Programadores A linha 53 chama o mtodo setResult da Activity usando a constante RESULT_CAN- CELED. Se o usurio sair dessa Activity pressionando o boto Voltar antes de termos uma chance de retornar dados reais, esse resultado permitir que a chamada da Activity saiba que houve um erro. As linhas 5666 obtm scanButton e configuram seu OnClickLis- tener, o qual chama o mtodo startDiscovery (Fig. 17.24) quando o usurio toca no Button. As linhas 6970 criam o ArrayAdapter utilizado para preencher o ListView dessa Activity. As linhas 7376 configuram o ListView para exibir os aparelhos detectados. As linhas 8082 criam um IntentFilter usando a constante BluetoothDevice. ACTION_FOUND e, em seguida, chamam o mtodo registerReceiver da Activity com o deviceChooserReceiver BroadcastReceiver (definido na Fig. 17.26) e o novo IntentFil- ter. Sempre que um aparelho Bluetooth for encontrado, um Intent BluetoothDevice. ACTION_FOUND ser transmitido e o mtodo onReceive de nosso deviceChooserReceiver ser chamado para adicionar o novo aparelho na lista. As linhas 8688 criam outro In- tentFilter usando a constante BluetoothDevice. ACTION_DISCOVERY_FINISHED e, ento, registram deviceChooserReceiver para esse IntentFilter. Quando a descoberta de apa- relhos Bluetooth terminar, ser transmitido um Intent BluetoothAdapter.ACTION_DISCO- VERY_FINISHED e o mtodo onReceive de nosso deviceChooserReceiver ser chamado para verificar se foram encontrados aparelhos. A linha 91 obtm o BluetoothAdapter padro e, ento, as linhas 9495 utilizam o mtodo getBondedDevices de BluetoothAdapter para obter um conjunto de aparelhos (Set<BluetoothDevice>) contendo os aparelhos atualmente conectados. Ento, adicio- namos o nome e o endereo de cada aparelho (se houver) no ArrayAdapter do ListView (linhas 98102). Sobrescrevendo o mtodo onDestroy de Activity Quando a Activity termina ou quando o Android a destri, o mtodo onDestroy (Fig. 17.23) chama o mtodo cancelDiscovery de BluetoothAdapter para parar de procurar aparelhos prximos (linha 114). Tambm desfazemos o registro de device- ChooserReceiver. 105 // chamado antes que essa Activity seja destruda 106 @Override 107 protected void onDestroy() 108 { 109 super.onDestroy(); 110 111 // fim da descoberta de Bluetooth 112 if (bluetoothAdapter != null) 113 { 114 115 } // fim do if 116 117 // desfaz o registro do BroadcastReceiver deviceChooserReceiver 118 unregisterReceiver(deviceChooserReceiver); 119 } // fim do mtodo onDestroy 120 bluetoothAdapter.cancelDiscovery(); Fig. 17.23 | Sobrescrevendo o mtodo onDestroy de Activity. Captulo 17 Aplicativo Address Book melhorado 27 Mtodo startDiscovery O mtodo startDiscovery (Fig. 17.24) procura aparelhos prximos com Bluetooth habi- litado. Chamamos o mtodo isDiscovering de BluetoothAdapter (linha 132) para deter- minar se j estamos procurando aparelhos. Em caso positivo, interrompemos a busca atual chamando o mtodo cancelDiscovery de BluetoothAdapter (linha 134). A linha 138 exibe a barra de progresso indeterminado e, em seguida, a linha 141 chama o mtodo startDis- covery de BluetoothAdapter para comear a descoberta de aparelhos a partir do zero. 121 // inicia a descoberta 122 private void startDiscovery() 123 { 124 // verifica se o Bluetooth ainda est habilitado 125 if (!bluetoothAdapter.isEnabled()) 126 { 127 Toast.makeText(this, R.string.no_bluetooth, Toast.LENGTH_LONG); 128 return; 129 } // fim do if 130 131 // fim da descoberta existente, se necessrio 132 if ) ( 133 { 134 135 } // fim do if 136 137 // mostra a barra de progresso 138 139 140 // comea a procurar outros aparelhos 141 142 } // fim do mtodo startDiscovery 143 bluetoothAdapter.isDiscovering() bluetoothAdapter.cancelDiscovery(); setProgressBarIndeterminateVisibility(true); bluetoothAdapter.startDiscovery(); Fig. 17.24 | Mtodo startDiscovery. deviceListItemClickListener OnItemClickListener Quando o usurio toca em um aparelho no componente ListView da Activity, o mtodo onItemClick de deviceListItemClickListener (Fig. 17.25, linhas 148168) chamado. O usurio selecionou um aparelho; portanto, chamamos imediatamente o mtodo cancelDis- covery de BluetoothAdapter (linha 152) para interromper a busca de outros aparelhos. A procura diminui a vida da bateria de um aparelho; portanto, voc deve encerrar o processo quando a Activity no for mais necessria. As linhas 155157 extraem o endereo MAC do aparelho selecionado e, ento, as linhas 160166 criam um novo Intent com o endereo MAC includo como extra e configuram o Intent como resultado da Activity. Chamamos finish para terminar essa Activity e retornamos o Intent para a Activity ViewContact. 144 // capta eventos gerados quando o usurio clica no item de ListView 145 private OnItemClickListener deviceListItemClickListener = 146 new OnItemClickListener() 147 { 148 public void onItemClick(AdapterView<?> parent, View view, 149 int position, long id) 150 { Fig. 17.25 | deviceListItemClickListener OnItemClickListener. (continua) 28 Android para Programadores deviceChooserReceiver BroadcastReceiver A Figura 17.26 define o BroadcastReceiver que responde aos objetos Intent de trans- misso via broadcast indicando quando um aparelho descoberto e quando a descoberta est concluda. O mtodo onReceive chamado sempre que transmitido um Intent que corresponda a qualquer um dos elementos IntentFilter para os quais esse receptor est registrado para responder. Primeiramente, obtemos a ao do Intent recebido usando seu mtodo getAction (linha 180). Se a ao corresponder constante ACTION_FOUND de BluetoothDevice, sabemos que o Intent dado inclui o BluetoothDevice selecionado como extra. As linhas 186187 obtm o BluetoothDevice usando o mtodo getParcelableEx- tra do Intent. As linhas 191195 verificam se o aparelho retornado j est conectado com o aparelho atual (no caso em que ele j aparece na lista de aparelhos). Se no estiver, o adicionamos no ArrayAdapter do ListView. Se a ao corresponder constante ACTION_ DISCOVERY_FINISHED de BluetoothAdapter, a busca terminou. A linha 202 oculta a barra de progresso indeterminado. Se nenhum aparelho foi encontrado, adicionamos um item no ArrayAdapter do ListView explicando esse fato. 151 // cancela a descoberta antes de tentar se conectar 152 153 154 // obtm o endereo MAC do aparelho selecionado 155 String info = ((TextView) view).getText().toString(); 156 String address = info.substring(info.length() - 157 MAC_ADDRESS_LENGTH); 158 159 // cria um Intent para retornar Activity que fez a chamada 160 Intent intent = new Intent(); 161 162 // inclui o endereo MAC do aparelho no Intent de retorno 163 intent.putExtra(DEVICE_ADDRESS, address); 164 165 // configura nosso Intent como valor de retorno de sucesso e termina 166 setResult(Activity.RESULT_OK, intent); 167 finish(); 168 } // fim do mtodo onItemClick 169 }; // fim de OnItemClickListener 170 bluetoothAdapter.cancelDiscovery(); Fig. 17.25 | deviceListItemClickListener OnItemClickListener. 171 // capta Intents de transmisso anunciando quando uma 172 // descoberta termina e quando novos aparelhos so detectados 173 private final BroadcastReceiver deviceChooserReceiver = 174 new BroadcastReceiver() 175 { 176 // chamado quando uma transmisso recebida 177 public void onReceive(Context context, Intent intent) 178 { Fig. 17.26 | deviceChooserReceiver BroadcastReceiver capta Intents de transmisso via broadcast que indicam quando um aparelho encontrado e quando a descoberta de aparelhos terminou. Captulo 17 Aplicativo Address Book melhorado 29 17.6 Para finalizar O aplicativo Enhanced Address Book permite ao usurio transferir contatos entre aparelhos usando conexes de rede Bluetooth. Voc usou vrias classes do pacote android.bluetooth para executar tarefas relacionadas a Bluetooth. Usou o mtodo esttico getDefaultAdapter de BluetoothAdapter para obter um objeto que representa o adaptador Bluetooth do apa- relho. Para procurar aparelhos Bluetooth prximos, voc usou o mtodo startDiscovery de BluetoothAdapter para dizer ao Android que executasse a tarefa de descoberta, a fim de notific-lo quando aparelhos fossem encontrados e quando a descoberta terminasse. 179 // obtm a ao do Intent que fez a chamada 180 String action = intent.getAction(); 181 182 // um novo aparelho foi detectado 183 if (BluetoothDevice.ACTION_FOUND.equals(action)) 184 { 185 // obtm o BluetoothDevice do Intent de transmisso 186 187 188 189 // se o aparelho ainda no estiver conectado, adiciona seu nome 190 // no componente ListView 191 if ) ( 192 { 193 foundDevicesAdapter.add( + "\n" + 194 ; ) 195 } // fim do if 196 } // fim do if 197 // uma busca por novos aparelhos terminou 198 else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals( 199 action)) 200 { 201 // oculta a barra de progresso 202 203 204 // configura o ttulo da Activity 205 setTitle(getResources().getString(R.string.choose_device)); 206 207 // se no havia aparelhos ao alcance, exibe uma mensagem 208 if (foundDevicesAdapter.getCount() == 0) 209 { 210 // desabilita cliques de item da lista 211 newDevicesListView.setOnItemClickListener(null); 212 foundDevicesAdapter.add(getResources().getString( 213 R.string.no_devices)); 214 } // fim do if 215 } // fim do else if 216 } // fim do mtodo onReceive 217 }; // fim de BroadcastReceiver 218 } // fim da classe DeviceChooser BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); device.getBondState() != BluetoothDevice.BOND_BONDED device.getName() device.getAddress() setProgressBarIndeterminateVisibility(false); Fig. 17.26 | deviceChooserReceiver BroadcastReceiver capta Intents de transmisso via broadcast que indicam quando um aparelho encontrado e quando a descoberta de aparelhos terminou. 30 Android para Programadores Para receber os resultados da descoberta, voc se registrou para receber objetos In- tent de transmisso via broadcast que notificavam o aplicativo quando cada aparelho Bluetooth era descoberto (fornecendo um objeto BluetoothDevice para cada um deles) e quando a busca de aparelhos terminava. Voc usou uma thread separada para captar os pedidos de conexo recebidos de ou- tros aparelhos que estavam executando o aplicativo Enhanced Address Book. Nessa thre- ad, voc chamou o mtodo listenUsingRfcommWithServiceRecord de BluetoothAdapter para obter um BluetoothServerSocket e, ento, usou o mtodo accept desse objeto para captar os pedidos de conexo Bluetooth recebidos. Quando uma conexo era recebida, era retornado um BluetoothSocket contendo um InputStream e um OutputStream para comunicao com o outro aparelho. Para se conectar com o outro aparelho, voc usou o mtodo createRfcommSocket- ToServiceRecord de BluetoothDevice, a fim de obter um BluetoothSocket para comuni- cao com o aparelho remoto. Ento, voc chamou o mtodo connect desse objeto para abrir uma conexo com o aparelho. Por fim, voc transmitiu e recebeu os dados no formato JSON. Para criar os dados formatados em JSON, voc gerou um objeto JSONObject e obteve sua representao de String. Para receber os dados JSON, voc criou um objeto JSONObject e passou a repre- sentao de String formatada em JSON para o construtor do objeto. No Captulo 18, apresentaremos o OpenGL ES para criar elementos grficos tridi- mensionais e animaes. Voc vai criar um aplicativo que mostra um cubo, uma pirmide ou um prisma retangular. O usurio poder selecionar a figura a ser exibida, mudar sua escala e selecionar a velocidade de rotao da figura mostrada ao longo dos eixos x, y e z.