quinta-feira, 30 de junho de 2011

Android + RESTful + JSON

Olá pessoal,

No post anterior mostrei como criar um Web Service simples com o Jersey. Através dele podemos retornamos um arquivo JSON baseado no padrão RESTful.

Nesse pequeno POST, vou mostrar como ler os dados desse WS com o Android. O REST/JSON está tão forte que a Google já colocou nativamente no Android uma API para leitura desse tipo de arquivo. Então, dentro do seu projeto basta colocar os métodos abaixo:


private String toString(InputStream is)
throws IOException{

byte[] bytes = new byte[1024];
ByteArrayOutputStream baos =
new ByteArrayOutputStream();
int lidos;
while ((lidos = is.read(bytes)) > 0){
baos.write(bytes, 0, lidos);
}
return new String(baos.toByteArray());
}


Esse primeiro método é uma "receita de bolo". Ele será pelo método getRESTFileContent(String) (a seguir) e servirá para ler byte-a-byte (na verdade de 1024 em 1024 bytes) o conteúdo do arquivo JSON que é retornado pelo servidor, retornando-o em forma de String.

public String getRESTFileContent(String url) {
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);

try {
HttpResponse response =
httpclient.execute(httpget);

HttpEntity entity = response.getEntity();

if (entity != null) {
InputStream instream = entity.getContent();
String result = toString(instream);

instream.close();
return result;
}
} catch (Exception e) {
Log.e("NGVL", "Falha ao acessar Web service", e);
}
return null;
}

O método acima, utiliza o HttpClient (da Apache, que vem nativamente no Android) para estabelecer a conexão com o servidor, abrir o fluxo de leitura (InpuStream) e retornar o conteúdo do arquivo JSON em forma de String (feito pelo método toString(InputStream)).

private void lerTodasPessoas() {
String result = getRESTFileContent(
"http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/");
if (result == null){
Log.e("NGVL", "Falha ao acessar WS");
return;
}

try {
JSONObject json = new JSONObject(result);
JSONArray pessoasArray =
json.getJSONArray("pessoa");

JSONObject pessoaJson;

for (int i = 0; i < pessoasArray.length(); i++) {
pessoaJson = new JSONObject(
pessoasArray.getString(i));

Log.i("NGVL",
"------------------------");
Log.i("NGVL",
"id="+ pessoaJson.getInt("id"));
Log.i("NGVL",
"nome="+ pessoaJson.getString("nome"));
}
} catch (JSONException e) {
Log.e("NGVL", "Erro no parsing do JSON", e);
}
}

private void lerUmaPessoa() {
// Observe o ID da pessoa no final da URL
String result = getRESTFileContent(
"http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/1");
if (result == null){
Log.e("NGVL", "Falha ao acessar WS");
return;
}

try {
JSONObject pessoaJson = new JSONObject(result);

Log.i("NGVL",
"id="+ pessoaJson.getInt("id"));
Log.i("NGVL",
"nome="+ pessoaJson.getString("nome"));

} catch (JSONException e) {
Log.e("NGVL", "Erro ao fazer parsing do JSON", e);
}
}

Este é o método que deverá ser chamado diretamente e utilizará os demais. Inicialmente ele pega o conteúdo do arquivo JSON informando a URL do mesmo. Notem que foi usado o IP 10.0.2.2 estabelecido pelo Android para acessarmos um serviço da máquina local.
Em seguida, criamos o objeto JSON e a partir dele obtemos o array de pessoas. Esse array contém todos os objetos JSON do arquivo. Em seguida, iteramos sobre este array atribuindo à variável pessoaJson. E com essa variável pegamos a propriedade de cada objeto retornado.
Editado em 06/11/2011
Agora vou mostrar como passar um JSON para o nosso servidor inserir uma nova Pessoa.

