android devel

66
´ Indice 1.1 Introdu¸ ao ......................................... 1 1.2 Objectivos ......................................... 2 1.3 Avalia¸ ao ......................................... 2 1.4 Instala¸ ao Ambiente Desenvolvimento Android .................... 2 1.4.1 Lista de Software a instalar ........................... 3 1.4.2 Windows 7 Pro 64 bits: Instalar JDK 1.6.0 64 bits + Eclipse 64 bits + Android SDK 32 bits + ADT Plugin + MoSync 32 bits + JRE 1.7.0 32 bits 4 1.4.3 Mac OS X 64 bits: Instalar Eclipse 64 bits + Android SDK + ADT Plugin + MoSync ..................................... 7 1.4.4 Actualiza¸ oes Outubro 2011 ........................... 7 1.4.5 APIs do Android ................................. 8 1.4.6 Configurar o Eclipse para criar uma aplica¸ ao Android ............ 9 1.4.6.1 Threads ................................. 10 1.5 Arquitectura das Aplica¸ oes Android .......................... 12 1.5.1 Hello World em Android ............................. 15 1.5.1.1 Hello World em Android mas sem usar o estilo do Android .... 15 1.5.1.2 Hello World no estilo Android .................... 16 1.5.2 Utiliza¸ ao do Log do Android .......................... 17 1.5.3 GUIs em Android ................................ 19 1.5.4 O ciclo de vida das aplica¸ oes Android ..................... 22 1.5.5 Tarefas Ass´ ıncronas em Android ........................ 22 1.5.6 Thread que executa c´ alculos em background com indicador de progresso . . 26 1.6 Bases de Dados ...................................... 36 1.7 Ler e Escrever Ficheiros ................................. 43 1.8 GUI widgets ........................................ 44 1.8.1 ListView, Spinner, GridView .......................... 44 1.8.2 ExpandableListView (´ arvore com 2 n´ ıveis) ................... 48 1.8.3 ActionBar (tabs) ................................. 48 1.8.4 Criar activities com sub-activities ........................ 49 1.9 Escrever e ler de um Ficheiro de Texto ......................... 52 1.10 Aplica¸ ao de Manuten¸ ao ................................ 59 1.11 ´ Indice de Fragmentos de C´ odigo ............................. 61 1.12 Bibliografia ........................................ 64 1.13 ´ Indice de Ficheiros .................................... 65 1.14 ´ Indice Remissivo ..................................... 66 Vers˜ ao: $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ NOTA: partes do documento por completar ou desenvolver est˜ ao marcadas com a palavra FIXME. 1.1 Introdu¸ ao Este documento serve de apoio aos alunos da unidade curricular de Computa¸ ao M´ ovel, que ´ e dada em conjunto a alunos provenientes do terceiro ano de Sistemas de Informa¸c˜ ao e Software, Redes de Comunica¸c˜ ao e Telecomunica¸c˜ oes,e Tecnologias de Comunica¸c˜ ao Multim´ edia (op¸c˜ ao de Internet e Computa¸c˜ ao M´ ovel ). Dependendo da proveniˆ encia, estes alunos nos anos anteriores aprenderam a programar em C#, Java, ActionScript e/ou Python. Este documento vai estar em permanente actualiza¸ ao (pelo que s´ o raramente deve ser im- presso). A ´ ultima vers˜ ao pode ser descarregada de http://portodigital.pt/avs/cm2012-2c35/. 1

Upload: vitor-nogueira

Post on 31-Dec-2015

57 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Android Devel

Indice

1.1 Introducao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Objectivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.3 Avaliacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.4 Instalacao Ambiente Desenvolvimento Android . . . . . . . . . . . . . . . . . . . . 2

1.4.1 Lista de Software a instalar . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.4.2 Windows 7 Pro 64 bits: Instalar JDK 1.6.0 64 bits + Eclipse 64 bits +

Android SDK 32 bits + ADT Plugin + MoSync 32 bits + JRE 1.7.0 32 bits 41.4.3 Mac OS X 64 bits: Instalar Eclipse 64 bits + Android SDK + ADT Plugin

+ MoSync . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.4.4 Actualizacoes Outubro 2011 . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.4.5 APIs do Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.4.6 Configurar o Eclipse para criar uma aplicacao Android . . . . . . . . . . . . 9

1.4.6.1 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101.5 Arquitectura das Aplicacoes Android . . . . . . . . . . . . . . . . . . . . . . . . . . 12

1.5.1 Hello World em Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151.5.1.1 Hello World em Android mas sem usar o estilo do Android . . . . 151.5.1.2 Hello World no estilo Android . . . . . . . . . . . . . . . . . . . . 16

1.5.2 Utilizacao do Log do Android . . . . . . . . . . . . . . . . . . . . . . . . . . 171.5.3 GUIs em Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191.5.4 O ciclo de vida das aplicacoes Android . . . . . . . . . . . . . . . . . . . . . 221.5.5 Tarefas Assıncronas em Android . . . . . . . . . . . . . . . . . . . . . . . . 221.5.6 Thread que executa calculos em background com indicador de progresso . . 26

1.6 Bases de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361.7 Ler e Escrever Ficheiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431.8 GUI widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

1.8.1 ListView, Spinner, GridView . . . . . . . . . . . . . . . . . . . . . . . . . . 441.8.2 ExpandableListView (arvore com 2 nıveis) . . . . . . . . . . . . . . . . . . . 481.8.3 ActionBar (tabs) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481.8.4 Criar activities com sub-activities . . . . . . . . . . . . . . . . . . . . . . . . 49

1.9 Escrever e ler de um Ficheiro de Texto . . . . . . . . . . . . . . . . . . . . . . . . . 521.10 Aplicacao de Manutencao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591.11 Indice de Fragmentos de Codigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611.12 Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641.13 Indice de Ficheiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651.14 Indice Remissivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

Versao: $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

NOTA: partes do documento por completar ou desenvolver estao marcadas com a palavra FIXME.

1.1 Introducao

Este documento serve de apoio aos alunos da unidade curricular de Computacao Movel, que edada em conjunto a alunos provenientes do terceiro ano de Sistemas de Informacao e Software,Redes de Comunicacao e Telecomunicacoes, e Tecnologias de Comunicacao Multimedia (opcao deInternet e Computacao Movel).

Dependendo da proveniencia, estes alunos nos anos anteriores aprenderam a programar emC#, Java, ActionScript e/ou Python.

Este documento vai estar em permanente actualizacao (pelo que so raramente deve ser im-presso). A ultima versao pode ser descarregada de http://portodigital.pt/avs/cm2012-2c35/.

1

Page 2: Android Devel

2 Computacao Movel

Em termos da bibliografia as referencias principais para a materia abordada nesta unidadecurricular sao [Ableson et al. 2011], [Darcey e Conder 2011], [Mednieks et al. 2011], [Steele e To2011], [Collins et al. 2012] e [Neil 2012].

1.2 Objectivos

Sao estes os objectivos desta unidade curricular:

1. (objectivo principal) Conseguir que o aluno crie programas para dispositivos moveis, es-tando ciente das suas diferencas, vantagens e limitacoes face a aplicacoes para computadoresdesktops

2. (objectivo principal) Conseguir que o aluno consiga a partir da descricao de um problema ouda especificacao de um programa, nao muito complexo mas tambem nao trivial, proprio parauma aplicacao movel, criar no sistema operativo Android um programa com algumas dezenasa poucas centenas de linhas de codigo que permita resolver esse problema ou implementar opretendido;

3. (objectivo secundario) Familiarizar o aluno com as classes existentes nas bibliotecas stan-dards do Android;

4. (objectivo secundario) Familiarizar o aluno com o uso do ambiente de desenvolvimento in-tegrado1 Eclipse para escrever codigo, compilar, fazer a depuracao (debug), e pesquisar nadocumentacao das bibliotecas do Android.

1.3 Avaliacao

FIXME avaliacao contınua, trabalhos individuais, etc

1.4 Instalacao Ambiente Desenvolvimento Android

Esta seccao fornece instrucoes sobre onde ir buscar e como instalar dois ambientes de desenvol-vimento para Smartphones ou Tablets com o sistema operativo Android. Ambos os sistemas dedesenvolvimento sao baseados no Eclipse, mas um deles utiliza uma versao normal do Eclipse paradesenvolvimento na linguagem de programacao Java (isto e, funciona a custa da instalacao deum Plugin), enquanto que o outro utiliza uma versao especial do Eclipse que ja vem incluıda noficheiro de instalacao e permite desenvolver para Android utilizando a linguagem de programacaoC/C++.

A vantagem do primeiro sistema de desenvolvimento (disponibilizado pela Google) e que re-presenta a maneira standard de desenvolver software para Android (usando Java), a vantagemdo segundo sistema de desenvolvimento (disponibilizado pela MoSync AB) e que permite escreverprogramas em C/C++ que depois sao convertidos para Java (de modo a poderem ser executadosno Android), permitindo igualmente que a partir do mesmo codigo fonte se crie programas paraoutros tipos de Smartphones (iPhone, Symbian, etc.)

As duas seccoes que se seguem, criadas em 1 de Outubro 2012 pelo que se referem as versoesde software existentes nessas datas, possuem objectivos distintos. A primeira apresenta a listado software a instalar, consoante a versao do sistema operativo da maquina de desenvolvimento(Windows de 32 bits, Windows de 64 bits, Mac OS X, Linux), e a segunda seccao apresenta emdetalhe o modo de instalar ambos os sistemas de desenvolvimento em Windows 7 Professional de64 bits (o sistema operativo instalado na maioria dos computadores do ISMAI).

A seccao 1.4.4 sintetisa a informacao sobre novas versoes do software que tenham saıdo apos 1de Outubro 2012 e chama a atencao para eventuais problemas, incompatibilidades ou diferencasde comportamento.

1IDE, Integrated Development Environment

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 3: Android Devel

Computacao Movel 3

1.4.1 Lista de Software a instalar

Para criar software para Smartphones ou Tablets com o sistema operativo Android, usando o am-biente de desenvolvimento Eclipse, e necessario obter e instalar nesta ordem, em funcao do sistemaoperativo e da arquitectura (32 bits ou 64 bits) da maquina utilizada para o desenvolvimento:

1. JDK (java development kit) 1.7.0_72

Windows XP, Vista ou 7 (32 bits): jdk-7u7-windows-i586.exe

Windows Vista ou 7 (64 bits): jdk-7u7-windows-x64.exe

Mac OS X (Lion e Mountain Lion): jdk-7u7-macosx-x64.dmg

Mac OS X (Snow Leopard 10.6.8): aparentemente o jdk-7u7-macosx-x64.dmg nao fun-ciona nesta versao, utilizar o software update (actualizacoes do sistema operativo) paraautomaticamente obter a ultima versao do JDK (1.6.0_35), nao tentar instalar oudescarregar o software de outra forma. Para verificar a versao do Java instalada abrir oTerminal (Applications/Utilities/Terminal) e escrever “javac -version”, deve-se obtercomo resposta “javac 1.6.0_35”

Linux 32 bits: jdk-7u7-linux-i586.tar.gz (ou jdk-7u7-linux-i586.rpm se for paraum sistema que suporte RPMs)

Linux 64 bits: jdk-7u7-linux-x64.tar.gz (ou jdk-7u7-linux-x64.rpm se for para umsistema que suporte RPMs)

2. Eclipse3 (tem de ser Eclipse 3.6.2 ou superior, podendo ser o Eclipse Classic, ou o EclipseIDE for Java Developers, ou o Eclipse IDE for Java EE Developers4):

Windows XP, Vista ou 7 (32 bits): eclipse-SDK-4.2.1-win32.zip (Eclipse Classic)ou eclipse-java-juno-SR1-win32.zip (Eclipse IDE for Java Developers)

Windows Vista ou 7 (64 bits): eclipse-SDK-4.2.1-win32-x86_64.zip (Eclipse Clas-sic) ou eclipse-java-juno-SR1-win32-x86_64.zip (Eclipse IDE for Java Developers)

Mac OS X Snow Leopard 10.6.8: eclipse-SDK-4.2.1-macosx-cocoa-x86_64.tar.gz(Eclipse Classic 64 bits) ou eclipse-SDK-4.2.1-macosx-cocoa.tar.gz (Eclipse Clas-sic 32 bits) ou eclipse-java-juno-SR1-macosx-cocoa-x86_64.tar.gz (Eclipse IDEfor Java Developers 64 bits) ou eclipse-java-juno-SR1-macosx-cocoa.tar.gz (EclipseIDE for Java Developers 32 bits)

Linux 32 bits: eclipse-SDK-4.2.1-linux-gtk.tar.gz

Linux 64 bits: eclipse-SDK-4.2.1-linux-gtk-x86_64.tar.gz

3. Android SDK5

Windows XP, Vista ou 7 (32 bits): installer_r20.0.3-windows.exe

Windows Vista ou 7 (64 bits): installer_r20.0.3-windows.exe (identico versao 32bits)

Mac OS X Snow Leopard 10.6.8: android-sdk_r20.0.3-macosx.zip

Linux 32 bits (pode ser de 64 bits mas com capacidade para correr aplicacoes de 32 bits):android-sdk_r20.0.3-linux.tgz

FIXME 4-oct-2012: below not yet updated2 http://www.oracle.com/technetwork/java/javase/downloads/jdk7u7-downloads-1836413.html3 http://www.eclipse.org/downloads/4E preferıvel nao se escolher o Eclipse IDE for Java EE Developers ja que nao se vai utilizar as funcionalidades

da Enterprise Edition do Java, de resto tanto faz, pode-se escolher o Eclipse Classic ou o Eclipse IDE for JavaDevelopers

5 http://developer.android.com/sdk/index.html

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 4: Android Devel

4 Computacao Movel

4. Android Development Tools (ADT), plugin para o Eclipse:

(a) Arrancar com o Eclipse, seleccionar Help > Install New Software ...

(b) Clicar Add no canto superior direito

(c) Na janela de dialogo “Add Repository” que aparece colocar “ADT Plugin” no Namee na URL da Location colocar: https://dl-ssl.google.com/android/eclipse/ (seisto der problemas trocar https por http)

5. SDK tools, documentacao, duas plataformas Android, exemplos, e APIs especıficas da Go-ogle e da Samsung:

(a) Android SDK Platform-tools

(b) Documentation for Android SDK

(c) SDK Platform Android 2.3.4 (API Level: 10)

(d) SDK Platform Android 3.1 (API Level: 12)

(e) Samples6 for SDK API 10

(f) Samples for SDK API 12

(g) Third party add-ons:

i. Google Inc. add-ons (dl-ssl.google.com), necessario para usar Google Mapsii. Samsung Electronics add-ons (innovator.samsungmobile.com)

6. Instalar o MoSync7 2.5, que permite escrever um programa em C/C++ e automaticamenteconverter o mesmo para este poder ser executado em iOS (iPhone / iPad /iPod Touch),Android 1.5, 1.6, 2.1, Java ME MIDP 2, Symbian S60 (2nd, 3rd, 5th editions), WindowsMobile 5.0-6.5, Windows Pocket PC 2003, ou Windows Smartphone 2003. Espera-se8 queate ao fim do ano de 2011 o Mosync passe a ter suporte para Blackberry e Windows Phone7 (WP7).

Note-se que esta e uma forma alternativa de desenvolver software para o Android. Nestecaso utiliza-se a linguagem de programacao C/C++ em vez de Java embora depois o codigoseja convertido9 para Java pelo que corre nativamente no Android (e o mesmo codigo e comoque convertido para a linguagem de programacao Objective C de modo a correr no iPhone).

Windows XP, Vista ou 7 (32 bits): MoSyncWindows-2.5.exe

Windows Vista ou 7 (64 bits): MoSyncWindows-2.5.exe (Nota: e provavel que depoisde instalado nao funcione a nao ser que se instale um Java Run-time Environment(JRE) de 32 bits)

Mac OS X Snow Leopard 10.6.8: MoSyncOSX-2.5.dmg

Linux 32 bits: pode-se tentar criar o MoSync a partir do codigo fonte mas nem tudo esuportado10, pelo que nao e aconselhado o uso do MoSync em Linux (excepto dentrode uma maquina virtual a correr Windows)

1.4.2 Windows 7 Pro 64 bits: Instalar JDK 1.6.0 64 bits + Eclipse 64bits + Android SDK 32 bits + ADT Plugin + MoSync 32 bits +JRE 1.7.0 32 bits

A instalacao em Windows 7 versao de 64 bits introduz alguns problemas visto que nem todas asferramentas sao de 64 bits (algumas sao de 32 bits) pelo que e necessario resolver alguns problemas

6Exemplos de codigo7 http://www.mosync.com/download8 http://www.mosync.com/content/mosync-roadmap9 http://www.mosync.com/documentation/manualpages/runtime-architecture

10 http://www.mosync.com/documentation/manualpages/building-mosync-source-linux

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 5: Android Devel

Computacao Movel 5

adicionais de modo a conseguir que elas fiquem a funcionar correctamente. O que e descrito abaixofoi testado numa maquina virtual criada especificamente para permitir efectuar este teste, peloque se forem seguidos (nesta ordem) os passos aqui indicados o sistema deve ficar a funcionarcorrectamente:

1. Instalacao de Windows 7 Professional com o Service Pack 1, versao de 64 bits, numa maquinavirtual (neste caso o VMware Fusion 3.1.3 que suporta o Windows 7 Service Pack 1 de 64bits, a correr em Mac OS X 10.6.8). Foi usada a versao inglesa do Windows 7 mas nao harazao para se esperar problemas com a utilizacao da versao portuguesa.

2. Aplicar todas as actualizacoes ao sistema operativo Windows 7 Pro e instalar um antivirus(neste caso foi usado o Microsoft Security Essentials11)

3. Em 28 de Julho 2011 a Oracle disponibilizou a nova versao do java, o Java Development Kit(JDK) 1.7.0. Embora de uma forma geral o JDK 1.7.0 seja compatıvel com o JDK 1.6.0_26existe uma diferenca ao nıvel do codigo gerado12 (byte codes para a maquina virtual java) namedida em que a versao do ficheiro .class gerado pelo JDK 1.7.0 e a 51 (devido a introducaodo invokedynamic byte code), e a versao dos ficheiros criados pelo JDK 1.6.0 e a 5013. Istosignifica que nalgumas circunstancias se bem que nao existam problemas em usar o JavaRun-Time 1.7 para correr codigo de versoes anteriores do Java podem existir problemas sese compilar com o JDK 1.7 e se executar com versoes mais antigas do JRE. Assim sendo,para evitar problemas optou-se por se instalar a ultima versao do JDK 1.6 de 64 bits e, pararesolver um problema de incompatibilidade entre 32 e 64 bits ao nıvel de carregar bibliotecasdinamicas (DLLs), instalar-se igualmente a versao 1.7 do JRE de 32 bits.

Instalar o JDK 1.6.0_26 escolhendo a versao de 64 bits14: jdk-6u26-windows-x64.exe

Se o JDK estiver instalado mas nao estiver definida a variavel de ambiente JAVA_HOME algumsoftware pode nao encontra-lo. Para que isso nao aocnteca e para que a partir da linhade comando sem se colocar o caminho completo se possa executar o compilador de java(javac.exe) e necessario actualizar o PATH e criar o JAVA_HOME:

Control Panel / System and Security / System / Advanced system settings / Environ-ment Variables / System variables / seleccionar Path / Edit ... adicionar ao fim destaalgo de semelhante a “;c:\program files\java\jdk1.7.0_7\bin” (nao esquecer de co-locar o ponto e vırgula como separador, muita cautela para nao estragar o que ja estavano Path, senao o computador pode deixar de funcionar correctamente). Criar igualmenteuma nova variavel de ambiente chamada JAVA_HOME e colocar nela o valor “c:\programfiles\java\jdk1.7.0_7” (isto e, nao tem o ponto e vırgula nem tem o subdirectorio bin).

4. Instalar o 7-Zip15 de modo a se ter uma ferramenta de linha de comando que permitadescomprimir arquivos ZIP, e adicionar a localizacao do binario do 7z e dos binarios do JDKao PATH16

11 http://www.microsoft.com/en-us/security_essentials/default.aspx12 http://www.oracle.com/technetwork/java/javase/compatibility-417013.html13JDK 1.1 cria classes com a versao 45, JDK 1.2 versao 46, JDK 1.3 versao 47, JDK 1.4 versao 48, JDK 1.5

versao 49, JDK 1.6 versao 50, JDK 1.7 versao 51.14 http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u26-download-400750.html15 http://www.7-zip.org, seleccionar a versao de 64 bits: 7z920-x64.msi16Abrir o painel de controle, seleccionar Systam and Security, System, Advanced System Settings, Environment

Variables, System variables, seleccionar a variavel Path (pode ser necessario fazer scroll para a ver na lista devariaveis do sistema), escolher Edit... e adicionar ao fim do Variable value um ponto e vırgula se nao existirja um no fim do texto, seguido do caminho completo para o directorio onde o 7-Zip foi instalado (se se acei-tou o valor do local fornecido por omissao para a instalacao do programa este ficou instalado em “c:\ProgramFiles\7-Zip”), outro ponto e vırgula, e o caminho completo para o directorio onde o JDK ficou instalado(se se aceitou o valor do local fornecido por omissao para a instalacao do programa este ficou instalado em“c:\Program Files\jdk1.6.0_26” e os binarios estao no subdirectorio bin), pelo que se deve adicionar ao Path

a string “;c:\program files\7-Zip;c:\Program Files\jdk1.6.0_26\bin”.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 6: Android Devel

6 Computacao Movel

5. Descarregar o Eclipse 3.7.0 Classic17 para Windows 64 bit: eclipse-SDK-3.7-win32-x86_64.zip.Mover o ficheiro zip para o directorio onde se pretende instalar o software (por exem-plo c:\Program Files) e descomprimir o ficheiro usando por exemplo o comando “7z xeclipse-SDK-3.7-win32-x86_64.zip”, isto cria o sub-directorio eclipse. Para correr oeclipse executar o programa “c:\Program Files\eclipse\eclipse.exe”

6. Instalar o SDK do Android18: installer_r12-windows.exe.

NOTA IMPORTANTE: e muito provavel que ao instalar o SDK do Android de o erro deque nao consegue encontrar o JDK, nesse caso basta seleccionar o botao de Back no wizard,e a seguir voltar a seleccionar o botao de Next que na segunda passagem ja encontra o JDK(isto parece acontecer quando se mistura versoes de 64 bits com versoes de 32 bits, existemoutras solucoes para este problema mas esta e a mais simples19).

Ao acabar a instalacao aceitar a opcao de deixar correr o SDK Manager do Android SDKTools para instalar as imagens das plataformas que interessarem, ele selecciona automatica-mente um conjunto de plataformas, adicionar a essas ja pre-seleccionadas as seguintes (bastaclicar no nome do lado esquerdo e depois seleccionar o radio button Accept):

• Google APIs by Google Inc., Android API13, revision 1• Google USB Driver Package, revision 4

Isto instala um total de 23 pacotes.

7. Instalar o Android Development Tools (ADT), plugin para o Eclipse:

(a) Arrancar com o Eclipse, seleccionar Help > Install New Software ...

(b) Clicar Add no canto superior direito(c) Na janela de dialogo “Add Repository” que aparece colocar “ADT Plugin” no Name

e na URL da Location colocar: https://dl-ssl.google.com/android/eclipse/ (seisto der problemas trocar https por http). Instalar os 6 pacotes embora so os 4 primei-ros sejam relevantes: Android DDMS, Android Development Tools, Android HierarchyViewer, Android Traceview, Tracer for OpenGL ES, Android Native Development To-ols. Ao instalar aceitar a licenca e aceitar instalar conteudo nao assinado, depois dese rearrancar com o Eclipse em Help / About / Installation details no tab Installedsoftware aparecem esses 4 ou 6 nomes ao lado do Eclipse SDK

8. No Eclipse seleccionar Window /Preferences, escolher Android do lado esquerdo, e colocar odirectorio do SDK do Android, se ele ficou instalado no local sugerido por omissao trata-sede C:\Program Files (x86)\Android\android-sdk (atencao que como este local consistenum nome que tem espacos embebidos isto pode originar um erro, ver Seccao 1.4.6)

A partir de agora ja deve ser possıvel ir ao Eclipse e seleccionar, File / New Project, expandira entrada Android, seleccionar Android Project, fazer Next, e deve ser visıvel a possibilidadede criar o projecto para um conjunto alargado de versoes da API do Android (desde a 1.5ate a 3.2)

9. Instalar o MoSync20 2.5: MoSyncWindows-2.5.exe

NOTA IMPORTANTE: Muito provavelmente depois de se instalar o MoSync este daerro ao arrancar, algo do genero de nao conseguir carregar uma biblioteca (apesar do fi-cheiro existir no caminho indicado na mensagem de erro): “Failed to load the JNI shared li-brary ”C:\Program Files\Java\jdk1.6.0_26\bin\..\jre\bin\server\jvm.dll”. Trata-se muito provavelmente do problema de se estar a usar o JRE que veio junto com o JDK eque portanto e de 64 bits e o MoSync estar a espera de um JRE de 32 bits.

17 http://www.eclipse.org/downloads/18 http://developer.android.com/sdk/index.html19 http://stackoverflow.com/questions/4382178/android-sdk-installation-doesnt-find-jdk20 http://www.mosync.com/download

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 7: Android Devel

Computacao Movel 7

A melhor solucao para este erro e instalar agora (isto e, nao instalar antes mas somentese e quando este erro aparece) a versao de 32 bits do Java Run-time Environment (JRE)1.7.0. Ao se escolher esta versao facilita-se conseguir-se distinguir mais facilmente entre as 2versoes do Java, isto e, o JDK 1.6.0 + JRE 1.6.0 de 64 bits e o JRE 1.7.0 de 32 bits, ja que sepode usar os comandos “java -version” ou “javac -version” para, respectivamente, sesaber a versao do run-time e a versao do compilador de Java. Portanto neste caso instala-seo JRE 1.7.0 de 32 bits21: jre-7-windows-i586.exe

Apos o JRE 1.7.0 de 32 bits estar instalado voltar a tentar arrancar o MoSync e este erro deveter desaparecido e o programa deve arrancar normalmente. E agora necessario proceder aoregisto do software criando um username e uma password e fornecendo um email, recebendodepois por email o link que activa o MoSync.

1.4.3 Mac OS X 64 bits: Instalar Eclipse 64 bits + Android SDK +ADT Plugin + MoSync

Usar as explicacoes detalhadas apresentadas na Seccao 1.4.2, adaptando-as ao ambiente Mac OSX:

1. Instalar o Eclipse IDE for Java Developers (versao para Mac OS X 64 bits)22

2. Instalar o SDK do Android. Por exemplo descarregar o ficheiro com a ultima versao doSDK23, abrir um terminal (/Applications/Utils/Terminal.app) e executar os seguintescomandos:

7 〈Mac OS X: instalar o Android SDK 7〉≡cd /

mkdir /Developer2

cd /Developer2

unzip ~/Downloads/android-sdk_r15-macosx.zip

estando isto feito tem-se acesso ao SDK Manager. Escolheu-se instalar em /Developer2para o caso de se ter instalado o Xcode da Apple em /Developer. Em Mac OS X o SDKManager e o programa /Developer2/android-sdk-macosx/tools/android

3. Usando o SDK Manager instalar as imagens das plataformas que interessarem

4. Instalar o Android Development Tools (ADT), plugin para o Eclipse

5. No Eclipse seleccionar Eclipse / Preferences... /Android, e colocar em “SDK location” odirectorio onde se instalou o SDK, neste caso trata-se de /Developer2/android-sdk-macosx

6. Instalar o MoSync

1.4.4 Actualizacoes Outubro 2011

A versao instalada nos computadores do ISMAI baseou-se no conteudo da Seccao 1.4.2, que foiescrita em 31 de Julho 2011. Entretanto sairam novas versoes de software:

• Em 16 de Setembro 2011 foi disponibilizada a versao de producao do MoSync 2.6; inclui24

actualizacoes da API (nomeadamente maquina fotografica, manipular os contactos, fontesnativas e sensores, e uma API multi-plataforma para o Facebook); suporta a utilizacao deemuladores nativos (isto e, usar o emulador do iOS ou do Android em vez do MoRE); esuporta profiling25 no MoRE.

21 http://www.oracle.com/technetwork/java/javase/downloads/java-se-jre-7-download-432155.html22 http://www.eclipse.org/downloads e seleccionar eclipse-java-indigo-SR1-macosx-cocoa-x86_64.tar.gz23 http://dl.google.com/android/android-sdk_r15-macosx.zip24 http://www.mosync.com/documentation/manualpages/whats-new-mosync-26-pyramid25Ver glossario

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 8: Android Devel

8 Computacao Movel

• Em 25 de Outubro 2011 foi disponibilizada a versao de producao do MoSync 2.7; inclui26

suporte para HTML5, suporte para a C++ Standard Template Library (STL), e suportepara a NFC (Near Field Communication) API

• Em 22 de Setembro 2011 foi disponibilizado o SDK r13 do Android que inclui o SDK platformAndroid 3.2 (API level 13).

• Em 19 de Outubro 2011 foi disponibilizado o SDK r14 do Android que inclui o SDK platformAndroid 4.0 (API level 14), e em 28 de Outubro o SDK r15 (bug fixes do r14)

Apesar disso originar software ligeiramente diferente do disponıvel no ISMAI, nos computadorespessoais de cada aluno nao parece haver problema em actualizar o MoSync da versao 2.5 para a2.7 nem em usar o SDK Android r15, desde que no Android tambem se instale as APIs 2.3.4 (APIlevel 10), 3.1 (API level 12) e os Samples (exemplos) relativos ao SDK API 10 e API 12.

1.4.5 APIs do Android

O API level identifier no Android serve para identificar qual e a versao mais alta da frameworkque e suportada pelo dispositivo, permite que as aplicacoes digam qual e a versao da frameworkque necessitam, e permite que o sistema operativo controle que nao sejam instaladas no disposi-tivo aplicacoes que necessitem de versoes da framework Android que nao sejam suportadas nessedispositivo.

As aplicacoes no ficheiro manifest.xml podem usar o elemento <uses-sdk> para dizer quale a versao mınima da framework que suportam (android:minSdkVersion), a versao da fra-mework para a qual a aplicacao foi desenhada (android:targetSdkVersion, so passou a existira partir da API 4), e a versao maxima da framework na qual a aplicacao consegue funcionar(android:maxSdkVersion, so passou a existir a partir da API 4), esta ultima deve ser usada combastante cautela porque pode originar que uma aplicacao desapareca do telemovel logo a seguirao sistema operativo ser actualizado e as frameworks do Android sao concebidas para serem com-patıveis com as versoes anteriores. Devido a isto versoes do Android superiores a 2.0.1 deixaram deobedecer a android:maxSdkVersion durante a instalacao ou actualizacao de software, somente oAndroid Market e que continua a utilizar este atributo como um filtro ao apresentar as aplicacoesdisponıveis para download.

Eis a lista das versoes do SDK, da API e da versao do SQLite3 utilizada, e as respectivas datasde disponibilizacao:

19-Ago-2008: Android SDK 0.9 Beta

25-Set-2008: Android SDK 1.0 R1

8-Dez-2008: Android SDK 1.0 R2 (versao 1.0 base, API 1)

7-Mar-2009: Android SDK 1.1 R1 (versao 1.1 base 1 1, API 2)

15-Abr-2009: Android SDK 1.5 Pre

28-Abr-2009: Android SDK 1.5 R1

26-Jun-2009: Android SDK 1.5 R2

17-Jul-2009: Android SDK 1.5 R3 (versao 1.5 cupcake, API 3), SQLite 3.5.9

16-Set-2009: Android SDK 1.6 Release 1 (versao 1.6 donut, API 4), SQLite 3.5.9

28-Out-2009: Android SDK 2.0 Release 1 (versao 2.0 eclair, API 5), SQLite 3.5.9

4-Dez-2009: Android SDK 2.0.1 Release 1 (versao 2.0.1 eclair 0 1, API 6), SQLite 3.5.9

26 http://www.mosync.com/documentation/manualpages/whats-new-mosync-27-pyramid

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 9: Android Devel

Computacao Movel 9

13-Mai-2010: Android SDK R5

21-Mai-2010: Android SDK R6

6-Dez-2010: Android SDK R7 (versao 2.1.x eclair MR1, API 7), SQLite 3.5.9

6-Dez-2010: Android SDK R8 (versao 2.2.x froyo, API 8), SQLite 3.6.22

27-Jan-2011: Android SDK R9 (versao 2.3, 2.3.1, 2.3.2 gingerbread, API 9), SQLite 3.6.22

23-Fev-2011: Android SDK R10 (versao 2.3.3 e 2.3.4 gingerbread MR1, API 10), SQLite 3.6.22

16-Jul-2011: Android SDK R11 (versao 3.0.x honeycomb, API 11), SQLite 3.7.4

16-Jul-2011: Android SDK R12 (versao 3.1.x honeycomb MR1, API 12), SQLite 3.7.4

23-Set-2011: Android SDK R13 (versao 3.2 honeycomb MR2, API 13), SQLite 3.7.4

19-Out-2011: Android SDK R14 (versao 4.0.1 ice cream sandwich, API 14), SQLite 3.7.4

28-Nov-2011: Android SDK R15 (versao 4.0.2 ice cream sandwich, API 15), SQLite 3.7.4

16-Dez-2011: Android SDK R16 (versao 4.0.3 ice cream sandwich, API 15), SQLite 3.7.4

Mar-2012: Android SDK R17

Apr-2012: Android SDK R18

Apr-2012: Android SDK R19

9-Jul-2012: Android SDK R20 (versao 4.1.2 jelly bean, API 16)

13-Nov-2012: Android SDK R21 (versao 4.2 jelly bean, API 17)

24-Jul-2013: Android SDK R22 (versao 4.3 jelly bean, API 18)

??-Out-2013: Android SDK R2? (versao 4.4 kit kat, API 19)

Detalhes das principais funcionalidades associadas a cada versao do SDK podem ser obtidas dasrelease notes27, detalhes das principais funcionalidades associadas a cada nıvel da API podem serobtidas de http://developer.android.com/guide/appendix/api-levels.html

1.4.6 Configurar o Eclipse para criar uma aplicacao Android

Depois de se instalar o software de desenvolvimento para Android (ver Seccao 1.4.2 ou Seccao 1.4.3)e necessario configurar o Eclipse para que este permita criar programas em Android. Sendo assimdeve-se efectuar a seguinte sequencia de passos:

1. Criar um novo Workspace no caso do workspace do Eclipse que estivermos a usar ja contercodigo de outras aplicacoes Java. Para isso usa-se “File / Switch workspace / Other ...” eescolhe-se um novo nome.

2. Embora neste momento ja se possa escolher “File / New / Android project”, a verdadee que nao conseguimos terminar o wizard (isto e, so podemos premir cancel) porque naoconseguimos seleccionar um SDK target

27 http://mac.softpedia.com/progChangelog/Google-Android-SDK-Changelog-31208.html

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 10: Android Devel

10 Computacao Movel

3. Para que um ou mais SDK targets estejam disponıveis e necessario ir a “Window / Preferen-ces / Android” (em Mac OS X e “Eclipse / Preferences... /Android) e preencher o caminhopara o SDK Location.

Note-se que se estivermos numa maquina Windows 7 de 64 bits e o SDK for instalado no localseleccionado por omissao ele fica em “C:\Program Files (x86)\Android\android-sdk”,e isso origina problemas devido ao caminho possuir espacos embebidos. Embora se possapremir o botao Apply e mal isso seja feito apareca a lista com as diversas versoes do SDK queestao instalados, se depois criarmos uma aplicacao Android usando “File / New / AndroidProject”, se depois se tentar compilar e correr essa aplicacao obtem-se o erro:

10 〈erros: erro se o Android SDK foi instalado num local com espacos no nome 10〉≡...

[2011-10-30 22:11:36 - lixo1] Launching a new emulator with Virtual Device ’Avs1’

[2011-10-30 22:11:36 - Emulator] invalid command-line parameter: Files.

[2011-10-30 22:11:36 - Emulator] Hint: use ’@foo’ to launch a virtual device named ’foo’.

[2011-10-30 22:11:36 - Emulator] please use -help for more information

note-se que o erro “invalid command-line parameter: Files.” advem do local de instalacaoser “C:\Program Files (x86)\Android\android-sdk”, isto e, o Files da mensagem deerro e a parte do nome que vem logo a seguir ao espaco. A solucao e no “Windows /Preferences / Android” alterar o caminho passando a usar o nome curto do directorio (nomecom um maximo de 8 caracteres e que nao possui espacos). Para saber qual e o nome curtode um directorio ir para uma consola (linha de comando) do Windows e executar o comando“dir /x c:\”, isto deve dizer algo do genero que o nome curto de Program Files (x86) ePROGRA~2, nesse caso no “Windows / Preferences / Android” substituir “C:\Program Files(x86)\Android\android-sdk” por “C:\PROGRA~2\Android\android-sdk”. A partir daı jase consegue compilar e executar no emulador programas Android.

1.4.6.1 Threads

Hoje em dia a maioria dos programas que aceitam interaccoes com o utilizador ja nao se podemdar ao luxo de serem single-threaded, isto e, so terem um fluxo ou sequencia de execucao, etem de ser multi-threaded, isto e, possuem mais do que uma sequencia de execucao a executarsimultaneamente28.

Uma thread e um processo mais leve, isto e que utiliza muito menos recursos do que criar umnovo processo. Todas as threads existem dentro do mesmo processo, pelo que partilham recursosincluindo a memoria e os ficheiros abertos. A desvantagem e que qualquer outra thread podepotencialmente ter acesso aos dados de uma thread e se algo correr mal numa thread o processopode fazer crash, o que termina todas as threads.

Cada thread esta associada com uma instancia da classe Thread.Uma aplicacao cria uma thread e fornece o codigo que deve ser executado nessa thread. Existem

duas maneiras de fazer isto:

• Criar um objecto que implemente o interface Runnable. O interface Runnable define umunico metodo, run() que e suposto conter o codigo que e para ser executado na thread. Oobjecto que implementa o interface Runnable e passado ao construtor da classe Thread.

• Criar uma subclasse de Thread. Como Java nao suporta heranca multipla e normalmentepreferıvel a primeira opcao, isto e, a nossa classe implementar o interface Runnable em vezde ser uma subclasse de Thread, desta forma pode herdar de quem quiser

O exemplo que se segue cria 3 threads, cada uma delas dorme, respectivamente, durante 300,200 e 400 milisegundos antes de acordar e continuar. Note-se que pode ocorrer a excepcao

28Mesmo em sistemas com processadores so com um core utiliza-se programas multi-threaded, nesses casos quandouma thread e interrompida ou executa ate ao fim da unidade de tempo que lhe foi atribuıda outra thread fica como controle do CPU.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 11: Android Devel

Computacao Movel 11

InterruptedException que e lancada pelo sleep quando outra thread tenta interromper estathread enquanto sleep esta activo. Este programa origina o seguinte output:

Hello da thread um, vou dormir durante 300 milisegundosHello da thread dois, vou dormir durante 200 milisegundosHello da thread tres, vou dormir durante 400 milisegundosHello outra vez da thread dois, acordeiHello outra vez da thread um, acordeiHello outra vez da thread tres, acordei

11 〈version/androidDevel/HelloNumaThread.java 11〉≡import java.lang.InterruptedException;

public class HelloNumaThread implements Runnable {

protected String nome;

protected int duracaoDoSono;

public HelloNumaThread(String s, int tempo) {

nome = s;

duracaoDoSono = tempo;

}

public void run() {

System.out.println("Hello da thread " + nome + ", vou dormir durante " +

duracaoDoSono + " milisegundos");

try {

Thread.sleep(duracaoDoSono);

} catch (InterruptedException e) {

System.out.println("Hello outra vez da thread " + nome + ", fui acordada");

}

System.out.println("Hello outra vez da thread " + nome + ", acordei");

}

public static void main(String args[]) {

Thread t1, t2, t3;

t1 = new Thread(new HelloNumaThread("um", 300));

t2 = new Thread(new HelloNumaThread("dois", 200));

t3 = new Thread(new HelloNumaThread("tres", 400));

t1.start();

t2.start();

t3.start();

}

}

This code is written to file version/androidDevel/HelloNumaThread.java.Defines:

HelloNumaThread, never used.Uses main().

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 12: Android Devel

12 Computacao Movel

1.5 Arquitectura das Aplicacoes Android

As aplicacoes Android recorrem a utilizacao de um conjunto de 6 classes principais (Intent,IntentFilter, Activity, Service, BroadcastReceiver, e ContentProvider), e a utilizacao deum conjunto de 3 ficheiros XML principais (AndroidManifest.xml, main.xml, e strings.xml).Eis o contexto da sua utilizacao e as suas caracterısticas principais:

1. Classe Intent: representa aquilo que temos a intencao de fazer (por exemplo ver o conteudode um registo dos contactos, abrir um site internet, mostrar uma dada factura). Destaforma um Intent deve ser visto como uma declaracao de uma necessidade que necessita deser satisfeita (por um IntentFilter).

• O atributo accao (action) de um Intent e normalmente um verbo, por exemploVIEW, PICK, ou EDIT. Para ver o conteudo de um registo podia-se por exemplo usarandroid.content.Intent.ACTION_VIEW

• O atributo dados (data) de um Intent e uma URI. Por exemplo para ver todosos contactos usa-se content://contacts/people, e para ver o contacto 18 usa-secontent://contacts/people/18

• Para por exemplo obter os registos dos contactos usar-se-ia algo do genero de:12a 〈Android: ver os registos da base de dados de contactos 12a〉≡

Intent pickIntent = new Intent(

Intent.ACTION_PICK,Uri.parse("content://contacts/people"));

startActivity(pickIntent);

• A identificacao do IntentFilter que deve ser usado e efectuada em run-time (em vezde em compile time). Ha a possibilidade de se substituir funcionalidade standard doAndroid substituindo um IntentFilter (mas nao esquecer que isto torna mais difıcilpor exemplo o suporte tecnico remoto ja que deixa de se ter a certeza de numa dadasituacao qual e o comportamento efectivo do dispositivo movel em questao)

2. Classe IntentFilter: declaracao da capacidade e interesse em disponibilizar uma dada fun-cionalidade para quem dela necessite (um Intent). Os IntentFilter’s tem de ser registados,esse processo e feito a custa do ficheiro AndroidManifest.xml

3. Classe Activity: embora uma aplicacao Android nao tenha de ter um GUI, se o tiverentao tera obrigatoriamente pelo menos um Activity. Normalmente existe uma relacaode um para um entre um Activity e um ecra visıvel. Um Activity efectua o display deum interface grafico e responde a eventos iniciados pelo sistema e pelos utilizadores. Umadas tarefas principais efectuadas pela classe Activity e efectuar o display dos elementos dointerface grafico (que sao implementados na forma de Views) e sao definidos em ficheirosXML com o layout. Normalmente a interaccao e assıncrona (isto e, nao se fica bloqueado aespera do resultado), mas se se quiser uma interaccao sıncrona ela e possıvel (utilizando ometodo startActivityForResult())

12b 〈Android: exemplo de uma Activity 12b〉≡package com.msi.manning.chapter1;

import android.app.Activity;

import android.os.Bundle;

public class Activity1 extends Activity {

@Override

public void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

}

Uses onCreate() 27.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 13: Android Devel

Computacao Movel 13

4. Classe Service: se a aplicacao e suposto ter um tempo de execucao longo (por exemplo umatarefa de backup) entao ela deve ser colocada num servico. Os servicos devem ser iniciadosperiodicamente ou quando forem necessarios sendo nesse caso iniciados como reaccao a umalarme do sistema, e devem terminar a execucao (de modo a libertar recursos) quando atarefa estiver terminada. Os servicos nao possuem um GUI [Ableson et al. 2011, pag. 18]

13 〈Android: exemplo de um Service 13〉≡package com.msi.manning.chapter1;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.util.Log;

public class Service1 extends Service implements Runnable {

public static final String tag = "service1";

private int counter = 0;

@Override

protected void onCreate() {

super.onCreate();

Thread aThread = new Thread (this);

aThread.start();

}

public void run() {

while (true) {

try {

Log.i(tag, "service1 firing : # " + counter++);

Thread.sleep(10000);

} catch(Exception ee) {

Log.e(tag,ee.getMessage());

}

}

}

@Override

public IBinder onBind(Intent intent) {

return null;

}

}

Uses onCreate() 27.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 14: Android Devel

14 Computacao Movel

5. Classe BroadcastReceiver: aplicacoes que pretendem reagir a um evento global (como porexemplo a recepcao de uma mensagem ou o telefone tocar). O registo pode ser feito de 2maneiras diferentes [Ableson et al. 2011, pag. 19]:

(a) Colocando uma tag <receiver> no AndroidManifest.xml que descreve o nome daclasse BroadcastReceiver e enumera os seus <IntentFilter>’s. Neste caso a aplicacaonao necessita de estar a executar para ser chamada a lidar com um evento global

(b) Registando-se em run-time (isto e quando estiver a correr) usando o metodo registerReceiver

Os BroadcastReceiver nao possuem um GUI e devem demorar um tempo muito curtoa executar (semelhante ao tempo associado a um interrupt handler). Se a tarefa podedemorar bastante tempo a executar entao o BroadcastReceiver deve e iniciar um Servicepara executar a funcionalidade pretendida

6. Classe ContentProvider: para a aplicacao gerir dados (ver Seccao ??)

7. Ficheiro AndroidManifest.xml: existe uma relacao de um para um entre um destes ficheirose a aplicacao correspondente. Eis um exemplo:

14 〈Android: exemplo de um AndroidManifest.xml 14〉≡<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.msi.manning.unlockingandroid">

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

<application android:icon="@drawable/icon">

<activity android:name=".Activity1" android:label="@string/app_name">

<intent-filter>

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

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<receiver android:name=".MySMSMailBox" >

<intent-filter>

<action android:name="android.provider.Telephony.SMS_RECEIVED" />

</intent-filter>

</receiver>

</application>

</manifest>

8. Ficheiro main.xml: usado para descrever o layout dos componentes do interface grafico. NoEclipse pode ser editado directamente em modo texto ou manipulado graficamente, podendomisturar-se os dois tipos de edicao

9. Ficheiro strings.xml: usado para se colocar strings (texto, etiquetas, mensagens, etc) quepossam necessitar de ser localizadas ou adaptadas consoante o idioma da aplicacao. NoEclipse pode ser editado directamente em modo texto ou manipulado recorrendo a um in-terface grafico, podendo misturar-se os dois tipos de edicao

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 15: Android Devel

Computacao Movel 15

1.5.1 Hello World em Android

Apresentam-se aqui duas versoes da aplicacao Hello World em Android, a primeira baseada so-mente em dois ficheiros (HelloAndroid1.java e AndroidManifest.xml) mas utilizando um estilode programacao que se bem que mais simples nao e o aconselhavel ja que lhe falta flexibili-dade, e a segunda baseada em quatro ficheiros (HelloAndroid2.java, strings.xml, main.xml,AndroidManifest.xml) recorrendo a tecnica normal que deve ser utilizada para criar aplicacoesAndroid.

1.5.1.1 Hello World em Android mas sem usar o estilo do Android

15a 〈version/androidDevel/helloWorld1/HelloAndroid1.java 15a〉≡package pt.ismai.cm2011;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TextView;

public class HelloAndroid1 extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextView tv = new TextView(this);

tv.setText("Hello World em Android, vers~ao simplificada");

setContentView(tv);

}

}

This code is written to file version/androidDevel/helloWorld1/HelloAndroid1.java.Defines:

HelloAndroid1, used in chunk 15b.Uses onCreate() 27.

E importante a colocacao do “.” no inıcio do “<activity android:name”.15b 〈version/androidDevel/helloWorld1/AndroidManifest.xml 15b〉≡

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="pt.ismai.cm2011" android:versionCode="1"

android:versionName="1.0">

<uses-sdk android:minSdkVersion="3" />

<application android:icon="@drawable/ic_launcher"

android:label="@string/app_name">

<activity android:name=".HelloAndroid1" android:label="@string/app_name">

<intent-filter>

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

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

This code is written to file version/androidDevel/helloWorld1/AndroidManifest.xml.Uses HelloAndroid1 15a.

Para criar este projecto em Eclipse fazer:

1. File / New / Android Project

2. Colocar HelloAndroid1 no Project Name

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 16: Android Devel

16 Computacao Movel

3. Seleccionar Android 1.5 como Build Target

4. Colocar pt.ismai.cm2011 no Package Name

5. Desseleccionar Create Activity (ja que neste caso nao estamos a fazer um projecto Androidusando o estilo de desenvolvimento para Android)

6. No Package Explorer (janela a esquerda do Eclipse) seleccionar e expandir HelloAndroid1 /src / pt.ismai.cm2011, seguidamente premir o botao da direita do rato e seleccionar New /Class e colocar no Name HelloAndroid1

7. Adicionar a este ficheiro o conteudo acima apresentado para HelloAndroid1.java

8. Nota: uma forma simples para se adicionar os import que um ficheiro necessite e premirCtrl-Shift-O (ou Cmd-Shift-O em Mac OS X). Isto e um atalho do eclipse que identificaos pacotes que estao em falta e adiciona-os automaticamente

9. Substituir o conteudo do AndroidManifest.xml pelo acima apresentado

10. Seleccionar Run, escolher run as Android Application, e se necessario criar um new AndroidVirtual Device, a aplicacao deve arrancar no emulador

1.5.1.2 Hello World no estilo Android

16a 〈version/androidDevel/helloWorld2/res/layout/main.xml 16a〉≡<?xml version="1.0" encoding="utf-8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/textview"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:text="@string/hello"/>

This code is written to file version/androidDevel/helloWorld2/res/layout/main.xml.

16b 〈version/androidDevel/helloWorld2/res/values/strings.xml 16b〉≡<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">Hello World em Android, vers~ao correcta</string>

<string name="app_name">Hello Android2</string>

</resources>

This code is written to file version/androidDevel/helloWorld2/res/values/strings.xml.

A ligacao entre estes dois ficheiros XML e o codigo Java e efectuada recorrendo ao ficheiroR.java que e gerado automaticamente.

16c 〈version/androidDevel/helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java 16c〉≡package pt.ismai.cm2011;

import android.app.Activity;

import android.os.Bundle;

public class HelloAndroid2 extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

}

This code is written to file version/androidDevel/helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java.Defines:

HelloAndroid2, used in chunk 17.Uses onCreate() 27.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 17: Android Devel

Computacao Movel 17

17 〈version/androidDevel/helloWorld2/AndroidManifest.xml 17〉≡<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="pt.ismai.cm2011" android:versionCode="1"

android:versionName="1.0">

<uses-sdk android:minSdkVersion="3" />

<application android:icon="@drawable/ic_launcher"

android:label="@string/app_name">

<activity android:name=".HelloAndroid2" android:label="@string/app_name">

<intent-filter>

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

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

This code is written to file version/androidDevel/helloWorld2/AndroidManifest.xml.Uses HelloAndroid2 16c.

Para criar este projecto em Eclipse fazer:

1. File / New / Android Project

2. Colocar HelloAndroid2 no Project Name

3. Seleccionar Android 1.5 como Build Target

4. Colocar pt.ismai.cm2011 no Package Name

5. Manter seleccionado Create Activity

6. No Package Explorer (janela a esquerda do Eclipse) seleccionar e expandir HelloAndroid2 /src / pt.ismai.cm2011 / HelloAndroid2Activity, mantendo o nome do ficheiro seleccionadofazer File / Rename..., e alterar o nome de HelloAndroid2Activity para HelloAndroid2