private String inserirPessoa() {
try {
URL url = new URL(
"http://10.0.2.2:8080/BlogWS_REST/rest/pessoas/");
HttpURLConnection conexao =
(HttpURLConnection)url.openConnection();

conexao.setRequestMethod("POST");
conexao.addRequestProperty(
"Content-type", "application/json");

conexao.setDoOutput(true);

conexao.connect();

OutputStream os = conexao.getOutputStream();
os.write("{\"id\":\"1\",\"nome\":\"Nelson\",\"outro\":\"info1\"}".getBytes());
os.flush();

InputStream is = conexao.getInputStream();
return toString(is);

} catch (Exception e) {
e.printStackTrace();
return "ERRO!";
}
}

Este método abre a conexão com o servidor, mas desta vez para fazer um POST ao invés de um GET. Ele passará um objeto JSON que representa um objeto Pessoa no servidor através de um OutputStream. Aqui colocamos um JSON hard-coded, mas você poderia utilizar a bibilioteca GSON do Google para converter seus objetos em JSON.
[/Editado]

Dois detalhes: 1) toda comunicação deve ser feita na Thread separada da UI. Logo chame o método acima dentro de uma outra Thread ou use a abordagem da AsyncTask que eu mostrei nesse post. 2) Você deve adicionar a permissão android.permission.INTERNET no seu AndroidManifest.XML conforme abaixo:
<uses-permission android:name="android.permission.INTERNET"/>


Qualquer dúvida deixem seus comentários.

4br4ç05,
nglauber

RESTful Web Services com Jersey

Olá povo,

O padrão REST (Representational State Transfer) está cada vez mais se tornando um padrão para compartilhamento de informações nos serviços web. Criado em 2000 esse padrão utiliza o conceito de recursos que são identificados por um ids globais e utiliza os próprios métodos do protocolo HTTP para realizar operações sobre esses recursos em um servidor Web. Por exemplo, o método GET obtém um recurso ou uma lista deles, enquanto que o PUT insere, o POST atualiza e o DELETE remove. Outra característica interessante do REST é que podemos optar pelo tipo de retorno do serviço, ele pode ser: XML, JSON, ATOM, etc.

Para estabelecer um padrão para disponibilização de Web Services utilizando RESTful, foi criada a JSR-311, e sua implementação de referência é o Jersey, desenvolvido pela Sun (que saudade de você :) que utilizaremos nesse post.

Faça o download do Jersey (http://jersey.java.net/) e descompacte-o em algum lugar do seu computador (usei a versão 1.8). Para o nosso exemplo, além dele, você vai precisar do Tomcat (utilizei a versão 6.0), Eclipse for Java EE Developers ou o Eclipse comum com o plugin WTP (Web Tools Platform)

Inicie um novo Web/Dynamic Web Project no Eclipse chamado BlogWS_REST. Copie os JARs que estão na pasta lib do ZIP do Jersey para a pasta WebContent/WEB-INF/lib do seu projeto. Em seguida, adicione esses arquivos no Build Path do projeto. Clicando com o botão direito sobre eles com o botão direito, Build Path > Add to build path.

Agora, abra o arquivo Web Content/WEB-INF/web.xml e adicione as linhas abaixo dentro da tag <web-app>

<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>
com.sun.jersey.config.property.packages
</param-name>
<param-value>
ngvl.javaee.rest
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

Quem já fez alguma coisa com Java Web já deve saber que estamos declarando o Servlet que responderá pelas requisições HTTP. As tags servlet-class está declarando o Servlet do Jersey. Passamos como parâmetros de inicialização o (init-param) o nome do pacote da nossa aplicação que terá os web services, no nosso caso, ngvl.javaee.rest. Depois informamos que esse servlet iniciará junto com o servidor (load-on-startup). E por último, fazemos o mapeamento de URL que esse Servlet responderá. No nosso caso, dizemos que qualquer requisição que venha pra http://servidor/BlogWS_REST/rest/ será tratado pelo Servlet do Jersey.

Agora vamos fazer o primeiro Web Service Rest. Crie a classe HelloResource e deixe-a conforme abaixo:

package ngvl.javaee.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class HelloResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayHello() {
return "Hello Jersey";
}
}

A anotação @Path informa o caminho para acessar esse serviço. O @GET diz o método HTTP que estamos utilizando. Já o @Produces determina o tipo de retorno que é dado, neste caso, texto plano.