7. Substituir o conteudo deste ficheiro pelo conteudo acima apresentado para HelloAndroid2.java

8. Substituir o conteudo de res/layout/main.xml pelo conteudo acima apresentado

9. Substituir o conteudo de res/values/strings.xml pelo conteudo acima apresentado

10. Substituir o conteudo do AndroidManifest.xml pelo acima apresentado

11. Seleccionar Run, escolher run as Android Application, e se necessario criar um new AndroidVirtual Device, a aplicacao deve arrancar no emulador

A razao porque este programa funciona como esperado e que o Android arranca o HelloAndroid2,este no onCreate usando setContentView utiliza a informacao existente no main.xml (recorrendoao R.java gerado automaticamente e que liga o ficheiro xml ao codigo java), no main.xml noandroid:text faz-se referencia a @string/hello e em strings.xml o string name="hello" temcomo conteudo "Hello World em Android, vers~ao correcta" e por isso esse texto e visualizado.

1.5.2 Utilizacao do Log do Android

No Android sempre que queremos escrever uma mensagem que fique registada, por exemplo in-formacao sobre um erro que ocorreu no programa ou se pretendemos guardar outro tipo de in-formacao, deve-se escrever para o Android log.

A classe Log contem metodos que permitem adicionar entradas de log com uma classificacaode Verbose, Debug, Information, Warning, e Error. Pode-se filtrar as entradas no log de acordo

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 18: Android Devel

18 Computacao Movel

com a classificacao, o ID do processo que originou a entrada de log, e/ou o valor de uma tagque se adiciona a cada entrada de log. Por exemplo para se criar uma entrada de log com aclassificacao de Information, uma tag de calcular, e a mensagem ‘valor demasiado alto’ faz-se“Log.i("calcular", "valor demasiado alto");”.

Para se ver as entradas no log com capacidade para filtrar e so ver as que nos interessam usa-sea LogCat view da perspectiva DDMS (Window / Open Perspective / DDMS) [Ableson et al. 2011,pag. 40].

18 〈Android: escrever para o log 18〉≡import android.util.Log;

public class Exemplo {

public static final String tag = "oMeuLog";

// ...

final Button button = (Button)findViewById(R.id.calculate);

button.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

try {

Log.i(tag, "chamada a onClick");

// ...

} catch (Exception e) {

Log.e(tag, "excepc~ao ...");

// ...

}

}

});

}

Uses setOnClickListener().

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 19: Android Devel

Computacao Movel 19

1.5.3 GUIs em Android

Pretende-se agora criar uma aplicacao exemplo em Android que utilize TextView (etiqueta detexto), EditText (campo de texto editavel que pode ser configurado para so ler numeros), eButton para permitir escrever um valor base e a percentagem de desconto a aplicar, e que quandose prime o botao obtem-se o valor a pagar. No caso de haver um erro a ler o numero (por exemplopor o campo estar vazio) escreve-se para o log uma mensagem de erro e mostra-se uma janela dedialogo a avisar que o valor e invalido.

19 〈version/androidDevel/descontos/src/pt/ismai/cm2011/DescontosActivity.java 19〉≡package pt.ismai.cm2011;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

public class DescontosActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

final String tag = "descontos";

final AlertDialog.Builder adb = new AlertDialog.Builder(this);

final Button button = (Button)findViewById(R.id.button1);

final EditText valor = (EditText)findViewById(R.id.editValor);

final EditText desconto = (EditText)findViewById(R.id.oDesconto);

final TextView resultado = (TextView)findViewById(R.id.oResultado);

button.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

String umValor = valor.getText().toString();

String umDesconto = desconto.getText().toString();

try {

double dv = Double.parseDouble(umValor);

double dd = Double.parseDouble(umDesconto) / 100;

resultado.setText(new Double(dv * (1 - dd)).toString());

} catch (NumberFormatException e) {

String errMsg = "Valor invalido";

Log.e(tag, errMsg);

AlertDialog ad = adb.create();

ad.setMessage(errMsg);

ad.show();

}

}

});

}

}

This code is written to file version/androidDevel/descontos/src/pt/ismai/cm2011/DescontosActivity.java.Defines:

DescontosActivity, used in chunk 20b.Uses onCreate() 27 and setOnClickListener().

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 20: Android Devel

20 Computacao Movel

Aqui explicita-se o texto a aparecer nas etiquetas (TextView, Button) e o nome da aplicacao.20a 〈version/androidDevel/descontos/res/values/strings.xml 20a〉≡

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="app_name">Descontos</string>

<string name="base">Valor base</string>

<string name="desconto">Desconto</string>

<string name="calcular">Calcular</string>

<string name="vazio"></string>

</resources>

This code is written to file version/androidDevel/descontos/res/values/strings.xml.

20b 〈version/androidDevel/descontos/AndroidManifest.xml 20b〉≡<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="pt.ismai.cm2011"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk android:minSdkVersion="3" />

<application

android:icon="@drawable/ic_launcher"

android:label="@string/app_name" >

<activity

android:label="@string/app_name"

android:name=".DescontosActivity" >

<intent-filter >

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

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

This code is written to file version/androidDevel/descontos/AndroidManifest.xml.Uses DescontosActivity 19.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 21: Android Devel

Computacao Movel 21

Usando o interface para criar layouts graficos disponibilizado pelo Eclipse cria-se um Table-Layout, e coloca-se na primeira linha um TextView seguido de um EditText configurado paranumber (onde o utilizador coloca o valor propriamente dito), na segunda linha coloca-se um Text-View seguido de um EditText configurado para number (onde o utilizador coloca a percentagemde desconto, um numero entre 0 e 100), na terceira linha coloca-se um Button (para mandar cal-cular o valor a pagar apos se aplicar o desconto), e na quarta linha coloca-se um TextView paraapresentar o resultado. Mudando para a visualizacao em modo textual (main.xml) obtem-se algode semelhante ao aqui apresentado.

De modo a que no ficheiro R.java, gerado automaticamente, existam as entradas apropri-adas que nos permitam referenciar os widgets que nos interessam (alguns widgets, por exemploTextView, podem nao ser necessarios referenciar a partir do codigo da aplicacao e por isso escusamde ter um id), coloca-se em cada um deles android:id="@+id/nomeEscolhidoParaID", isto vaifazer com que em R.java apareca algo do tipo “public final class R { ... public staticfinal class id { public static final int nomeEscolhidoParaID=0x7f050000; ... } ...}”. No Eclipse em vez de se editar o main.xml directamente pode-se controlar os Id e os Textassociados aos widgets clicando-se com o botao da direita sobre o componente e seleccionando-se,respectivamente, Edit ID... e Edit Text....

21 〈version/androidDevel/descontos/res/layout/main.xml 21〉≡<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="horizontal" >

<TableLayout

android:id="@+id/tableLayout1"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:layout_weight="0.02" >

<TableRow

android:id="@+id/tableRow1"

android:layout_width="wrap_content"

android:layout_height="wrap_content" >

<TextView

android:id="@+id/textView1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/base" />

<EditText

android:id="@+id/editValor"

android:layout_width="84dp"

android:layout_height="wrap_content"

android:inputType="number" />

</TableRow>

<TableRow

android:id="@+id/tableRow2"

android:layout_width="wrap_content"

android:layout_height="wrap_content" >

<TextView

android:id="@+id/textView2"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/desconto" />

<EditText

android:id="@+id/oDesconto"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:inputType="numberDecimal" />

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 22: Android Devel

22 Computacao Movel

</TableRow>

<Button

android:id="@+id/button1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/calcular" />

<TextView

android:id="@+id/oResultado"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/vazio"

android:textAppearance="?android:attr/textAppearanceMedium" />

</TableLayout>

</LinearLayout>

This code is written to file version/androidDevel/descontos/res/layout/main.xml.

1.5.4 O ciclo de vida das aplicacoes Android

FIXME onCreate(), onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy().

1.5.5 Tarefas Assıncronas em Android

Quando se desenvolve software em Android e importante notar o seguinte [Mednieks et al. 2011,pags. 142–156]:

• A framework do interface grafico do Android, da mesma forma do que acontece noutrasframeworks de interfaces graficos equivalentes, e event-driven, baseia-se no uso de uma bi-blioteca com uma hierarquia de componentes graficos, e e single-threaded . Se fosse multi-threaded seria quase impossıvel evitar a possibilidade de aparecimento de um deadlock aoreagir a eventos de input (teclado ou ecra tactil), output (display).

• Os pedidos de input/output sao processados sequencialmente pela thread associada ao GUI,por ordem de chegada

• Tarefas realizadas pelo programa do utilizador que demorem algum tempo a executar (maisdo que alguns milisegundos) devem ser executadas numa thread separada

• E necessario sincronizar o funcionamento dessa thread com o funcionamento da thread doGUI

• A classe android.os.AsyncTask<T,S,U> simplifica o processo de execucao numa threadseparada e a respetiva sincronizacao. Baseia-se no uso de tipos genericos e disponibilizaos metodos onPreExecute(), “U doInBackground(T... args)”, onProgressUpdate(S...progress), “onPostExecute(U result)”. Os argumentos T, S, U sao os seguintes: T e otipo do argumento a dar a worker thread que vai fazer o trabalho propriamente dito, S eo tipo das unidades em que se vai reportar o progresso, e U e o tipo de retorno da workerthread. Se nao for necessario utilizar “onProgressUpdate(S... progress)” entao no lugardo S deve-se colocar Void (querendo dizer uma referencia para uma classe que representa akeyword java void)

• A classe AsyncTask garante que todos os callbacks sao sincronizados de tal forma que estasoperacoes sao seguras sem terem a necessidade de serem explicitamente sincronizadas:

– Alterar o valor de atributos da classe no construtor ou em onPreExecute(), e ler osseus valores em doInBackground(args...).

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 23: Android Devel

Computacao Movel 23

– Alterar o valor de atributos da classe em doInBackground(args...), e referir-se a elesem onProgressUpdate(progress...) e onPostExecute(result).

• Cada instancia de uma especializacao da classe android.os.AsyncTask<T,S,U> so pode serexecutada uma vez, tentar executa-la mais do que uma vez origina a excepcao IllegalStateException,deve-se criar uma nova instancia da classe cada vez que seja necessario

23a 〈version/androidDevel/lixo.txt 23a〉≡〈exemplo: uso de AsyncTaskDemo mas sem indicador de progresso 23b〉

This code is written to file version/androidDevel/lixo.txt.

23b 〈exemplo: uso de AsyncTaskDemo mas sem indicador de progresso 23b〉≡ (23a)

package ...

import android.app.Activity;

import android.os.AsyncTask;

...

public class AsyncTaskDemo extends Activity {

private final class AsyncInitGame extends AsyncTask<String, Void, String> {

private final View dots;

private final Game game;

private final TextView message;

private final Drawable bg;

〈AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c〉〈AsyncTask: protected void onPreExecute() 23d〉〈AsyncTask: protected void onPostExecute(String msg) 24a〉〈AsyncTask: protected String doInBackground(String... args) 24b〉

}

int mInFlight;

@Override

〈AsyncTask: public void onCreate(Bundle state) 24c〉}

Defines:AsyncTaskDemo, used in chunk 25.

23c 〈AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c〉≡ (23b 25)

public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) {

this.dots = dots;

this.bg = bg;

this.game = game;

this.message = msg;

}

Defines:AsyncInitGame(), used in chunk 24c.

23d 〈AsyncTask: protected void onPreExecute() 23d〉≡ (23b 25)

@Override // runs on the GUI thread

protected void onPreExecute() {

if (mInFlight++ < 0) {

dots.setBackgroundResource(R.anim.dots);

((AnimationDrawable)dots.getBackground()).start();

}

}

Defines:onPreExecute(), never used.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 24: Android Devel

24 Computacao Movel

24a 〈AsyncTask: protected void onPostExecute(String msg) 24a〉≡ (23b 25)

@Override // runs on the GUI thread

protected void onPostExecute(String msg) {

if (--mInFlight < 0) {

((AnimationDrawable)dots.getBackground()).stop();

dots.setBackgroundDrawable(bg);

}

message.setText(msg);

}

Defines:onPostExecute(), never used.

24b 〈AsyncTask: protected String doInBackground(String... args) 24b〉≡ (23b)

@Override // runs on its own thread

protected String doInBackground(String... args) {

return ((1 != args.length) || (null == args[0]))

? null : game.initialize(args[0]);

}

Defines:doInBackground(), used in chunk 25.

24c 〈AsyncTask: public void onCreate(Bundle state) 24c〉≡ (23b)

public void onCreate(Bundle state) {

super.onCreate(state);

setContentView(R.layout.asyncdemo);

final View dots = findViewById(R.id.dots);

final Drawable bg = dots.getBackground();

final TextView msg = ((TextView)findViewById(R.id.msg));

final Game game = Game.newGame();

((Button) findViewById(R.id.start)).setOnClickListener(

new View.OnClickListener() {

@Override

public void onClick(View v) {

new AsyncInitGame(dots, bg, game, msg).execute("basic");

}

});

}

Uses AsyncInitGame() 23c, onCreate() 27, and setOnClickListener().

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 25: Android Devel

Computacao Movel 25

25 〈exemplo: uso de AsyncTaskDemo com indicador de progresso 25〉≡package ...

import android.app.Activity;

import android.os.AsyncTask;

...

/** AsyncTaskDemo */

public class AsyncTaskDemoWithProgressIndicator extends Activity {

private final class AsyncInitGame extends AsyncTask<String, Integer, String>

implements Game.InitProgressListener {

〈AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c〉〈AsyncTask: protected void onPreExecute() 23d〉〈AsyncTask: protected void onPostExecute(String msg) 24a〉// runs on its own thread

@Override

protected String doInBackground(String... args) {

return ((1 != args.length) || (null == args[0]))

? null : game.initialize(args[0], this);

}

@Override // runs on the UI thread

protected void onProgressUpdate(Integer... vals) {

updateProgressBar(vals[0].intValue());

}

@Override // runs on its own thread

public void onInitProgress(int pctComplete) {

publishProgress(Integer.valueOf(pctComplete));

}

}

int mInFlight;

int mComplete;

@Override

public void onCreate(Bundle state) {

super.onCreate(state);

setContentView(R.layout.asyncdemoprogress);

final View dots = findViewById(R.id.dots);

final Drawable bg = dots.getBackground();

final TextView msg = ((TextView)findViewById(R.id.msg));

final Game game = Game.newGame();

((Button) findViewById(R.id.start)).setOnClickListener(

new View.OnClickListener() {

@Override

public void onClick(View v) {

mComplete = 0;

new AsyncInit(dots, bg, game, msg).execute("basic");

}

});

}

void updateProgressBar(int progress) {

int p = progress;

if (mComplete < p) {

mComplete = p;

((ProgressBar)findViewById(R.id.progress)).setProgress(p);

}

}

}

Defines:onInitProgress(), never used.onProgressUpdate(), used in chunk 33b.updateProgressBar(), never used.

Uses AsyncTaskDemo 23b, doInBackground() 24b 33a, onCreate() 27, and setOnClickListener().

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 26: Android Devel

26 Computacao Movel

1.5.6 Thread que executa calculos em background com indicador deprogresso

Apresenta-se aqui a implementacao completa (isto e, MainActivity.java, AndroidManifest.xml,activity_main.xml, strings.xml) de uma aplicacao que possui 2 campos de texto onde se podemcolocar no

¯s inteiros, um campo de texto onde nao se pode escrever e que serve para apresentara media, e um botao. Cada vez que se prime o botao e lancada uma thread em background quevai gerar 200 000 no

¯s aleatorios pertencentes ao intervalo [min..max], seguidamente o campo coma media e actualizado de modo a representar a media de todos os no

¯s gerados ate ao momento.Note-se que entre cada vez que se prime o botao se pode alterar os valores do mınimo ou domaximo. A operacao de gerar os 200 000 no

¯s aleatorios e executada numa thread separada quecorre em background, de modo a dar feedback do andamento da operacao cada vez que 10 000 no

¯ssao gerados o texto do botao e atualizado de modo a apresentar a percentagem do trabalho queja foi realizado (isto e, a informacao de progresso avanca em multiplos de 5%).

Para alem disso este metodo implementa diversos metodos do ciclo de vida das aplicacoes An-droid, embora na maior parte dos casos a unica coisa que faz e mostrar um curto alerta recorrendoa um Toast.

26 〈version/androidDevel/MainActivity.java 26〉≡package pt.ismai.cm2012;

import java.util.Random;

import android.app.Activity;

import android.os.AsyncTask;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

import android.widget.Toast;

public class MainActivity extends Activity {

protected EditText campoInputMin, campoInputMax;

protected TextView campoOutputMed;

protected int inputMin, inputMax;

protected double outputMed;

protected Random r;

protected Button botaoGerar;

protected int contador;

protected double percentage;

AsyncGenerator backgroundTask;

〈CicloDeVida: public void onCreate(Bundle savedInstanceState) 27〉〈CicloDeVida: protected void restoreVarsFromBundle(Bundle savedInstanceState) 28b〉〈CicloDeVida: onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy() 29a〉〈CicloDeVida: public void onSaveInstanceState(Bundle outputState) 28a〉〈CicloDeVida: public void onRestoreInstanceState(Bundle savedInstanceState) 29b〉〈CicloDeVida: public void gerarNumeros(int n) 30a〉〈CicloDeVida: public void atualizarNumeros() 30b〉〈CicloDeVida: MyAsyncInit() 31a〉

}