Execute a aplicação (botão direito sobre o projeto Run as > Run on server) e depois execute no browser http://localhost:8080/BlogWS_REST/rest/hello. Você verá o texto "Hello Jersey" no navegador :)

Muito lindo! Mas na vida real não trabalhamos com "Olá mundo" :) Assim como no post que falei sobre o AXIS e Web Services com SOAP, vou mostrar como trabalhar com objetos. Crie a classe Pessoa e deixe-a conforme abaixo.

package ngvl.javaee.rest;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Pessoa {
private int id;
private String nome;
@DefaultValue("")
private String outro; // atributo opcional

// Contrutor padrão é obrigatório
public Pessoa(){
}

public Pessoa(int id, String nome, String outro) {
this.id = id;
this.nome = nome;
this.outro = outro;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getOutro() {
return outro;
}
public void setOutro(String outro) {
this.outro = outro;
}
}


A única observação sobre essa classe é a notação @XmlRootElement para que ela possa ser retornada como JSON, XML, whatever... :) Outro detalhe é o campo opcional "outro", isso fará que ele não seja obrigatório sua informação
Agora vamos a implementação do Web Service :)
package ngvl.javaee.rest;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/pessoas")
public class PessoaResource {

static List<Pessoa> pessoas;
static {
pessoas = new ArrayList<Pessoa>();
pessoas.add(new Pessoa(1, "Nelson", "info1"));
pessoas.add(new Pessoa(2, "Glauber", null));
pessoas.add(new Pessoa(3, "Vasconcelos", null));
pessoas.add(new Pessoa(4, "Leal", "info2"));
}

@GET
@Produces({ MediaType.APPLICATION_JSON })
public List<Pessoa> obterPessoas() {
return pessoas;
}

@GET
@Path("{pessoa}")
@Produces(MediaType.APPLICATION_JSON)
public Pessoa obterPessoa(@PathParam("pessoa") int id) {
return obterPessoas().get(id);
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public String insert(Pessoa p){
if (p != null){
pessoas.add(p);
return "Funcionou!";
} else {
return "Erro ao adicionar pessoa.";
}
}
}


Quase todas as notações nós discutimos aqui. O método obterPessoas retorna um JSON com todas as pessoas da lista. Para acessar esse método, basta colocar no browser http://localhost:8080/BlogWS_REST/rest/pessoas o retorno será um arquivo nesse formato:

{"pessoa":[{"id":"1","nome":"Nelson"},{"id":"2","nome":"Glauber"},{"id":"3","nome":"Vasconcelos"},{"id":"4","nome":"Leal"}]}

Já o segundo método obtém apenas uma pessoa. Para acessa-lo, use a URL http://localhost:8080/BlogWS_REST/rest/pessoas/1 . Utilizar esse "1" no final é determinado pela notação @Path("{pessoa}") onde pessoa será o id do recurso desejado. O retorno dessa chamada será o arquivo abaixo:

{"id":"2","nome":"Glauber"}

O último método insere um objeto Pessoa na lista. Note que ele utiliza a notação @Consumes que indica que um parâmetro no formato JSON será enviado para esse método. Ou seja, um JSON será enviado e convertido "automagicamente" para um objeto Pessoa.

Bem pessoal, é isso. Esse post foi baseado nessa página aqui. Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

segunda-feira, 20 de junho de 2011

Android: Dicas 3

Olá povo,

Meus alunos, mesmo após o fim das aulas, não me deixam em paz. E isso é bom ;) Eles ficam mandando dúvidas e soluções para problemas em que eles se deparam no dia-a-dia do desenvolvimento com Android. Sendo assim, lá vai mais uma leva de dicas de Android.

Dica 1 (dúvida de Felipe Bonezi)

A dúvida era como obter o tamanho da tela. Segue abaixo:
Display display = 
getWindowManager().getDefaultDisplay();

int width = display.getWidth();
int height = display.getHeight();


Dica 2 (dica de Bruno Baudel)

Ocultar o teclado virtual programaticamente.
InputMethodManager imm = (InputMethodManager) 
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);