This code is written to file version/androidDevel/MainActivity.java.Defines:

MainActivity, used in chunks 34, 45–47, 50b, 52a, and 59.Uses AsyncGenerator 31a.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 27: Android Devel

Computacao Movel 27

No metodo onCreate() para alem de se obter acesso aos elementos do interface grafico (botaoGerar,campoInputMin, campoInputMax, e campoOutputMed), inicializa-se o contador com zero (usadono processo de calcular as medias dos no

¯s aleatorios), e inicializa-se o gerador de no¯s aleatorios.

Para alem disso usando uma classe anonima adiciona-se ao botao um setOnClickListener() que,cada vez que o botao e premido, cria uma nova instancia da classe AsyncGenerator que e umasubclasse de AsyncTask, e executa essa instancia chamando o metodo execute(), isto despoletaa criacao da thread em backgound e a geracao de 200 000 no

¯s aleatorios.Note-se que o argumento fornecido a execute() e o argumento recebido por doInBackground().

27 〈CicloDeVida: public void onCreate(Bundle savedInstanceState) 27〉≡ (26)

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

campoInputMin = (EditText)findViewById(R.id.inputMinimo);

campoInputMax = (EditText)findViewById(R.id.inputMaximo);

campoOutputMed = (TextView)findViewById(R.id.outputMedia);

botaoGerar = (Button)findViewById(R.id.botaoGerar);

botaoGerar.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

backgroundTask = new AsyncGenerator(botaoGerar, campoInputMin,

campoInputMax, campoOutputMed);

backgroundTask.execute(200000);

}

});

contador = 0;

r = new Random(999); // so para debug e que se usa o 999

if (savedInstanceState != null) {

restoreVarsFromBundle(savedInstanceState);

Toast.makeText(this, "onCreate(with bundle)", Toast.LENGTH_SHORT).show();

} else {

Toast.makeText(this, "onCreate(null)", Toast.LENGTH_SHORT).show();

}

}

Defines:onCreate(), used in chunks 12b, 13, 15a, 16c, 19, 24c, 25, 39, 43–45, 47, 49, 50a, and 61.

Uses AsyncGenerator 31a, AsyncGenerator() 31b, restoreVarsFromBundle() 28b, and setOnClickListener().

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 28: Android Devel

28 Computacao Movel

No caso da aplicacao ser interrompida e ter de guardar o seu estado o metodo onSaveInstanceStatearmazena no Bundle os valores que se encontram nos campos campoInputMin, campoInputMax, ecampoOutputMed, e o valor do contador, usando tags apropriadas.

28a 〈CicloDeVida: public void onSaveInstanceState(Bundle outputState) 28a〉≡ (26)

@Override

public void onSaveInstanceState(Bundle outputState) {

String s = campoInputMin.getText().toString();

if (!s.equals("")) {

inputMin = Integer.parseInt(s);

outputState.putInt("mınimo", inputMin);

}

s = campoInputMax.getText().toString();

if (!s.equals("")) {

inputMax = Integer.parseInt(s);

outputState.putInt("maximo", inputMax);

}

s = campoOutputMed.getText().toString();

if (!s.equals("")) {

outputMed = Double.parseDouble(s);

outputState.putDouble("media", outputMed);

}

outputState.putInt("contador", contador);

Toast.makeText(this, "onSaveInstanceState()", Toast.LENGTH_SHORT).show();

super.onSaveInstanceState(outputState);

}

Defines:onSaveInstanceState(), never used.

O metodo restoreVarsFromBundle() faz a operacao inversa de onSaveInstanceState().Optou-se por recorrer a um metodo separado de modo a permitir que ele seja chamado a partirde dois locais, quer a partir do onCreate() quer a partir do onRestoreInstanceState().

28b 〈CicloDeVida: protected void restoreVarsFromBundle(Bundle savedInstanceState) 28b〉≡ (26)

protected void restoreVarsFromBundle(Bundle savedInstanceState) {

inputMin = savedInstanceState.getInt("mınimo");

if (inputMin != 0)

campoInputMin.setText("" + inputMin);

inputMax = savedInstanceState.getInt("maximo");

if (inputMax != 0)

campoInputMax.setText("" + inputMax);

outputMed = savedInstanceState.getInt("media");

if (outputMed != 0)

campoOutputMed.setText("" + outputMed);

contador = savedInstanceState.getInt("contador");

}

Defines:restoreVarsFromBundle(), used in chunks 27 and 29b.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 29: Android Devel

Computacao Movel 29

29a 〈CicloDeVida: onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy() 29a〉≡ (26)

@Override

public void onStart() {

super.onStart();

Toast.makeText(this, "onStart()", Toast.LENGTH_SHORT).show();

}

@Override

public void onResume() {

super.onResume();

Toast.makeText(this, "onResume()", Toast.LENGTH_SHORT).show();

}

@Override

public void onPause() {

Toast.makeText(this, "onPause()", Toast.LENGTH_SHORT).show();

super.onPause();

}

@Override

public void onStop() {

Toast.makeText(this, "onStop()", Toast.LENGTH_SHORT).show();

super.onStop();

}

@Override

public void onRestart() {

super.onRestart();

Toast.makeText(this, "onRestart()", Toast.LENGTH_SHORT).show();

}

@Override

public void onDestroy() {

Toast.makeText(this, "onDestroy()", Toast.LENGTH_SHORT).show();

super.onDestroy();

}

29b 〈CicloDeVida: public void onRestoreInstanceState(Bundle savedInstanceState) 29b〉≡ (26)

@Override

public void onRestoreInstanceState(Bundle savedInstanceState) {

super.onRestoreInstanceState(savedInstanceState);

if (savedInstanceState != null)

restoreVarsFromBundle(savedInstanceState);

Toast.makeText(this, "onRestoreInstanceState()", Toast.LENGTH_SHORT).show();

}

Defines:onRestoreInstanceState(), never used.

Uses restoreVarsFromBundle() 28b.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 30: Android Devel

30 Computacao Movel

Cada chamada a funcao gerarNumeros(n, ...) origina n chamadas a atualizarNumeros(),sendo que o valor retornado por atualizarNumeros() e a media dos valores obtidos ate ao mo-mento, pelo que o que interessa que a funcao gerarNumeros() retorne e o ultimo valor obtidopara a media.

30a 〈CicloDeVida: public void gerarNumeros(int n) 30a〉≡ (26)

public double gerarNumeros(int n, int min, int max, double media) {

double d = media;

for (int k = 0; k < n; ++k)

d = atualizarNumeros(min, max, d);

return d;

}

Defines:gerarNumeros(), used in chunk 33a.

Uses atualizarNumeros() 30b.

Cada chamada a atualizarNumeros() origina que um novo no¯ no intervalo de min (inclusive)

a max (inclusive) seja gerado, o que faz com que o contador (de no¯s aleatorios gerados ate ao

momento) seja incrementado e que a media seja atualizada.O novo valor, oNovo, ja que e um no

¯ aleatorio entre min e max, tem um valor que e calculadoda seguinte forma: min + n × (max − min + 1), em que n e um no

¯ aleatorio entre 0 (inclusive) e1 (exclusive). Aceitou-se que o max pudesse ser menor do que o min, nesse caso troca-se o valordas 2 variaveis.

30b 〈CicloDeVida: public void atualizarNumeros() 30b〉≡ (26)

public double atualizarNumeros(int min, int max, double media) {

int largura = max - min;

int base = min;

if (largura < 0) {

largura = -largura;

base = max;

}

++largura;

int oNovo = (int)(r.nextDouble() * largura) + base;

media = (media * contador + oNovo) / (contador + 1);

++contador;

return media;

}

Defines:atualizarNumeros(), used in chunk 30a.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 31: Android Devel

Computacao Movel 31

A classe android.os.AsyncTask<T,S,U>, simplifica o processo de execucao numa thread se-parada e a respetiva sincronizacao. Baseia-se no uso de tipos genericos e disponibiliza os metodos“onPreExecute()”, “U doInBackground(T... args)”, “onProgressUpdate(S... progress)”,“onPostExecute(U result)”. Os argumentos T, S, U sao os seguintes: T e o tipo do argumento adar a worker thread (parametros) que vai fazer o trabalho propriamente dito (esse argumento e paraser fornecido ao metodo execute() da instancia da classe, que depois de uma forma automaticao faz chegar ao doInBackground()), S e o tipo das unidades em que se vai reportar o progresso,e U e o tipo de retorno da worker thread que por sua vez e passado ao metodo onPostExecute().Por outras palavras trata-se de android.os.AsyncTask<args,progress,result>.

31a 〈CicloDeVida: MyAsyncInit() 31a〉≡ (26)

private final class AsyncGenerator extends AsyncTask<Integer, Double, Double> {

protected Button b;

protected String savedLabel;

protected TextView tMin, tMax, tMedia;

protected int min, max;

protected double media;

protected boolean ignorar;

〈AsyncGenerator: public AsyncGenerator(View botao) 31b〉〈AsyncGenerator: protected void onPreExecute() 32〉〈AsyncGenerator: protected String doInBackground(Integer... args) 33a〉〈AsyncGenerator: protected void onProgressUpdate(Double... percentComplete) 33b〉〈AsyncGenerator: protected void onPostExecute(String msg) 34a〉

}

Defines:AsyncGenerator, used in chunks 26, 27, and 31b.

Os argumentos fornecidos ao construtor sao as referencias para os 4 elementos do interfacegrafico. Desta forma a classe AsyncGenerator consegue estar isolada do ambiente que a rodeia,nao tendo de aceder a nenhum dos atributos da classe envolvente (nao era obrigatoorio fazer destamaneira mas assim fica melhor).

31b 〈AsyncGenerator: public AsyncGenerator(View botao) 31b〉≡ (31a)

public AsyncGenerator(Button botao, TextView min, TextView max, TextView media) {

b = botao;

tMedia = media;

tMin = min;

tMax = max;

}

Defines:AsyncGenerator(), used in chunk 27.

Uses AsyncGenerator 31a.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 32: Android Devel

32 Computacao Movel

A funcao onPreExecute() e chamada antes de se comecar o trabalho em background. Trata-sedo ultimo momento em que ainda podemos manipular elementos do interface grafico, por isso o queesta funcao faz e colocar em savedLabel o texto original que estava no botao (de modo a permitirmais tarde repor esse texto), coloca 0% como texto do botao para indicar que o trabalho aindaesta no inıcio, desativa o botao de modo a que ninguem o possa premir, seguidamente le os valoresdos 3 campos de texto, tMin tMax tMedia, que tem referencias para os campos campoInputMincampoInputMax campoOutputMed, e coloca-os em variaveis chamadas, respectivamente, min, max,media.

A variavel ignorar tera o valor de true se nao for possıvel dar seguimento ao trabalho aexecutar em background por um dos valores, o maximo ou o mınimo ou ambos, nao tiver sidofornecido. Nesse caso uma ou mais das variaveis max e min podem nao ficar inicializadas.

32 〈AsyncGenerator: protected void onPreExecute() 32〉≡ (31a)

@Override // runs on the GUI thread

protected void onPreExecute() {

ignorar = false;

savedLabel = b.getText().toString();

b.setText("0%");

b.setEnabled(false);

String s = tMedia.getText().toString();

if (s.equals(""))

media = 0.0;

else

media = Double.parseDouble(s);

s = tMin.getText().toString();

if (s.equals("")) {

ignorar = true;

return;

}

min = Integer.parseInt(s);

s = tMax.getText().toString();

if (s.equals("")) {

ignorar = true;

return;

}

max = Integer.parseInt(s);

}

Defines:onPreExecute(), never used.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 33: Android Devel

Computacao Movel 33

Note-se que a funcao doInBackground(), que corre numa thread que esta proibida de manipularelementos do interface grafico (e portanto proibida de chamar funcoes que manipulem o interfacegrafico), chama a funcao ‘publishProgress(args)’ que por sua vez, como que por milagre, fazautomaticamente o espelho dessa chamada para a funcao ‘onProgressUpdate(args)’, sendo que osargs sao os mesmos, e esta funcao ja corre na thread do interface grafico pelo que pode manipularelementos do interface grafico.

Cada vez que se prime o botao esta thread vai gerar 200 000 no¯s aleatorios no intervalo

[min..max] e calcular a media desses 200 000 no¯s. Note-se que antes de se premir o botao pode-se

alterar o valor do maximo e/ou do mınimo e sao esses os valores que sao utilizados nos calculos.Ja se se alterar um dos valores dos campos enquanto a geracao de no

¯s aleatorios esta a ocorrer onovo valor nao e usado, so sendo usado na proxima vez que se premir o botao (que nao pode serpremido enquanto os calculos estao a ser feitos ja que propositadamente por esta razao fizemos odisable do botao no onPreExecute()). O progresso e apresentado em incrementos de 5%, isto e,5%, 10%, 15%, ..., 90%, 95%, 100%.

33a 〈AsyncGenerator: protected String doInBackground(Integer... args) 33a〉≡ (31a)

@Override // runs on its own thread

protected Double doInBackground(Integer... args) {

if (!ignorar) {

int n = args[0] / 20;

if (n == 0)

n = 1;

for (int k = 0; k < 20; ++k) {

media = gerarNumeros(n, min, max, media);

publishProgress(k / 20.0);

}

}

return media;

}

Defines:doInBackground(), used in chunk 25.

Uses gerarNumeros() 30a.

A medida que vai havendo progresso o texto do botao vai mostrando um no¯ que representa a

percentagem do trabalho que ja se encontra terminado.Note-se que so vale a pena atualizar o texto que aparece no botao enquanto o trabalho ainda

nao estiver todo completo, quando o trabalho termina a etiqueta de imediato volta a ter o textooriginal (que tinha sido guardado na variavel savedLabel e que vai ser reposto pela funcaoonPostExecute()), portanto a partida ninguem tem tempo de ver o texto a dizer 100% antesdele desaparecer.

33b 〈AsyncGenerator: protected void onProgressUpdate(Double... percentComplete) 33b〉≡ (31a)

@Override // runs on the GUI thread

protected void onProgressUpdate(Double... percentComplete) {

String theText;

percentage = percentComplete[0];

theText = "" + (int)(percentage * 100) + "%";

b.setText(theText);

}

Defines:onInitProgress(), never used.

Uses onProgressUpdate() 25.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 34: Android Devel

34 Computacao Movel

O trabalho que estava a ser feito em background foi dado por terminado pelo que so falta reporo texto original da etiqueta do botao e colocar o valor da media no campo tMedia (que e o mesmoque o campo campoOutputMed da classe envolvente).

34a 〈AsyncGenerator: protected void onPostExecute(String msg) 34a〉≡ (31a)

@Override // runs on the GUI thread

protected void onPostExecute(Double d) {

b.setText(savedLabel);

b.setEnabled(true);

tMedia.setText("" + d);

}

Defines:onPostExecute(), never used.

34b 〈version/androidDevel/CicloDeVida-AndroidManifest.xml 34b〉≡<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="pt.ismai.cm2012"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk

android:minSdkVersion="8"

android:targetSdkVersion="15" />

<application

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".MainActivity"

android:label="@string/title_activity_main" >

<intent-filter>

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

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

This code is written to file version/androidDevel/CicloDeVida-AndroidManifest.xml.Uses MainActivity 26.

34c 〈version/androidDevel/CicloDeVida-strings.xml 34c〉≡<resources>

<string name="app_name">CicloDeVida</string>

<string name="menu_settings">Settings</string>

<string name="title_activity_main">MainActivity</string>

<string name="etiquetaMedia">Media</string>

<string name="etiquetaMin">Mınimo</string>

<string name="etiquetaMax">Maximo</string>

<string name="botaoGerar">Gerar</string>

</resources>

This code is written to file version/androidDevel/CicloDeVida-strings.xml.Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 35: Android Devel

Computacao Movel 35

Usou-se um AbsoluteLayout de modo a facilitar a criacao do interface grafico, no entanto umlayout absoluto tem o defeito de que esta amarrado ao tamanho do ecra. De qualquer forma comoo objectivo desta aplicacao era ver como trabalhar com threads que executavam em background,o layout utilizado pelo interface grafico e pouco relevante.

35 〈version/androidDevel/CicloDeVida-activity main.xml 35〉≡<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/AbsoluteLayout1"

android:layout_width="match_parent" android:layout_height="match_parent" >

<TextView

android:id="@+id/textView2"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="7dp" android:layout_y="12dp"

android:text="@string/etiquetaMin"

android:textAppearance="?android:attr/textAppearanceMedium" />

<EditText

android:id="@+id/inputMinimo"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="85dp" android:layout_y="13dp"

android:ems="10"

android:inputType="number" />

<TextView

android:id="@+id/textView1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="12dp" android:layout_y="88dp"

android:text="@string/etiquetaMax" />

<EditText

android:id="@+id/inputMaximo"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="77dp" android:layout_y="83dp"

android:ems="10"

android:inputType="number" />

<TextView

android:id="@+id/outputMedia"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="92dp" android:layout_y="148dp"

android:text="0.00" />

<TextView

android:id="@+id/textView3"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="12dp" android:layout_y="154dp"

android:text="@string/etiquetaMedia" />

<Button

android:id="@+id/botaoGerar"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="106dp" android:layout_y="222dp"