Dica 3 (dica de Felipe Vasconcelos)

Essa é a mais complicada de todas. Como modificar o background das abas de um TabHost. A primeira coisa que devemos ter são as imagens das abas. Se você não diver, crie no XML mesmo :) segue abaixo os XML para aba selecionada e normal, salve-os na pasta res/drawable com os nomes aba_normal.xml e aba_normal.xml.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<gradient
android:startColor="#000000"
android:endColor="#CCCCCC"
android:angle="90"
android:type="linear" />
<padding
android:left="10dp"
android:top="10dp" />
<corners
android:topLeftRadius="15dp"
android:topRightRadius="15dp" />
</shape>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<gradient
android:startColor="#000000"
android:endColor="#00FF00"
android:angle="90"
android:type="linear" />
<padding
android:left="10dp"
android:top="10dp" />
<corners
android:topLeftRadius="15dp"
android:topRightRadius="15dp" />
</shape>

Uma vez definida as imagens, precisamos associa-las ao estado da TabWidget, ou seja, quando a aba estiver selecionada, use a segunda imagem, caso contrário use a primeira. Crie o arquivo aba_selector também na pasta res/drawable.
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:drawable="@drawable/aba_selecionada" />
<item
android:state_selected="false"
android:drawable="@drawable/aba_normal" />
</selector>

Depois é só setar o background de cada aba do TabWidget.
TabWidget tabWidget = getTabWidget();
for (int i = 0; i < tabWidget.getChildCount(); i++) {
View v = tabWidget.getChildAt(i);
v.setBackgroundDrawable(
getResources().getDrawable(R.drawable.aba_selector));
}



Dica 4 (dica de Ricardo Gilson)

Sem mais aguentar a linha "TODO" que é adicionada toda vez que criamos algum tipo de bloco ou método no Eclipse, Ricardo foi em busca de como retirá-lo. A solução é ir até o menu Window > Preferences... No lado esquerdo, selecione Java > Code style > Code templates. Ao percorrer a lista é possível observar que o TODO está configurado para várias chamadas:
Method body, Construtor body, Catch....

O jeito é remover a linha do TODO de cada um deles...
// ${todo} Auto-generated catch block.


É isso pessoal, vão mandando suas dicas que eu coloco aqui! :)
Obrigado a todos que contribuíram.

4br4ç05,
nglauber

sexta-feira, 17 de junho de 2011

Tarefas assíncronas no Android

Olá pessoal,

Em muitos momentos será necessário a comunicação com um servidor web para fazer a integração da aplicação mobile. Essa atividade deve ser feita em uma Thread separada da UI, entretanto precisamos dar um feedback para o usuário de que essa operação está sendo executada. Para esse tipo de tarefa o Android disponibiliza a classe AsyncTask. Ele permite configurar o que será feito antes, durante e depois da execução de uma tarefa.
No código eu vou acessar a página do dicionário Michaelis para obter a tradução de uma determinada palavra. No onClick do botão, estou iniciando uma nova AsyncTask. Notem que foi passado o conteúdo da caixa de texto como parâmetro pro método execute.
O método downloadArquivo baixará o conteúdo HTML que é gerado pelo site do Michaelis.

public class PrincipalActivity extends Activity
implements OnClickListener {

private String URL_TXT  =
  "http://michaelis.uol.com.br/moderno/portugues/"+
  "index.php?lingua=portugues-portugues&"+
  "typeToSearchRadio=exactly&pagRadio=50&palavra=";
private TextView txtHTML;
private EditText edtTexto;
 
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.teste_async);

  findViewById(R.id.button1).setOnClickListener(this);

  txtHTML = (TextView)findViewById(R.id.textView2);
  edtTexto = (EditText)findViewById(R.id.editText1);
}

public void onClick(View v) {
  new MinhaAsyncTask().execute(
    edtTexto.getText().toString());
}