android:text="@string/botaoGerar" />

</AbsoluteLayout>

This code is written to file version/androidDevel/CicloDeVida-activity main.xml.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 36: Android Devel

36 Computacao Movel

1.6 Bases de Dados

O programa adb (Android Debug Bridge) faz parte do SDK do Android. Em Windows esseprograma encontra-se em “c:\Program Files (x86)\Android\android-sdk\platform-tools”.Pode ser usado para executar remotamente uma shell na variante do sistema operativo Linuxutilizada pelo Android. As bases de dados Sqlite3 sao mantidas em subdirectorios de /data/data.

Somente a partir de SQlite 3.6.19 e que o sqlite3 passou a suportar chaves estrangeiras (istoe a fazer verificacoes de integridade de chaves estrangeiras), no entanto por omissao essa funci-onalidade esta desactivada podendo ser activada usando um pragma (isto e, uma instrucao SQLque representa uma extensao ao SQL standard suportado por SQLite3). Portanto so a partir doAndroid 2.2 froyo, que actualiza o Sqlite 3.5.9 para o SQLite 3.6.22, e que a versao do SQLite3 quevem com a plataforma suporta chaves estrangeiras. Para activar essa funcionalidade usar “pragmaFOREIGN_KEYS = 1”.

Para funcionar com o uso de um cursor para obter os dados de uma query do tipo select, achave primaria das tabelas tem de obrigatoriamente ter o nome “_id” [Mednieks et al. 2011, pag.257].

36 〈sqlite3: criar a base de dados manualmente 36〉≡ (37a)

adb shell

cd /data/data

mkdir pt.ismai.cm2011.app1

cd pt.ismai.cm2011.app1

mkdir databases

cd databases

sqlite3 theScores.db

pragma FOREIGN_KEYS; /* valor actual da configurac~ao de chaves estrangeiras */

pragma FOREIGN_KEYS = 1; /* necessita de Android 2.2 ou superior */

pragma FOREIGN_KEYS;

CREATE TABLE scores(_id INTEGER PRIMARY KEY AUTOINCREMENT,

nPretas INTEGER,

nBrancas INTEGER);

CREATE TABLE players(_id INTEGER PRIMARY KEY AUTOINCREMENT,

name TEXT,

fk_score_id INTEGER NOT NULL CONSTRAINT score_id REFERENCES scores(_id) ON

DELETE CASCADE

);

/* CREATE TRIGGER fk_insert_player BEFORE INSERT ON players

FOR EACH ROW

BEGIN

SELECT RAISE(ROLLBACK,

’insert na tabela "players" violou chave estrangeira "fk_score_id"’)

WHERE (select _id from scores where _id = NEW.fk_score_id) IS NULL;

END;

*/

/* falta trigger para update e trigger para delete */

insert into scores(_id, nPretas, nBrancas) values(1, 2, 0);

insert into scores(_id, nPretas, nBrancas) values(2, 3, 1);

insert into scores(_id, nPretas, nBrancas) values(3, 2, 2);

insert into players(_id, name, fk_score_id) values(1, "avs", 1);

insert into players(_id, name, fk_score_id) values(2, "xyz", 2);

insert into players(_id, name, fk_score_id) values(3, "avs", 3);

insert into players(_id, name, fk_score_id) values(4, "avs", 7); /* erro! */

select * from scores;

select name, nPretas, nBrancas from scores, players where players.fk_score_id =

scores._id and nBrancas > 0;

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 37: Android Devel

Computacao Movel 37

37a 〈version/androidDevel/criaBaseDadosManualmente.txt 37a〉≡〈sqlite3: criar a base de dados manualmente 36〉

This code is written to file version/androidDevel/criaBaseDadosManualmente.txt.

37b 〈Sqlite3: strings com os comandos SQL para criar as tabelas 37b〉≡ (37c 39)

String pragmaForeignKeys = "pragma FOREIGN_KEYS = 1";

String recreateTableScores = "DROP TABLE if exists scores";

String createScoresTable = "CREATE TABLE scores(" +

"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +

"nPretas INTEGER, nBrancas INTEGER)";

String recreateTablePlayers = "DROP TABLE if exists players";

String createPlayersTable = "CREATE TABLE players(" +

"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +

"name TEXT, " +

"fk_score_id INTEGER NOT NULL CONSTRAINT score_id REFERENCES scores(_id) ON"+

" DELETE CASCADE)";

String trigger1 = "CREATE TRIGGER fk_insert_player BEFORE INSERT ON players " +

"FOR EACH ROW " +

"BEGIN " +

"SELECT RAISE(ROLLBACK, " +

"’insert na tabela \"players\" violou chave estrangeira \"fk_score_id\"’) " +

"WHERE (select _id from scores where _id = NEW.fk_score_id) IS NULL; " +

"END";

37c 〈Sqlite3: strings com os comandos SQL 37c〉≡ (38a)

〈Sqlite3: strings com os comandos SQL para criar as tabelas 37b〉String insert1 = "insert into scores(_id, nPretas, nBrancas) values(1, 2, 0)";

String insert2 = "insert into scores(_id, nPretas, nBrancas) values(2, 3, 1)";

String insert3 = "insert into scores(_id, nPretas, nBrancas) values(3, 2, 2)";

String insert4 = "insert into players(_id, name, fk_score_id) values(1, \"avs\", 1)";

String insert5 = "insert into players(_id, name, fk_score_id) values(2, \"xyz\", 2)";

String insert6 = "insert into players(_id, name, fk_score_id) values(3, \"avs\", 3)";

String insert7 = "insert into players(_id, name, fk_score_id) values(4, \"avs\", 7)"; // erro!

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 38: Android Devel

38 Computacao Movel

Pode-se obter o mesmo resultado a partir de um programa no Android.38a 〈sqlite3: criar a base de dados programaticamente 38a〉≡ (38b)

import android.database.sqlite.SQLiteDatabase;

// ...

protected SQLiteDatabase db;

// ...

db = openOrCreateDatabase("theScores.db", SQLiteDatabase.CREATE_IF_NECESSARY, null);

db.setLocale(Locale.getDefault());

db.setLockingEnabled(true);

db.setVersion(1);

〈Sqlite3: strings com os comandos SQL 37c〉db.execSQL(pragmaForeignKeys);

db.execSQL(recreateTableScores);

db.execSQL(createScoresTable);

db.execSQL(recreateTablePlayers);

db.execSQL(createPlayersTable);

db.execSQL(insert1);

db.execSQL(insert2);

db.execSQL(insert3);

db.execSQL(insert4);

db.execSQL(insert5);

db.execSQL(insert6);

db.execSQL(insert7); // tem de dar erro

// db.query("players", null, null, null, null, null, null);

// db.query(tableName, projection, where, whereArgs, null, null, sortOrder);

38b 〈version/androidDevel/criaBaseDadosProgramaticamente.txt 38b〉≡〈sqlite3: criar a base de dados programaticamente 38a〉

This code is written to file version/androidDevel/criaBaseDadosProgramaticamente.txt.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 39: Android Devel

Computacao Movel 39

39 〈version/androidDevel/ScoresDatabase.java 39〉≡private static int DATABASE_VERSION = 1;

public class ScoresDatabase extends SQLiteOpenHelper {

〈Sqlite3: strings com os comandos SQL para criar as tabelas 37b〉private ScoresDatabase(Context context, String nome, SQLiteDatabase.CursorFactory factory) {

super(context, nome, factory, DATABASE_VERSION);

}

@Override

public void onCreate(SQLiteDatabase sqLiteDatabase) {

createTable(sqLiteDatabase);

}

private void createTable(SQLiteDatabase db) {

db.execSQL(pragmaForeignKeys);

db.execSQL(createScoresTable);

db.execSQL(createPlayersTable);

}

@override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

db.execSQL(pragmaForeignKeys);

db.execSQL(recreateTableScores);

db.execSQL(createScoresTable);

db.execSQL(recreateTablePlayers);

db.execSQL(createPlayersTable);

}

public ScoresCursor obterScores(ScoresCursor.SortBy ordenarPor) {

String sql = ScoresCursor.QUERY + ordenarPor.toString();

SQLiteDatabase d = getReadableDatabase();

ScoresCursor c = (ScoresCursor)d.rawQueryWithFactory(

new ScoresCursor.Factory(), sql, null, null);

c.moveToFirst();

return c;

}

public static class ScoresCursor extends SQLiteCursor {

public static enum SortBy {

nPretas,

nBrancas

}

private static final String Query =

"select scores._id, nPretas, nBrancas" +

"from scores"

"order by ";

private ScoresCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String

editTable, SQLiteQuery query) {

super(db, driver, editTable, query);

}

private static class Factory implements SQLiteDatabase.CursorFactory {

@Override

public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver driver,

String editTable, SQLiteQuery query) {

return new ScoresCursor(db, driver, editTable, query);

}

}

public long getColScoresId() {

return getLong(getColumnIndexOrThrow("scores._id"));

}

}

This code is written to file version/androidDevel/ScoresDatabase.java.Uses onCreate() 27.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 40: Android Devel

40 Computacao Movel

O objecto cursor associado ao resultado de uma query tem de ser manipulado com cautelade modo a evitar que cursores mal manipulados comecem a consumir um excesso de recursosque acabam por dar cabo da aplicacao ou afectar o sistema operativo. Quando a aplicacao en-tra em pausa ou termina, o cursor tem de ser desactivado com uma chamada a deactivate(),quando a aplicacao volta a estar activa o cursor deve ser actualizado usando requery(), quandoja nao e necessario tem de se chamar close() para libertar os recursos. Isto e, devem serefectuadas estas chamadas no ambito do cilco de vida da aplicacao: onPause(), onResume(),onDestroy(). Outra hipotese bastante mais simples e usar startManagingCursor(c) para queseja a framework do Android a tratar destes detalhes (em casos especiais mais tarde pode-se sem-pre chamar stopManagingCursor(c) para obter de volta a gestao do cursor) [Darcey e Conder2011, pag. 245].

40 〈SQLite3: query usando QueryBuilder 40〉≡ (42c)

// query simples

// Cursor c;

// c = db.query(tableName, projection, where, whereArgs, null, null, sortOrder);

// query usando QueryBuilder

import android.database.sqlite.SQLiteQueryBuilder;

// ...

SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

queryBuilder.setTables("contactos, locais");

queryBuilder.appendWhere("contactos.local_id = locais._id");

String devolverColunas[] = {

"nome", "telefone", "localidade"

};

String ordenarPor = "nome ASC";

Cursor c = queryBuilder.query(db, devolverColunas, null, null, null, null, ordenarPor);

c.close(); // o cursor tem de ser fechado quando n~ao for mais necessario

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 41: Android Devel

Computacao Movel 41

Esta classe mostra uma maneira simples de lidar com um cursor, obviamente que o cursorpossui bastantes outros metodos.

41a 〈SQLite3: classe MeuCursor 41a〉≡ (42c)

// classe auxiliar para trabalhar com o cursor

public class MeuCursor {

protected Cursor c;

int nResultados;

int nColunas;

public MeuCursor(Cursor oCursor) {

c = oCursor;

nResultados = c.getCount();

nColunas = c.getColumnCount();

c.moveToFirst();

}

public String[] obterNomesCampos() {

String[] nomesCampos = new String[nColunas];

for (int k = 0; k < nColunas; ++k)

nomesCampos[k] = c.getColumnName(k);

return nomesCampos;

}

public String[] obterProximoRegisto() {

if (c.isAfterLast())

return null;

String[] campos = new String[nColunas];

for (int k = 0; k < nColunas; ++k)

campos[k] = c.getString(k); //funciona mesmo que campo n~ao seja string

c.moveToNext();

return campos;

}

}

Defines:MeuCursor, never used.

41b 〈SQLite3: strings base de dados nome, telefone, localidade 41b〉≡ (42c)

// strings com esquema da base de dados

String pragmaForeignKeys = "pragma FOREIGN_KEYS = 1";

String recreateTableLocais = "DROP TABLE if exists locais";

String createLocaisTable = "CREATE TABLE locais(" +

"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +

"localidade TEXT)";

String recreateTableContactos = "DROP TABLE if exists contactos";

String createContactosTable = "CREATE TABLE contactos(" +

"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +

"nome TEXT, telefone TEXT, " +

"fk_local_id INTEGER NOT NULL CONSTRAINT locais_id REFERENCES locais(_id) ON"+

" DELETE CASCADE)";

String trigger1 = "CREATE TRIGGER fk_insert_contacto BEFORE INSERT ON contactos " +

"FOR EACH ROW " +

"BEGIN " +

"SELECT RAISE(ROLLBACK, " +

"’insert na tabela \"contactos\" violou chave estrangeira \"fk_local_id\"’) " +

"WHERE (select _id from locais where _id = NEW.fk_locais_id) IS NULL; " +

"END";

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 42: Android Devel

42 Computacao Movel

42a 〈SQLite3: criar base de dados nome, telefone, localidade 42a〉≡ (42c)

// abrir base de dados

import android.database.sqlite.SQLiteDatabase;

// ...

protected SQLiteDatabase db;

// ...

db = openOrCreateDatabase("theScores.db", SQLiteDatabase.CREATE_IF_NECESSARY, null);

db.setLocale(Locale.getDefault());

db.setLockingEnabled(true);

db.setVersion(1);

// criar base de dados

db.execSQL(pragmaForeignKeys);

db.execSQL(recreateTableLocais);

db.execSQL(createLocaisTable);

db.execSQL(recreateTableContactos);

db.execSQL(createContactosTable);

42b 〈SQLite3: inserir nome, telefone, localidade 42b〉≡ (42c)

// inserir dados

++contador;

db.execSQL("insert into locais(_id, localidade) values(" + contador + ", " +

aLocalidade + ")");

db.execSQL("insert into contactos(nome, telefone, fk_local_id) values(" +

oNome + ", " + oTelefone + ", " + contador + ")");

42c 〈version/androidDevel/exemploQueryComCursor.txt 42c〉≡〈SQLite3: strings base de dados nome, telefone, localidade 41b〉〈SQLite3: criar base de dados nome, telefone, localidade 42a〉〈SQLite3: inserir nome, telefone, localidade 42b〉〈SQLite3: query usando QueryBuilder 40〉〈SQLite3: classe MeuCursor 41a〉

This code is written to file version/androidDevel/exemploQueryComCursor.txt.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 43: Android Devel

Computacao Movel 43

1. Usar ListView widget (Composite / ListView)

1.7 Ler e Escrever Ficheiros

Estes exemplos mostram como escrever dados para a memoria interna do telemovel (internalstorage). Note-se que ficheiros na memoria interna so podem ser acedidos pela aplicacao, nao existeacesso directo pelo utilizador ou acesso por outras aplicacoes, e os ficheiros sao automaticamenteapagados quando a aplicacao e desinstalada.

1. No que se segue assume-se que o package da aplicacao e pt.ismai.cm2013 e que o nome daaplicacao e Test1

2. Para criar um ficheiro chamado osMeusDados.txt e colocar la a mensagem “Hello World.”usar

43 〈escrever para o ficheiro osMeusDados.txt 43〉≡import java.io.FileOutputStream;

import java.io.IOException;

public class ExemploActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

String filename = "osMeusDados.txt";

String msg = "Hello World.";

try {

FileOutputStream f = openFileOutput(filename, Context.MODE_PRIVATE);

f.write(msg.getBytes());

f.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

Uses onCreate() 27.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 44: Android Devel

44 Computacao Movel

3. Pode-se tambem usar “openFileOutput(filename, Context.MODE_PRIVATE | Context.MODE_APPEND)”para evitar que ao abrir o ficheiro para output o seu conteudo (se ja existir) seja apagado

4. Compilar e executar o programa

5. Seleccionar Window / Open Perspective / DDMS, de modo a obter o Dalvik Debug MonitorServer, e no topo a direita seleccionar o tab “File Explorer”. Seguidamente ver o conteudode /data/data/pt.ismai.cm2013/files, deve ter o ficheiro osMeusDados.txt

6. Para copiar o ficheiro para o PC usar “adb pull /data/data/pt.ismai.cm2013/files/osMeusDados.txtosMeusDados-remotos.txt”

7. Para ler o conteudo do ficheiro usar algo de semelhante a:44 〈ler do ficheiro osMeusDados.txt 44〉≡

public class InternalStorageExample2 extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

String fileName = "osMeusDados.txt";

try {

FileInputStream f = openFileInput(fileName);

InputStreamReader isr = new InputStreamReader(f);

StringBuilder sb = new StringBuilder();

char[] inputBuffer = new char[2048];

int k;

while ((k = isr.read(inputBuffer)) != -1) {

sb.append(inputBuffer, 0, k);

}

String readString = sb.toString();

Log.i("LOG_TAG", "String lido: " + readString);

// deleteFile(fileName);

} catch (IOException e) {

e.printStackTrace();

}

}

}

Uses onCreate() 27.

1.8 GUI widgets

1.8.1 ListView, Spinner, GridView

1. O widget ListView29 representa uma list box, enquanto que o Spinner30 representa umadrop-down list. O GridView31 permite fazer uma seleccao numa matriz bidimensional [Murphy2012, pag. 167]

2. Todas estas classes (e a ExpandableListView apresentada na Seccao 1.8.2) herdam de umaclasse comum, o AdapterView

3. Um adapter fornece um interface comum para um conjunto diverso de APIs ou de origensde dados (adapta os dados ao componente grafico em questao), recorrendo a implementacaode interfaces (class x implements y)

29Que se encontra na palete Composite do editor grafico do Eclipse30Que se encontra na palete Forms do editor grafico do Eclipse31Que se encontra na palete Forms do editor grafico do Eclipse

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 45: Android Devel

Computacao Movel 45

4. O exemplo mais simples e o ArrayAdapter que pode ser colocado a volta de um array ou deum java.util.List.

Dado que um ListView representa um conjunto de linhas que aparecem todas ao mesmotempo no ecra faz sentido que seja usado no contexto de uma Activity que so contenha essaListView. Para esse efeito existe o ListActivity, que e uma activity que por omissao incluium ListView como sendo o unico elemento que aparece no layout.

O simple_list_item_1 serve para controlar o aspecto das linhas, neste caso representa usaro formato standard do Android, com um tipo de letra grande, muito espaco em branco, textoem branco.

45 〈criar um ListView 45〉≡package pt.ismai.cm2012;

import android.os.Bundle;

import android.widget.ListView;

import android.widget.ArrayAdapter;

import android.view.View;

import android.app.ListActivity;

import android.widget.Toast;

public class MainActivity extends ListActivity {

private String[] osItensDaLista = {"um", "dois", "tres", "quatro",

"cinco", "seis", "sete", "oito", "nove", "dez"};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1, osItensDaLista);

setListAdapter(adapter);

}

@Override

public void onListItemClick(ListView parent, View v, int position,

long id) {

Toast.makeText(this, osItensDaLista[position], Toast.LENGTH_SHORT).show();

}

}

Uses MainActivity 26 and onCreate() 27.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 46: Android Devel

46 Computacao Movel

Quando se usa o ListActivity e obrigatorio colocar como id no XML android:id="@android:id/list"(por exemplo android:id="@+id/listView1" nao pode ser usado), se isso nao for feito oprograma ao executar origina uma excepcao dizendo java.lang.RuntimeException: Unable tostart activity ...: java.lang.RuntimeException: Your content must have a ListView whose idattribute is ’android.R.id.list’.

46 〈layout XML do ListView 46〉≡<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity" >

<ListView

android:id="@android:id/list"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginBottom="100dp"

android:layout_marginLeft="51dp" >

</ListView>

</RelativeLayout>

Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 47: Android Devel

Computacao Movel 47

No caso do Spinner ja faz sentido que ele seja somente um entre varios componentes graficos,por isso recorremos a um Activity e adicionamos esse widget.

47 〈criar um spinner 47〉≡package pt.ismai.cm2012;

import android.os.Bundle;

import android.app.Activity;

import android.widget.ArrayAdapter;

import android.widget.Spinner;

import android.widget.TextView;

import android.widget.AdapterView;

import android.widget.AdapterView.OnItemSelectedListener;

import android.view.View;

public class MainActivity extends Activity {

private Spinner oSpin; // assume-se que o ID do componente e spinner1

private TextView seleccionado; // so para mostrar o que foi seleccionado

private String[] osItensDaLista ={"um", "dois", "tres", "quatro", "cinco"};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

oSpin = (Spinner)findViewById(R.id.spinner1);

seleccionado = (TextView)findViewById(R.id.textView1);

ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1, osItensDaLista);

oSpin.setAdapter(adapter1);

oSpin.setOnItemSelectedListener(new OnItemSelectedListener() {

// @Override FIXME bug fix,poder usar ou n~ao depende da vers~ao do SDK?

public void onItemSelected(AdapterView<?> parentView,

View selectedItemView, int position, long id) {

seleccionado.setText(osItensDaLista[position]);

}

// @Override FIXME bug fix,poder usar ou n~ao depende da vers~ao do SDK?

public void onNothingSelected(AdapterView<?> parentView) {

seleccionado.setText("?");

}

});

}

}

Uses MainActivity 26 and onCreate() 27.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 48: Android Devel

48 Computacao Movel

FIXME GridView

1.8.2 ExpandableListView (arvore com 2 nıveis)

FIXME

1.8.3 ActionBar (tabs)

FIXME

48 〈XML layout inflater 48〉≡public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View result;

if (getArguments().getInt(ARG_SECTION_NUMBER) == 2)

result=inflater.inflate(R.layout.tab2, container, false);

else

if (getArguments().getInt(ARG_SECTION_NUMBER) == 1)

result=inflater.inflate(R.layout.tab1, container, false);

else

result=inflater.inflate(R.layout.tab3, container, false);

return result;

}

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 49: Android Devel

Computacao Movel 49

1.8.4 Criar activities com sub-activities

Exemplo de uma activity simples chamada MulMainActivity que contem um botao que lhe per-mite chamar o sub-activity existente no ficheiro SubActivity1.java, sendo que esse SubActivity1quando termina devolve o controlo ao MulMainActivity, podendo-se portanto saltar de um acti-vity para outro e voltar ao ponto inicial tantas vezes quantas se quiser.

Para evitar problemas com o contexto do this por o on click listener ser uma classe anonima(logo o this iria referir-se a classe anonima e nao ao MulMainActivity), criou-se uma funcaoseparada chamada executarOutraActivity() que e chamada tomando como argumento o nomeda classe a executar (note-se que o nome nao e dado como uma String, tem de se dar o nome daclasse sem se colocar aspas, isto e, o argumento e do tipo classe).

49 〈version/androidDevel/MulMainActivity.java 49〉≡package pt.ismai.cm2012;

import android.os.Bundle;

import android.app.Activity;

import android.view.View;

import android.content.Intent;

import android.widget.Button;

public class MulMainActivity extends Activity {

protected Button button1;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

button1 =(Button)findViewById(R.id.button1);

button1.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

executarOutraActivity(SubActivity1.class);

}

});

}

private void executarOutraActivity(Class<?> subActividade) {

Intent x = new Intent(this, subActividade);

startActivity(x);

}

}

This code is written to file version/androidDevel/MulMainActivity.java.Uses onCreate() 27 and setOnClickListener().

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 50: Android Devel

50 Computacao Movel

Note-se que ao se executar finish() se fecha o activity e se devolve o controle ao activity queo chamou.

50a 〈version/androidDevel/SubActivity1.java 50a〉≡package pt.ismai.cm2012;

import android.os.Bundle;

import android.app.Activity;

import android.view.View;

import android.content.Intent;

import android.widget.Button;

import android.widget.EditText;

public class SubActivity1 extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.subactivity_main);

final EditText campo1 = (EditText)findViewById(R.id.editText1);

final EditText campo2 = (EditText)findViewById(R.id.editText2);

final Button finished = (Button)findViewById(R.id.button2);

finished.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

finish();

}

});

}

}

This code is written to file version/androidDevel/SubActivity1.java.Uses onCreate() 27 and setOnClickListener().

50b 〈version/androidDevel/mul actvity main.xml 50b〉≡<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity" >

<Button

android:id="@+id/button1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentTop="true"

android:layout_centerHorizontal="true"

android:layout_marginTop="124dp"

android:text="Button" />

</RelativeLayout>

This code is written to file version/androidDevel/mul actvity main.xml.Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 51: Android Devel

Computacao Movel 51

51 〈version/androidDevel/mul subactity main.xml 51〉≡<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >

<EditText

android:id="@+id/editText1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:ems="10" >

<requestFocus />

</EditText>

<EditText

android:id="@+id/editText2"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:ems="10"

android:inputType="number" />

<Button

android:id="@+id/button2"

style="?android:attr/buttonStyleSmall"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Terminate" />

</LinearLayout>

This code is written to file version/androidDevel/mul subactity main.xml.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 52: Android Devel

52 Computacao Movel

Por fim eis o AndroidManifest.xml deste programa. E necessario registar uma accao poromissao (a <action>) para esta activity, sendo importante que no “activity android:name”apareca ".SubActivity1" [Steele e To 2011, pag. 37].

52a 〈version/androidDevel/GaleriaWidgets-AndroidManifest.xml 52a〉≡<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="pt.ismai.cm2012"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk

android:minSdkVersion="8"

android:targetSdkVersion="16" />

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name="pt.ismai.cm2012.MainActivity"

android:label="@string/app_name" >

<intent-filter>

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

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name=".SubActivity1"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.VIEW"/>

<category android:name="android.intent.category.DEFAULT"/>

</intent-filter>

</activity>

</application>

</manifest>

This code is written to file version/androidDevel/GaleriaWidgets-AndroidManifest.xml.Uses MainActivity 26.

1.9 Escrever e ler de um Ficheiro de Texto

Se no Android se pretender escrever ou ler algo de um ficheiro de texto muito provavelmentepretende-se escrever para a placa de memoria de armazenamento externo. Para que o programa te-nha permissao para escrever na memoria externa e necessario adicionar ao fim do AndroidManifest.xmlo seguinte:

52b 〈no AndroidManifest.xml dar permissoes para escrever na memoria externa 52b〉≡...

</application>

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

</manifest>

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 53: Android Devel

Computacao Movel 53

A classe FicheiroDeTexto implementa a funcionalidade basica para manipular um ficheiro detexto. Suporta diversos modos de abertura do ficheiro (no construtor), escrever texto (uma oumais linhas), ler uma linha de texto, ler desde o ponto onde se encontre ate ao fim do ficheiro,e fechar o ficheiro. Os erros se existirem sao escritos para o LogCat e sao igualmente mantidosinternamente pelo que podem ser obtidos por uma chamada a obterMensagemErro().

53 〈version/androidDevel/FicheiroDeTexto.java 53〉≡package pt.ismai.cm2012;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import android.util.Log;

import android.os.Environment;

public class FicheiroDeTexto {

boolean append;

int modo;

String filename;

String errorMsg;

String errorTag;

BufferedReader br;

BufferedWriter bw;

〈FicheiroDeTexto: public FicheiroDeTexto(String errorTag, String filename, int modo) 54a〉〈FicheiroDeTexto: public boolean escreverTexto(String osDados) 55b〉〈FicheiroDeTexto: public String lerUmaLinha() 56a〉〈FicheiroDeTexto: public String lerAteAoFim() 56b〉〈FicheiroDeTexto: public boolean fechar() 56c〉〈FicheiroDeTexto: public void escreverErroNoLog() 57b〉〈FicheiroDeTexto: public String obterMensagemErro() 57a〉

}

This code is written to file version/androidDevel/FicheiroDeTexto.java.Defines:

FicheiroDeTexto, used in chunks 54a and 58.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 54: Android Devel

54 Computacao Movel

No construtor explicita-se a tag a usar nas mensagens de erro, o nome do ficheiro, e o modode abertura do ficheiro.

A abertura do ficheiro suporta 3 modos de operacao: 0) readonly; 1) so para escrita, escrevendopor cima do que ja existia, isto e, truncando o ficheiro antes de comecar a escrever; 2) escrita emmodo append. Falta implementar o suporte para abrir um ficheiro no modo leitura e escrita (naofoi feito por ser menos interessante no contexto do Android).

Note-se que foi propositado o uso de um switch em que um dos case nao tem break.54a 〈FicheiroDeTexto: public FicheiroDeTexto(String errorTag, String filename, int modo) 54a〉≡ (53)

public FicheiroDeTexto(String errorTag, String filename, int modo) {

this.errorTag = errorTag;

this.filename = filename;

this.modo = modo;

br = null;

bw = null;

File rootDir = Environment.getExternalStorageDirectory();

File f = new File(rootDir, filename);

append = false;

switch (modo) {

case 0: // READONLY

〈FicheiroDeTexto: abrir so para leitura 54b〉break;

case 2: // APPEND, tem de estar aqui antes do WRITEONLY

append = true;

// break; // de proposito n~ao tem break de modo a continuar pelo

// "case 1:" abaixo

case 1: // WRITEONLY

〈FicheiroDeTexto: abrir para escrita (quer seja append quer nao) 55a〉break;

default:

errorMsg = "Erro ao abrir ’" + filename + "’ no modo ’" + modo + "’, tem de ser 0, 1 ou 2";

escreverErroNoLog();

break;

}

}

Defines:FicheiroDeTexto(), used in chunk 58b.

Uses escreverErroNoLog() 57b and FicheiroDeTexto 53.

54b 〈FicheiroDeTexto: abrir so para leitura 54b〉≡ (54a)

try {

if (rootDir.canRead()) {

FileReader filereader = new FileReader(f);

br = new BufferedReader(filereader, 8 * 1024);

} else {

errorMsg = "Erro de permiss~oes ao abrir para leitura ’"

+ filename + "’";

escreverErroNoLog();

}

} catch (IOException e) {

errorMsg = "Erro ao abrir p/ leitura ’" + filename + "’: "

+ e.getMessage();

escreverErroNoLog();

}

Uses escreverErroNoLog() 57b.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 55: Android Devel

Computacao Movel 55

55a 〈FicheiroDeTexto: abrir para escrita (quer seja append quer nao) 55a〉≡ (54a)

try {

if (rootDir.canWrite()) {

FileWriter filewriter = new FileWriter(f, append);

bw = new BufferedWriter(filewriter, 8 * 1024);

} else {

errorMsg = "Erro de permiss~oes ao abrir para escrita ’"

+ filename + "’";

escreverErroNoLog();

}

} catch (IOException e) {

errorMsg = "Erro ao abrir p/ escrita ’" + filename + "’: "

+ e.getMessage();

escreverErroNoLog();

}

Uses escreverErroNoLog() 57b.

Escreve verbatim o texto que lhe for fornecido, qualquer que ele seja (isto e, pode consistirnuma ou mais linhas). Em caso de erro devolve false.

55b 〈FicheiroDeTexto: public boolean escreverTexto(String osDados) 55b〉≡ (53)

public boolean escreverTexto(String osDados) {

try {

if (bw != null)

bw.write(osDados);

else {

errorMsg = "Erro, o ficheiro ’" + filename

+ "’ n~ao estava aberto para escrita";

escreverErroNoLog();

return false;

}

} catch (IOException e) {

errorMsg = "Erro ao escrever em ’" + filename + "’: "

+ e.getMessage();

escreverErroNoLog();

return false;

}

return true;

}

Defines:escreverTexto(), used in chunk 58b.

Uses escreverErroNoLog() 57b.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 56: Android Devel

56 Computacao Movel

Le uma linha de texto, isto e, uma linha terminada por \n (line feed), ou por \r (carriagereturn) ou por \r\n (carriage return seguido de line feed). O texto devolvido inclui um \n linefeed. Em caso de erro devolve um string nulo.

56a 〈FicheiroDeTexto: public String lerUmaLinha() 56a〉≡ (53)

public String lerUmaLinha() {

String s = null;

try {

if (br != null)

s = br.readLine();

else {

errorMsg = "Erro, o ficheiro ’" + filename

+ "’ n~ao estava aberto para leitura";

escreverErroNoLog();

return null;

}

} catch (IOException e) {

errorMsg = "Erro ao ler de ’" + filename + "’: " + e.getMessage();

escreverErroNoLog();

return null;

}

return s + "\n";

}

Defines:lerUmaLinha(), never used.

Uses escreverErroNoLog() 57b.

Le o ficheiro na sua totalidade desde o ponto onde se encontrava ate ao fim. Utiliza umStringBuffer em vez de um String de modo a ser mais eficiente a ler o ficheiro ja que evita estarsempre a concatenar Strings. Em caso de erro devolve um string nulo.

56b 〈FicheiroDeTexto: public String lerAteAoFim() 56b〉≡ (53)

public String lerAteAoFim() {

StringBuffer sb = new StringBuffer(4096);

try {

if (br != null) {

String s;

while ((s = br.readLine()) != null) {

sb.append(s);

sb.append("\n");

}

return sb.substring(0);

} else {

errorMsg = "Erro, o ficheiro ’" + filename

+ "’ n~ao estava aberto para leitura";

escreverErroNoLog();

return null;

}

} catch (IOException e) {

errorMsg = "Erro ao ler de ’" + filename + "’: " + e.getMessage();

escreverErroNoLog();

return null;

}

}

Defines:lerAteAoFim(), used in chunk 58b.

Uses escreverErroNoLog() 57b.

Fecha o ficheiro, esvaziando os buffers se for caso disso. Em caso de erro devolve false.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 57: Android Devel

Computacao Movel 57

56c 〈FicheiroDeTexto: public boolean fechar() 56c〉≡ (53)

public boolean fechar() {

try {

if (bw != null)

bw.close();

else if (br != null)

br.close();

} catch (IOException e) {

errorMsg = "Erro ao fechar ’" + filename + "’: " + e.getMessage();

escreverErroNoLog();

return false;

}

return true;

}

Defines:fechar(), never used.

Uses escreverErroNoLog() 57b.

Uma chamada a esta funcao permite obter o texto da ultima mensagem de erro (para permitiro seu uso na propria aplicacao, por exemplo num Toast, ja que esse mesmo texto ja foi escritopara o LogCat).

57a 〈FicheiroDeTexto: public String obterMensagemErro() 57a〉≡ (53)

public String obterMensagemErro() {

return errorMsg;

}

Defines:obterMensagemErro(), never used.

Dado que a escrita de todas as mensagens de erro para o log esta colocada aqui, quem pretenderusar outra forma de log (ou ate nenhuma) pode simplesmente redefinir esta funcao (fazer umasubclasse que redefine esta funcao, idealmente marcando-a como @Override de modo a garantirque esta mesmo a substituir esta funcao).