public String downloadArquivo(String url) {
  HttpURLConnection connection = null;
  InputStream is = null;
  ByteArrayOutputStream bos = null;
  String s = null;
  
  try {
    URL urlAddr = new URL(url);
    connection = (HttpURLConnection)
      urlAddr.openConnection();

    connection.setRequestMethod("GET");
    connection.setDoInput(true);     
    connection.setDoOutput(false);
    connection.connect();
   
    is = connection.getInputStream();
   
    bos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length;
   
    while ((length = is.read(buffer)) > 0 ) {
      bos.write(buffer, 0, length);
    }
    
    s = new String(bos.toByteArray(), "ISO-8859-1");
   
  } catch (Exception e) {
    e.printStackTrace();
  }  finally {
    try {
      bos.close();
      is.close();
      connection.disconnect();
    } catch (Exception e){}
  }

  return s;
}
 
class MinhaAsyncTask 
  extends AsyncTask <String, Void, String> {

  private ProgressDialog progressDialog;

  protected void onPreExecute() {
    progressDialog = new ProgressDialog(
      PrincipalActivity.this);

    progressDialog.setMessage("Aguarde...");
    progressDialog.show();
  }

  protected String doInBackground(String... v) {
    String s = downloadArquivo(
      URL_TXT + v[0].toString());

    return s;
  }

  protected void onPostExecute(String result) {
    txtHTML.setText(result);
    progressDialog.dismiss();
  }
}
}


Nossa AsyncTask é declarado recebendo 3 valores genéricos. O primeiro é o tipo do dado que será passado como parâmetro na para o método que executará em segundo plano (doInBackground). O segundo é o tipo do dado que pode ser usado para enviar o progresso de alguma execução da thread background (onProgressUpdate). E o último é o tipo de retorno da ação que será realizada em segundo plano.
No nosso caso usamos: uma String, que será a palavra a ser buscada; Void, já que não usarei o progresso com números; e a String que será o HTML lido.

EDITADO 19/06/2012
Para o Android, toda aplicação que possa onerar ou por em risco os dados do usuário deve requerer uma permissão para pode utilizar determinado recurso. No caso da nossa aplicação é a permissão de acesso a internet. Adicione a permissão abaixo dentro da tag do AndroidManifest.xml
<uses-permission
  android:name="android.permission.INTERNET" />

Esse post foi baseado no post sobre AsyncTask do blog Eu, Android e foi enviado pelo meu aluno Bruno Baudel. Qualquer dúvida, olhem o blog dos caras e deixem seus comentários.

4br4ç05,
nglauber

quarta-feira, 15 de junho de 2011

Atributos personalizados em Views Android

Olá povo,

Estou escrevendo mais uma matéria para Java Magazine em parceria com dois colegas meus. No exemplo apresentado no artigo, está sendo usado um componente criado por nós. Para criar um componente personalizado, basta criar uma classe que estenda um componente existente (EditText, Button, TextView, etc.). Porém, se quisermos criar um componente totalmente novo, devemos estender de View, que é classe mãe de todos os componentes do Android, e implementar o método draw que vai desenhar o componente na tela.
Neste ponto, surgiu a seguinte dúvida: como adicionar uma View personalizada em um arquivo de layout e passar atributos personalizados para a View? Bem simples, como quase tudo no Android. Abra o arquivos res/values/strings.xml e deixe-o conforme abaixo:
<resources>
<string name="hello">
debug is on the table
</string>
<string name="app_name">
nglauber
</string>

<declare-styleable name="MeuComponente">
<attr name="texto" format="string"/>
</declare-styleable>
</resources>

Notem que no código acima, além de dois textos com os ids hello e app_name, declaramos um estilo chamado MeuComponente, e dentro dele um atributo chamado "texto" do tipo string. Pronto! Agora é só usar esse atributo tanto na classe que herda de View, quanto no XML. Primeiro vamos ver como fica na classe que herda de view.
public class MeuComponente extends View {

private String text;
private Paint paint;

public MeuComponente(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
paint = new Paint();
paint.setColor(Color.RED);

TypedArray a = ctx.obtainStyledAttributes(
attrs, R.styleable.MeuComponente);

String s = a.getString(
R.styleable.MeuComponente_texto);

if (s != null) {
text = s.toString();
}
a.recycle();
}

@Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.WHITE);

if (text != null){
canvas.drawText(text, 0, 20, paint);
}
}
}


A classe não é nada complicada, mas temos que observar alguns detalhes. Ao criar uma classe que herda de View, você deve implementar um dos 3 construtores possíveis: View(Context), View(Context, AttributeSet) e View(Context, AttributeSet, int). O primeiro recebe apenas o contexto de onde o componente está sendo criado. O segundo recebe o contexto e um conjunto de atributos que são passados para o componente quando ele está inserido em um arquivo de layout. O último método recebe, além dos dois parâmetros citados anteriormente, recebe um estilo que o componente pode receber.
No exemplo acima, usamos a segunda opção, que recebe o contexto e a lista de atributos definidos no XML de layout. Com o método obtainStyledAttributes obtemos um array com os atributos de um determinados estilo, no nosso caso, passamos o estilos que criamos no arquivos strings.xml.
Na linha seguinte, tentamos obter o valor do atributo "texto", caso tenha sido atribuído algum valor a essa propriedade, o valor é passado para o atributo texto. A chamada do método recycle é obrigatória após terminar o uso do array de propriedades. Por fim, no método draw o texto definido no atributo é desenhado na tela.
Agora veja como adicionar um componente personalizado em um arquivo de layout e como utilizar a nossa própria propriedade.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ngvl="http://schemas.android.com/apk/res/ngvl.android.teste"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ngvl.android.teste.MeuComponente
ngvl:text="@string/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

Vamos analisar os detalhes importantes desse arquivo. No LinearLayout, temos uma propriedade chamada xmlns:ngvl, ela indica onde estão declaras as propriedades que começarem com "ngvl". Outro ponto é que, na declaração do componente, deve constar o nome pacote + nome classe. No nosso caso, a classe MeuComponente está no pacote ngvl.android.teste. Por fim, adicionamos a propriedade ngvl:text ao nosso componente.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

Referência:
http://jeffreysambells.com/posts/2010/10/28/custom-views-and-layouts-in-android-os-sdk/

sábado, 11 de junho de 2011

Documentação do Java ME no Eclipse MTJ

Olá povo,

Esse post vai pro pessoal da segunda turma do TECDAM, que hoje durante a aula Java ME, me questionaram como anexar a documentação das APIs do Java ME no Eclipse. Como isso não é das coisas que se faz todo dia, não lembrei na hora. Então lá vai...

Ao instalar o SDK do Java ME, além das APIs e ferramentas de desenvolvimento, é criada uma pasta onde encontra-se a documentação do MIDP (Mobile Information Device Profile), do CLDC (Connected Limited Device Configuration) e de outras JSRs (Java Specification Request) importantes do Java ME. Entretanto o Eclipse não consegue "enxergar" essa documentação, sendo assim, vou mostrar como fazer esse ligação.

Estou partindo do presuposto que o Eclipse já está instalado e com o plugin MTJ já configurado. Se não estiver, acesse esse link.

Para que essa documentação fique disponível dentro do Code Assist do Eclipse, acesse o menu Window > Preferences (no Mac, Eclipse > Preferences). Na janela que será exibida, no lado esquerdo, selecione Java ME > Device Management. E no lado direito, selecione ClamshellCldcPhone1 e clique em Edit...
Na tela que será exibida, clique na aba Libraries. Na coluna javadoc, selecione as "..." da API cldc_1.0.jar.
Na tela que será exibida, selecione o local onde encontra-se a documentação do Java ME no seu computador. Aos usuário de Mac, o Java ME SDK é distribuído em um arquivo *.app, sendo assim, para selecionar a documentação você deve copiar o endereço da documentação a partir do Finder (ou do terminal) e colar no campo da tela. No meu caso, estava no seguinte local:/Applications/Java_ME_SDK_3.0.app/Contents/Resources/docs/api/midp-2.0.zip.
Feito isso, clique em Ok. Em seguida, repita os paços para as APIs cldc_1.1.jar, midp_2.0.jar e midp_2.1.jar.
Após isso, já podemos ver o javadoc da documentação da classe dentro do Eclipse.

4br4ç05,
nglauber