57b 〈FicheiroDeTexto: public void escreverErroNoLog() 57b〉≡ (53)

public void escreverErroNoLog() {

Log.e(errorTag, errorMsg);

}

Defines:escreverErroNoLog(), used in chunks 54–56 and 58a.

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 58: Android Devel

58 Computacao Movel

Eis um exemplo de como se podia criar uma subclasse desta classe que em vez de escreverpara o LogCat usa um Toast para fazer aparecer a mensagem de erro no ecra. Basta adicionarum novo atributo para armazenar o contexto do Toast, criar um construtor sem o parametrodo errorTag (por nao ser necessario) e com o parametro extra que da o contexto para o Toast,depois no construtor chama-se o construtor da superclasse usando “super(...)” e inicializa-se oatributo extra. Para alem disso a unica coisa que se faz e redefinir a funcao escreverErroNoLog()de modo a usar um Toast.

58a 〈version/androidDevel/FicheiroDeTextoSemLogComToast.java 58a〉≡import android.widget.Toast;

import android.content.Context;

public class FicheiroDeTextoSemLogComToast extends FicheiroDeTexto {

Context toastContext;

public FicheiroDeTextoSemLogComToast(String filename, int modo, Context c) {

super(null, filename, modo);

toastContext = c;

}

@Override

public void escreverErroNoLog() {

Toast.makeText(toastContext, errorMsg, Toast.LENGTH_SHORT).show();

}

}

This code is written to file version/androidDevel/FicheiroDeTextoSemLogComToast.java.Defines:

FicheiroDeTextoSemLogComToast, used in chunk 58b.Uses escreverErroNoLog() 57b and FicheiroDeTexto 53.

Isto significa que agora se propositadamente usarmos mal as duas classes, na primeira abrindopara leitura e tentando escrever, e na segunda abrindo para escrita e tentando ler, a mensagemde erro da primeira vai para o LogCat e a mensagem de erro da segunda aparece num Toast.Note-se que devido ao polimorfismo, embora a variavel f seja do tipo FicheiroDeTexto ela podeser inicializada com um FicheiroDeTexto ou com um FicheiroDeTextoSemLogComToast (por seruma subclasse), e devido ao suporte para polimorfismo o metodo chamado quando da erro e ocorrecto.

58b 〈exemplos do uso do FicheiroDeTexto e FicheiroDeTextoSemLogComToast 58b〉≡... // por exemplo no contexto dum Activity

FicheiroDeTexto f = new FicheiroDeTexto("FICHEIRO", "oMeuFicheiro.txt", 0);

f = new FicheiroDeTexto("FICHEIRO", "oMeuFicheiro.txt", 0);

f.escreverTexto("o meu texto 2\no meu texto 3");

f = new FicheiroDeTextoSemLogComToast("oMeuFicheiro.txt", 1, this);

String s = f.lerAteAoFim();

...

Uses escreverTexto() 55b, FicheiroDeTexto 53, FicheiroDeTexto() 54a, FicheiroDeTextoSemLogComToast 58a,and lerAteAoFim() 56b.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 59: Android Devel

Computacao Movel 59

1.10 Aplicacao de Manutencao

Pretende-se criar uma aplicacao Android de manutencao de avarias que possua as seguintes fun-cionalidades:

1. Permita a introducao de dados via um interface grafico

2. Tenha mais do que um ecra (isto e, necessite de usar mais do que um activity) e possua umHelp About que despoleta um Toast

3. Armazene os dados numa base de dados, executando operacoes de insert, update, selectda base de dados

4. Necessite de executar tarefas em background

5. Via HTTP fale com um servidor remoto e sincronize a base de dados local com a base dedados do servidor

6. Utilize tipos genericos e excepcoes

7. Eis o formato pretendido para a base de dados:

• tipoAvaria(id, nome) onde nome e escolhido pelo utilizador podendo ser por exemplo:Menor, Grave, Urgente, Crıtica

• cliente(id, nome, morada)

• statusAvaria(id, nome), onde o nome e um dos 4 valores fixos: Registado, Sincroni-zado, A ser tratado, Resolvido

• avaria(id, cliente, tipo, status, observacao), onde somente observacao e textolivre, os restantes campos permitem escolher valores pre-definidos pertencentes as res-tantes tabelas

8. O ecra de topo deve ter 4 botoes que permitem escolher entre: Avarias (registar uma novaavaria ou actualizar os dados de uma avaria ja existente), Clientes (adicionar um novocliente ou actualizar os dados de um cliente ja existente), Tipos (ver os tipos existentes ouadicionar novos tipos de avarias), Sincronizar (ligar-se ao servidor remoto usando uma tarefaassıncrona), a aplicacao pode continuar a ser usada, quando acabar avisa que terminou

9. Minimum API 8 (Android 2.2 Froyo), target SDK (API 18, Android 4.3), compile with API19 (Android 4.4 KitKat)

59 〈version/androidDevel/activity main.xml 59〉≡<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context=".MainActivity" >

<Button

android:id="@+id/botaoTipos"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/botaoAvarias"

android:layout_alignParentTop="true"

android:layout_alignRight="@+id/botaoClientes"

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 60: Android Devel

60 Computacao Movel

android:layout_marginTop="14dp"

android:text="@string/textoTipos" />

<Button

android:id="@+id/botaoSincronizar"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_centerHorizontal="true"

android:layout_marginBottom="62dp"

android:text="@string/textoSincronizar" />

<Button

android:id="@+id/botaoAvarias"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_above="@+id/botaoSincronizar"

android:layout_alignLeft="@+id/botaoSincronizar"

android:layout_alignRight="@+id/botaoSincronizar"

android:layout_marginBottom="58dp"

android:text="@string/textoAvarias" />

<Button

android:id="@+id/botaoClientes"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@+id/botaoSincronizar"

android:layout_alignRight="@+id/botaoAvarias"

android:layout_below="@+id/botaoTipos"

android:layout_marginTop="35dp"

android:text="@string/textoClientes" />

</RelativeLayout>

This code is written to file version/androidDevel/activity main.xml.Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 61: Android Devel

Computacao Movel 61

61 〈database stuff 61〉≡public class DatabaseHelper extends SQLiteOpenHelper {

private static final String DATABASE_NAME = "myDatabase.db";

private static final int SCHEMA = 1;

static final String TITLE = "titulo";

static final String VALUE = "valor";

static final String TABLE = "constantes";

public DatabaseHelper(Context context) {

super(context, DATABASE_NAME, null, SCHEMA);

}

public void onCreate(SQLiteDatabase db) {

db.execSQL("CREATE TABLE constants (_id INTEGER PRIMARY KEY AUTOINCREMENT,

title TEXT, value REAL);");

}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

throw new RuntimeException("N~ao devia acontecer: base de dados obsoleta");

}

public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {

throw new RuntimeException("N~ao devia acontecer: base de dados demasiado nova");

}

}

db = new DatabaseHelper(getActivity().getApplicationContext());

db.getWriteableDatabase().rawQuery(...);

Cursor c = db.getReadableDatabase().rawQuery("SELECT _id, name, value from

dados", null);

...

c.close()

Uses onCreate() 27.

1.11 Indice de Fragmentos de Codigo

〈Android: escrever para o log 18〉 18〈Android: exemplo de um AndroidManifest.xml 14〉 14〈Android: exemplo de um Service 13〉 13〈Android: exemplo de uma Activity 12b〉 12b〈Android: ver os registos da base de dados de contactos 12a〉 12a〈AsyncGenerator: protected String doInBackground(Integer... args) 33a〉 31a, 33a〈AsyncGenerator: protected void onPostExecute(String msg) 34a〉 31a, 34a〈AsyncGenerator: protected void onPreExecute() 32〉 31a, 32〈AsyncGenerator: protected void onProgressUpdate(Double... percentComplete) 33b〉 31a, 33b〈AsyncGenerator: public AsyncGenerator(View botao) 31b〉 31a, 31b〈AsyncTask: protected String doInBackground(String... args) 24b〉 23b, 24b〈AsyncTask: protected void onPostExecute(String msg) 24a〉 23b, 24a, 25〈AsyncTask: protected void onPreExecute() 23d〉 23b, 23d, 25〈AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c〉 23b,

23c, 25〈AsyncTask: public void onCreate(Bundle state) 24c〉 23b, 24c〈CicloDeVida: MyAsyncInit() 31a〉 26, 31a〈CicloDeVida: onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy() 29a〉 26,

29a

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 62: Android Devel

62 Computacao Movel

〈CicloDeVida: protected void restoreVarsFromBundle(Bundle savedInstanceState) 28b〉 26, 28b〈CicloDeVida: public void atualizarNumeros() 30b〉 26, 30b〈CicloDeVida: public void gerarNumeros(int n) 30a〉 26, 30a〈CicloDeVida: public void onCreate(Bundle savedInstanceState) 27〉 26, 27〈CicloDeVida: public void onRestoreInstanceState(Bundle savedInstanceState) 29b〉 26, 29b〈CicloDeVida: public void onSaveInstanceState(Bundle outputState) 28a〉 26, 28a〈criar um ListView 45〉 45〈criar um spinner 47〉 47〈database stuff 61〉 61〈erros: erro se o Android SDK foi instalado num local com espacos no nome 10〉 10〈escrever para o ficheiro osMeusDados.txt 43〉 43〈exemplo: uso de AsyncTaskDemo com indicador de progresso 25〉 25〈exemplo: uso de AsyncTaskDemo mas sem indicador de progresso 23b〉 23a, 23b〈exemplos do uso do FicheiroDeTexto e FicheiroDeTextoSemLogComToast 58b〉 58b〈FicheiroDeTexto: abrir para escrita (quer seja append quer nao) 55a〉 54a, 55a〈FicheiroDeTexto: abrir so para leitura 54b〉 54a, 54b〈FicheiroDeTexto: public boolean escreverTexto(String osDados) 55b〉 53, 55b〈FicheiroDeTexto: public boolean fechar() 56c〉 53, 56c〈FicheiroDeTexto: public FicheiroDeTexto(String errorTag, String filename, int modo) 54a〉 53,

54a〈FicheiroDeTexto: public String lerAteAoFim() 56b〉 53, 56b〈FicheiroDeTexto: public String lerUmaLinha() 56a〉 53, 56a〈FicheiroDeTexto: public String obterMensagemErro() 57a〉 53, 57a〈FicheiroDeTexto: public void escreverErroNoLog() 57b〉 53, 57b〈layout XML do ListView 46〉 46〈ler do ficheiro osMeusDados.txt 44〉 44〈Mac OS X: instalar o Android SDK 7〉 7〈no AndroidManifest.xml dar permissoes para escrever na memoria externa 52b〉 52b〈SQLite3: classe MeuCursor 41a〉 41a, 42c〈sqlite3: criar a base de dados manualmente 36〉 36, 37a〈sqlite3: criar a base de dados programaticamente 38a〉 38a, 38b〈SQLite3: criar base de dados nome, telefone, localidade 42a〉 42a, 42c〈SQLite3: inserir nome, telefone, localidade 42b〉 42b, 42c〈SQLite3: query usando QueryBuilder 40〉 40, 42c〈SQLite3: strings base de dados nome, telefone, localidade 41b〉 41b, 42c〈Sqlite3: strings com os comandos SQL 37c〉 37c, 38a〈Sqlite3: strings com os comandos SQL para criar as tabelas 37b〉 37b, 37c, 39〈version/androidDevel/activity main.xml 59〉 59〈version/androidDevel/CicloDeVida-activity main.xml 35〉 35〈version/androidDevel/CicloDeVida-AndroidManifest.xml 34b〉 34b〈version/androidDevel/CicloDeVida-strings.xml 34c〉 34c〈version/androidDevel/criaBaseDadosManualmente.txt 37a〉 37a〈version/androidDevel/criaBaseDadosProgramaticamente.txt 38b〉 38b〈version/androidDevel/descontos/AndroidManifest.xml 20b〉 20b〈version/androidDevel/descontos/res/layout/main.xml 21〉 21〈version/androidDevel/descontos/res/values/strings.xml 20a〉 20a〈version/androidDevel/descontos/src/pt/ismai/cm2011/DescontosActivity.java 19〉 19〈version/androidDevel/exemploQueryComCursor.txt 42c〉 42c〈version/androidDevel/FicheiroDeTexto.java 53〉 53〈version/androidDevel/FicheiroDeTextoSemLogComToast.java 58a〉 58a〈version/androidDevel/GaleriaWidgets-AndroidManifest.xml 52a〉 52a〈version/androidDevel/HelloNumaThread.java 11〉 11〈version/androidDevel/helloWorld1/AndroidManifest.xml 15b〉 15b〈version/androidDevel/helloWorld2/AndroidManifest.xml 17〉 17

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 63: Android Devel

Computacao Movel 63

〈version/androidDevel/helloWorld1/HelloAndroid1.java 15a〉 15a〈version/androidDevel/helloWorld2/res/layout/main.xml 16a〉 16a〈version/androidDevel/helloWorld2/res/values/strings.xml 16b〉 16b〈version/androidDevel/helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java 16c〉 16c〈version/androidDevel/lixo.txt 23a〉 23a〈version/androidDevel/MainActivity.java 26〉 26〈version/androidDevel/mul actvity main.xml 50b〉 50b〈version/androidDevel/MulMainActivity.java 49〉 49〈version/androidDevel/mul subactity main.xml 51〉 51〈version/androidDevel/ScoresDatabase.java 39〉 39〈version/androidDevel/SubActivity1.java 50a〉 50a〈XML layout inflater 48〉 48

ISMAI $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Page 64: Android Devel

64 Computacao Movel

1.12 Bibliografia

Ableson, W. Frank, Robi Sen, Chris King, e C. Enrique Ortiz (2011). Android in Action (3rd ed.).Manning. ISBN 978-1-61729-050-3.

Collins, Charlie, Michael Galpin, e Matthias Kappler (2012). Android in Practice. Manning. ISBN978-1-935182-92-4.

Darcey, Lauren e Shane Conder (2011). Android Wireless Application Development (2nd ed.).Addison Wesley. ISBN 978-0-321-74301-5.

Mednieks, Zigurd, Laird Dornin, G. Blake Meike, e Masumi Nakamura (2011). ProgrammingAndroid. O’Reilly. ISBN 978-1-449-38969-7.

Murphy, Mark L. (2012). The Busy Coder’s Guide to Android Development, Version 4.3, supportsAndroid 2.x-4.1 and the R20 tools. CommonsWare LLC. ISBN 978-0-9816780-0-9.

Neil, Theresa (2012, Marco). Mobile Design Pattern Gallery. O’Reilly. ISBN 978-1-449-31432-3.

Steele, James e Nelson To (2011). The Android Developer’s Cookbook. Developer’s Library. AddisonWesley. ISBN 978-0-321-74123-3.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Page 65: Android Devel

1.13 Indice de Ficheiros

activity main.xml, 59

CicloDeVida-activity main.xml, 35CicloDeVida-AndroidManifest.xml, 34CicloDeVida-strings.xml, 34criaBaseDadosManualmente.txt, 37criaBaseDadosProgramaticamente.txt, 38

descontos/AndroidManifest.xml, 20descontos/res/layout/main.xml, 21descontos/res/values/strings.xml, 20descontos/src/pt/ismai/cm2011/DescontosActivity.java,

19

exemploQueryComCursor.txt, 42

FicheiroDeTexto.java, 53FicheiroDeTextoSemLogComToast.java, 58

GaleriaWidgets-AndroidManifest.xml, 52

HelloNumaThread.java, 11helloWorld1/AndroidManifest.xml, 15helloWorld1/HelloAndroid1.java, 15helloWorld2/AndroidManifest.xml, 17helloWorld2/res/layout/main.xml, 16helloWorld2/res/values/strings.xml, 16helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java,

16

lixo.txt, 23

MainActivity.java, 26mul actvity main.xml, 50mul subactity main.xml, 51MulMainActivity.java, 49

ScoresDatabase.java, 39SubActivity1.java, 50

65

Page 66: Android Devel

1.14 Indice Remissivo

adb, 36android

Activity, 12ambiente de desenvolvimento

configurar o eclipse, 9AndroidManifest.xml, 14arquitectura das aplicacoes, 12bases de dados, 36BroadcastReceiver, 14classes principais

Activity, 12BroadcastReceiver, 14ContentProvider, 14Intent, 12IntentFilter, 12Service, 13

ContentProvider, 14exemplos

GUI (aplicar um desconto), 19Hello World (estilo correcto), 16Hello World (estilo incorrecto), 15

instalacao ambiente de desenvolvimento, 2Mac OS X 64 bits, 7software necessario, 3Windows 7 Pro 64 bits, 4

Intent, 12IntentFilter, 12log (uso do), 17main.xml, 14minSdkVersion, 8Service, 13strings.xml, 14suporte para chaves estrangeiras, 36versoes das APIs e do SDK, 8xml

AndroidManifest.xml, 14main.xml, 14strings.xml, 14

avaliacao, 2

bases de dados, 36

C/C++, 2chave primaria

necessidade de uso do nome _id, 36

FIXME, 1

iPhone, 2

javathreads (concorrencia), 10

mensagens de erro

invalid command-line parameter: Files, 10MoSync, 4

objectivos, 2

polimorfismo, 58problemas potenciais

instalar SDK do Android num local comespacos embebidos no nome, 10

Symbian, 2

66