sexta-feira, 10 de junho de 2011

Artigo "Android Widgets"

Olá povo,

A revista Web Mobile Magazine edição 36, trás uma matéria bem legal sobre Android AppWidgets. Ela foi escrita por mim e pelos meus colegas Edilson Mendes(Ronaldo!) e Breno Menezes (Cid).

Nesse artigo apresentamos, através de um exemplo completo os AppWidgets, que são pequenas aplicações que rodam na tela principal de aparelhos Android. Durante o texto, são explicados os conceitos relacionados a esse tipo de aplicativo através da criação de um AppWidget funcional.

Infelizmente, desde a edição 35, a Devmedia (editora da revista) só está distribuindo essa revista em formato digital, não sendo possível de nenhuma forma adquirir a revista impressa.

Espero que vocês gostem.

4br4ç05,
nglauber

segunda-feira, 6 de junho de 2011

Curso de Android em Recife

Olá povo,

Dia 14 de junho começa a sétima turma de Google Android da Especializa Treinamentos. O treinamento tem carga horária de 40 horas e as aulas serão ministradas por mim, as terças e quintas, das 19:00 as 22:00.

No curso, estudaremos a revolucionária plataforma para smartphones e tablets da Google e como desenvolver aplicativos para ela. Os conceitos de Activities, Broadcast Receivers, ContentProviders e Services serão aplicados na prática. Veremos como armazenar informações no banco de dados do dispositivo com SQLite. Comunicação com WebServices e com o GoogleMaps. Envio de SMS. E muito mais.

Quem tiver interesse, pode entrar na página do curso para obter mais informações e fazer sua inscrição. Corram que ainda dá tempo!

4br4ç05,
nglauber

quinta-feira, 2 de junho de 2011

Copiando um banco de dados SQLite no Android

Olá povo,

As pessoas constantemente me perguntam como criar um banco de dados SQLite no Android que já venha com alguns registros. No meu post sobre SQLite no Android eu recomendo que isso seja feito na classe SQLiteOpenHelper através de um arquivo XML (por exemplo) por ser uma forma padronizada e centralizada para manutenção dos BDs no Android.
Entretanto algumas pessoas querem mesmo é copiar um banco de dados já prontinho pra aplicação, e que diga-se de passagem pode ser uma boa opção. Desta forma, segue abaixo o código que faz isso:
class Util {
public static void copiaBanco(
Context ctx, String nomeDB){

// Cria o banco vazio
SQLiteDatabase db = ctx.openOrCreateDatabase(
nomeDB, Context.MODE_WORLD_WRITEABLE, null);

db.close();

try {
// Abre o arquivo que deve estar na pasta assets
InputStream is = ctx.getAssets().open(nomeDB);
// Abre o arquivo do banco vazio ele fica em:
// /data/data/nome.do.pacote.da.app/databases
FileOutputStream fos = new FileOutputStream(
ctx.getDatabasePath(nomeDB));

// Copia byte a byte o arquivo do assets para
// o aparelho/emulador
byte[] buffer = new byte[1024];
int read;
while ((read = is.read(buffer)) > 0){
fos.write(buffer, 0, read);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

Notem alguns detalhes interessantes aqui. O método openOrCreateDatabase além de criar a pasta "databases" dentro do diretório onde os bancos de dados devem ficar, ele cria também o arquivo de banco de dados, só que vazio. Uma vez com a estrutura pronta, basta abrir o banco de dados que está com as informações (que no meu exemplo coloquei na pasta "assets") e sobrepor o banco vazio.
Para chamar o método de uma Activity e visualizar uma informação do banco fazemos conforme abaixo:
// carroDB é nome do banco e deve
// ser o nome do arquivo do banco
Util.copiaBanco(this, "carroDB");

SQLiteDatabase db = openOrCreateDatabase(
"dbCarro", MODE_WORLD_WRITEABLE, null);

Cursor c = db.query("carro", null, null, null, null, null, null);
while (c.moveToNext()){
Log.d("NGVL", "Carro: "+ c.getString(1));
}
c.close();
db.close();


4br4ç05,
nglauber