terça-feira, 1 de dezembro de 2009

Acrobat Reader "reads" pra você

Olá povo,

O formato de documentos PDF já está mais do que consolidado no mercado. Artigos, apresentações, e-books, e muitos outros tipos de documentos estão na internet disponibilizados nesse formato.

Para ler esse tipo de documento precisamos de um programinha para interpretá-lo. O mais conhecido deles é o Adobe Acrobat Reader (apesar de haver outros como o Foxit). E ao ler um e-book na minha casa descobri uma funcionalidade desse programinha que eu acho que poucas pessoas conhecem: Ler em voz alta.

Isso mesmo. Essa funcionalidade faz com que o Reader leia o documento pra você :) Para isso, vá ao menu Visualizar / Ler em voz alta. Pronto! Agora só aumentar o som e acompanhar a leitura. Se você achar que ele está lendo muito rápido, vá no menu Editar / Preferências e depois escolha a opção Leitura. Lá você pode diminuir o número de palavras por minuto para tornar a leitura mais lenta.

Fiz esse teste no Windows XP e com a versão 7.0 do Acrobat Reader. Infelizmente essa funcionalidade é apenas para documentos em inglês, em português ele ainda ficou devendo.

4br4ç05,
nglauber

quarta-feira, 25 de novembro de 2009

Palestra sobre Android na Faculdade Guararapes

Olá povo,

Nessa sexta-feira (27/11) às 20:00h estarei na Faculdade dos Guararapes ministrando uma palestra sobre a plataforma Android. Na ocasião, também estaremos divulgando o curso de Android que ministrarei em parceria com a Especializa Treinamentos.

A palestra será aberta ao público externo.

4br4ç05,
nglauber

terça-feira, 17 de novembro de 2009

Android: Personalizando os recursos

Olá povo,

Hoje vou falar um poquinho sobre a parte de recursos do Android. Todos os recursos que usamos na nossa aplicação (strings, imagens, cores, layouts, etc.) ficam dentro da pasta "res". Só que cada subdiretório tem um objetivo específico:

  • res/anim: arquivos XML de animações frame-a-frame ou de efeito;
  • res/drawable: arquivos de imagens da aplicação com as extensões .png, .9.png, .jpg;
  • res/layout: arquivos XML com os layouts de telas;
  • res/values: arquivos XML que podem ter os seguintes elementos: string-array (lista de valores), color (definição de cores), string (texto simples) ou style (estilos aplicados à views).
No entanto nós podemos criar variações desses diretórios dependendo da configuração do aparelho. Por exemplo, podemos criar textos em português e inglês para nossa aplicação. Assim, quando o usuário mudar o idioma o texto da aplicação mudaria automaticamente. No Android, basta fazermos o seguinte:

res/values-pt
res/values-en

Pronto! Coloque os textos em português no primeiro diretório e os em inglês no segundo.

Outra possibilidade é criarmos arquivos de layout para resoluções de tela diferentes, seguindo o mesmo princípio:

res/layout-land
res/layout-854x480
res/layout-land-480x320

Se quisermos fazer um layout retrato (portrait) e outro paisagem (landscape para uma tela podemos utilizar a opção "land". Para ter layouts para diferentes tamanhos de tela, basta colocar a resolução da tela passando a [altura]x[largura]. Já na terceira estamos combinando a opção de landscape + tamanho da tela.

E se quisermos que algo mude na nossa aplicação dependendo da operadora? Existe uma numeração que identifica a operadora. Essa numeração é a MCC (Mobile Country Code) e o MNC (Mobile Network Code). E o Android nos permite identificar isso para que nossa aplicação possa ter um comportamento diferente. Para descobrir o MCC e o MNC da operadora podemos utilizar esse código:

TelephonyManager tel = (TelephonyManager) 
  getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = tel.getSimOperator();

if (networkOperator != null) {

  int mcc = Integer.parseInt(
    networkOperator.substring(0, 3));

  int mnc = Integer.parseInt(
    networkOperator.substring(3));
}


Com o código da operadora, basta colocar o nas suas pastas mcc123-mnc123 (substitua o 123 pelo número encontrado no código acima).

res/drawable-mcc724-mnc31
res/values-mcc724-mnc02

Na pasta acima 724 é o MCC do Brazil e os valores 31 e 02 são da Oi e TIM respectivamente.

Além dessas opções, temos muitas outras que podemos achar aqui.

Resolvi também colocar um screen-shot só da pasta "res" do projeto que eu estou trabalhando.



Mais informações aqui.

4br54ç05,
nglauber

domingo, 8 de novembro de 2009

Android 2.0 - Lista de Contatos

Olá povo,

Como nós estamos acompanhando, as versões do Android estão saindo com uma velocidade enorme. Mal eu tinha instalado a versão 1.6 e a versão 2.0 tinha saido do forno. Como já tem se tornado comum também, novidades legais têm sido apresentadas. Uma mudança muito forte feita nessa versão foi na parte de contatos, que permite que você adicione várias informações sobre seu contato como e-mail, IM, empresa, notas, etc. No entanto, essas novidades trazem modificações na API. Sendo assim, segue abaixo um código que lista os contatos cadastrados e seus respectivos IDs, e ao clicarmos em um contato da lista, são exibidos os telefones associados.


public class ExemploListaContatos extends ListActivity {

private ListAdapter adaptador;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Obtém os contatos cadastrados utilizando
// a nova classe ContactsContract
Cursor c = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
startManagingCursor(c);

String[] colunas = {
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts._ID};

int[] campos = {
android.R.id.text1,
android.R.id.text2};

adaptador = new SimpleCursorAdapter(
this, android.R.layout.two_line_list_item,
c, colunas, campos);
setListAdapter(adaptador);
}

protected void onListItemClick(ListView l,
View v, int position, long id) {

super.onListItemClick(l, v, position, id);

// Obtém os campos da linha do contato
Cursor c = (Cursor)adaptador.getItem(position);

int contactId = c.getInt(
c.getColumnIndex(ContactsContract.Contacts._ID));

// Obtém os telefones do contato
Cursor phones = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,
null, null);

StringBuffer sb = new StringBuffer();
int tipo, resTipo;
String numero;

if (phones.getCount() > 0){
phones.moveToFirst();

// Percorre o cursor para obter
// Os telefones e o tipo
while (!phones.isAfterLast()){

tipo = phones.getInt(
phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.TYPE));

numero = phones.getString(
phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));

resTipo = ContactsContract.CommonDataKinds.Phone.getTypeLabelResource(tipo);
sb.append(getString(resTipo) +": "+ numero +"\n");
phones.moveToNext();
}

} else {
sb.append("Nenhum número cadastrado.");
}
Toast.makeText(this, sb.toString(), Toast.LENGTH_SHORT).show();
}
}


Agora é só adicionar a permissão de leitura dos contatos ao AndroidManifest.xml.


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


4br4ç05,
nglauber

terça-feira, 3 de novembro de 2009

Palestra sobre Android em Recife

Olá povo,

Amanhã (04/11) às 16:30 estarei palestrando sobre Android na III Semana de Informática da Universidade Católica de Pernambuco. Na palestra falarei sobre a história do Android, o porque da sua criação, quais as vantagens (e desvantagens) de se adotar esse sistema, alguns exemplos de código Android, etc. Também divulgaremos o curso de Android que ministrarei na Especializa Treinamentos.

Para mais informações, clique aqui e acesse a página do evento.

4br4ç05,
nglauber

domingo, 25 de outubro de 2009

Intents nativas do Android

Olá povo,

Um grande benefício que a plataforma Android trouxe para nós desenvolvedores foi a capacidade de nos comunicar com aplicações "nativas" através de intenções (Intents).
Sendo assim, resolvi publicar algumas das intents nativas mais comuns do Android. Segue abaixo o código-fonte.


public class ExemploIntents extends ListActivity {

private static final String[] OPCOES = {
"Browser",
"Realizando uma chamada",
"Visualizar contato",
"Todos os contatos",
"Mapa",
"Tocar música",
"SMS",
"Sair"
};

@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
OPCOES);

setListAdapter(adapter);
}

@Override
protected void onListItemClick(ListView l, View v,
int position, long id) {

super.onListItemClick(l, v, position, id);
Uri uri = null;
Intent intent = null;

switch (position) {
// Abrindo uma URL
case 0:
uri = Uri.parse("http://nglauber.blogspot.com");
intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
break;

// Realiza uma chamada
case 1:
uri = Uri.parse("tel:99887766");
intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);
break;

// Visualiza um contato específico
// da lista de contatos
case 2:
uri = Uri.parse(
"content://contacts/people/5");
intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
break;

// Visualiza todos os contatos e permite
// selecionar um através do resultado
// da Activity
case 3:
uri = Uri.parse(
"content://contacts/people/");
intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 0);
break;

// Pesquisa uma posição do mapa
// !Seu AVD deve estar usando Google APIs!
case 4:
uri = Uri.parse(
"geo:0,0?q=Rua+Amelia,Recife");
intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
break;

// Executa uma música do SDcard
case 5:
uri = Uri.parse(
"file:///sdcard/musica.mp3");
intent = new Intent();
intent.setDataAndType(uri,"audio/mp3");
startActivity(intent);
break;

// Abrindo o editor de SMS
case 6:
uri = Uri.parse("sms:12345");
intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra("sms_body", "Corpo do SMS");
startActivity(intent);
break;

default:
finish();
}
}

@Override
protected void onActivityResult(int requestCode,
int resultCode, Intent data) {

super.onActivityResult(requestCode,
resultCode, data);

// Mostra um contato selecionado (case 3)
// em um Toast
if (data == null){
Toast.makeText(this, "Nenhum contato",
Toast.LENGTH_SHORT).show();
} else {
Uri uri = data.getData();
Toast.makeText(this, "Contato: "+ uri,
Toast.LENGTH_SHORT).show();
}
}
}


Vamos explicar o que foi feito acima. Criamos uma lista com várias opções de intents nativas. E quando o usuário clicar em qualquer uma dessas opções, a respectiva Intent será disparada.
Em todos os casos, utilizamos o objeto Uri para criar a Intent. Esse objeto indica um recurso do aparelho queremos acessar, e baseado no protocolo informado, ele irá disparar a atividade desejada.
Em conjunto com a Uri, associamos uma ação que queremos realizar sobre o recurso. Vamos exemplificar como o Android entende a solicitação, usando o caso do Browser: "O usuário está com a intenção (Intent) de visualizar (ACTION_VIEW) o recurso (Uri) http://nglauber.blogspot.com". Nesse momento o Android faz uma busca interna pra saber quem pode tratar essa intenção.

No exemplo de discar um número, utilizamos a ação ACTION_DIAL e o protocolo "tel:" seguido do número do telefone a ser discado. Notem que aqui há uma diferença entre "discar" (ACTION_DIAL) e "chamar" (ACTION_CALL). A primeira opção apenas disca o número não chama. Se quiséssemos chamar o número ao invés de apenas discá-lo, deveríamos utilizar a ACTION_CALL e adicionar a permissão abaixo no manifest:

<uses-permission name="android.permission.CALL_PHONE"/>

Na lista de contatos temos dois casos. O primeiro visualiza um contato utilizando o protocolo "content://" - que identifica um ContentProvider - passando o caminho do provider de contatos. Já o segundo caso está usando a ação ACTION_PICK para recuperar a Uri de um contato específico. Para tal, é utilizado o método startActivityForResult que iniciará a tela de contatos permitindo que selecionemos um da lista. Para tratar o retorno da atividade, utilizamos o método onActivityResult.
Para abrir a aplicação de Mapas em um local específico utilizamos o protocolo "geo:" seguido do do endereço do local desejado. É possível passar também a longitude e latitude (geo:lat,long).
Para executarmos o MP3 Player padrão do aparelho passando uma música, utilizamos o método setDataAndType e passamos a Uri com o protocolo "file://" e informando o MIME type do arquivo.
Mais informações aqui.

4br4ç05,
nglauber

terça-feira, 13 de outubro de 2009

Hospedagem grátis: Java + MySql

Olá povo,

Aqui vai uma boa dica para quem precisa hospedar, para testes, sites em Java . O site EATJ permite que você hospede sua aplicação de graça com as seguintes configurações:
  • Java 1.5 ou 1.6;
  • Servidor Tomcat 5.5 ou 6.0 (permitindo o acesso ao Manager e o Administrator);
  • Banco de dados MySql 4.1 ou 5.0 com acesso através do PHPMyAdmin.
Tudo isso através de uma interface bem simples. Para fazer o deploy basta fazer o upload do seu arquivo WAR (de até 50MB para conta gratuita) e pronto! Sua aplicação já está no ar.

A URL fica no seguinte formato: http://seuusuario.s156.eatj.com/
Um ponto negativo que eu verifiquei, é que as contas gratuitas tem seus servidores reiniciados a cada 6 horas. Mas como utilizei para testes, é uma boa opção.

4br4ç05,
nglauber

terça-feira, 6 de outubro de 2009

Android Widgets

Olá povo,

Todo mundo vem reparando a grande quantidade de inovação trazida pelo Android para as aplicações mobile. Entre essas inovações, temos a possibilidade de criar pequenas aplicações que podem ser adicionadas na tela principal do aparelho. A essas aplicações chamamos de widgets.

Procurando um pouco na internet você acha esse exemplo no site do próprio Android.

Porém, ele não é muito trivial nem didático para quem tá querendo aprender os conceitos sobre o assunto. Sendo assim, vou apresentar um exemplo bem simples de widget baseado no tutorial em inglês de Norbert Möhring)

Vamos lá! Crie um novo projeto Android no Eclipse. Dê um nome ao seu projeto; na opção "Build target" coloque a versão 1.5 (os widgets surgiram a partir dessa versão); o pacote que coloquei foi ngvl.android.widget; desmarque a caixa para criar uma atividade, pois não precisamos.

Altere o arquivo res/layout/main.xml, para que fique da seguinte forma:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:orientation="vertical"
android:background="@drawable/widget_bg_normal"
android:layout_gravity="center"
android:layout_height="wrap_content">

<TextView android:id="@+id/widget_textview"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal|center"
android:layout_marginTop="5dip"
android:padding="10dip"
android:textColor="@android:color/black"/>
</LinearLayout>


Notem que eu utilizei uma imagem como background do LinearLayout. Clique aqui para baixá-la, e depois coloque na pasta res/drawable do seu projeto. Essa imagem está no formato Nine Patch. O Android dá suporte a esse padrão de imagem que estabelece como uma imagem deve ser redimensionada. Mais informações sobre esse formato clique aqui.

O nosso widget não será uma Activity, mas sim um BroadcastReceiver que será notificado quando nosso widget receber atualizações (que serão configuradas mais na frente).


package ngvl.android.widget;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
import android.widget.RemoteViews;

public class MeuWidget extends AppWidgetProvider {

private Handler handler;
private MyTime time;

@Override
public void onUpdate(Context ctx,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {

time = new MyTime(ctx, appWidgetManager)
handler = new Handler();
handler.post(time);
}

private class MyTime implements Runnable {
RemoteViews remoteViews;
AppWidgetManager appWidgetManager;
ComponentName thisWidget;
DateFormat dateFormat;

public MyTime(Context context,
AppWidgetManager appWidgetManager) {

this.appWidgetManager = appWidgetManager;
this.remoteViews = new RemoteViews(
context.getPackageName(),
R.layout.main);

this.thisWidget = new ComponentName(
context, MeuWidget.class);

this.dateFormat = SimpleDateFormat.
getTimeInstance(
SimpleDateFormat.MEDIUM,
Locale.getDefault());
}

@Override
public void run() {
remoteViews.setTextViewText(
R.id.widget_textview,
"Time "+ dateFormat.format(
new Date()));
appWidgetManager.updateAppWidget(
thisWidget, remoteViews);
handler.postDelayed(this, 1000);
}
}
}


Como podemos ver, nossa classe herda de android.appwidget.AppWidgetProvider que por sua vez, herda de android.content.BroadcastReceiver e devemos implementar o método onUpdate. Esse método é chamado toda vez o o widget precisar ser atualizado. Nesse método estou utilizando um Handler para atualizar o o conteúdo com a data/hora atual.
A classe MyTime recebe uma referência para o contexto da aplicação e um objeto do tipo AppWidgetManager. É a partir dele que obtemos as refências para as views utilizadas no layout. No método run dessa classe atualizamos o TextView com a data/hora atuais.

Agora vamos criar uma pasta chamada xml dentro da pasta res e criar o arquivo meuwidget_provider.xml com o seguinte conteúdo:


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="72dip"
android:initialLayout="@layout/main"
android:updatePeriodMillis="1000"/>


Nesse arquivo, definimos o tamanho mínimo do nosso componente, bem como o layout e o intervalo de atualizações do mesmo.

Agora, vamos atualizar o nosso AndroidManifest.xml:


<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="ngvl.android.widget"
android:versionCode="1"
android:versionName="1.0">

<application
android:icon="@drawable/icon"
android:label="@string/app_name">

<receiver
android:label="@string/app_name"
android:name=".MeuWidget">

<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/meuwidget_provider"
/>
</receiver>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>


Pronto! Agora mande rodar a aplicação. Depois que a aplicaçao for instalada (olhe no Console view ou no Logcat), vá até a Home Screen (a tela onde fica o relógio) e pressione Menu / Add / Widget e voilà! Nosso widget apareceu na lista. Selecione-o na lista e ele ficará na sua tela principal. Para removê-lo, basta pressioná-lo e arrasta-lo para lixeira (na parte inferior).



Espero que tenha sido útil. Qualquer dúvida, entrem em contato.

4br4ç05,
nglauber

quarta-feira, 16 de setembro de 2009

Curso de Android em Recife


Olá povo,

Estou lançando em parceria com a Especializa Treinamentos o curso de desenvolvimento de aplicações para a plataforma Android. As aulas serão ministradas por mim e o conteúdo do curso será baseado no livro do Ricardo Lecheta.
No curso, estudaremos tudo sobre essa revolucionária plataforma de desenvolvimento para smartphones. Os conceitos de Activities, Handles, ContentProvider e Services serão aplicados na prática. Vamos ver como armazenar informações no banco de dados do celular com SQLite. Comunicação com WebServices e com o GoogleMaps. Envio de SMS. E muito mais.

Quem tiver interesse, entre em contato com a Especializa ou comigo através do meu e-mail.

4br4ç05,
nglauber

terça-feira, 15 de setembro de 2009

Google Maps com Android 1.5

Olá povo,

Aqui vai um passo-a-passo pra quem for utilizar a API do GoogleMaps no Android.

Pré requisitos:
Android SDK 1.5
Eclipse Ganymede
Plugin ADT (Android Developer Tools) 0.9.1
JDK 1.5 ou superior com o diretório bin na variável de ambiente PATH.

Passo 1:
Obtenha o código do certificado digital que é usado para assinar as aplicações. Toda vez que o Eclipse executa uma aplicação Android ele gera um APK (Android PacKage) assinado. Essa assinatura é gerada a partir de um certificado digital de testes chamado debug.keystore que fica na pasta home do seu usuário. No meu caso, no Windows XP o caminho foi: C:\Documents and Settings\ngvl\.android\debug.keystore.

Para obter o código do certificado digital, utilizaremos a ferramenta keytool que vem com o JDK. Abra um terminal/prompt de comando e digite:

keytool -list -alias androiddebugkey -keystore "caminho do seu debug.keystore“

Será solicitado usuário e senha, digite "android" para ambos. Será gerado um resultado semelhante a esse:

androiddebugkey, 30/08/2009, PrivateKeyEntry,
Certificate fingerprint (MD5): AB:DB:39:2F:30:FC:7E:28:2B:7F:BF:54:7B:44:DF:C4


Passo 2:
Acesse o site http://code.google.com/android/maps-api-signup.html (é necessário ter uma conta do Google) digite o Certificate fingerprint no local indicado. Clique em "Generate API key". Será gerada uma chave parecida como a de baixo:

0CjoPthXnVvE9Uz3TW1d-ng82vlrdeTVBda4VPw


Pronto! agora você tem uma chave para o GoogleMaps.

Passo 3:
Vamos configurar nossa aplicação. Primeira coisa que devemos fazer é configurar na nossa aplicação o build target como "GoogleAPIs" e criar um AVD para esse target.

Depois, devemos colocar essa linha no arquivo AndroidManifest.xml, dentro da tag <application>


<uses-library
android:name="com.google.android.maps"/>


E essas duas fora da tag <application>, mas dentro da tag <manifest>.


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


Agora só falta nós criarmos a classe que mostrará o mapa na tela:


import android.os.Bundle;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class MapaSimples extends MapActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MapView mapView = new MapView(this,
"0CjoPthXnVvE9Uz3TW1d-ng82vlrdeTVBda4VPw");
setContentView(mapView);
}

protected boolean isRouteDisplayed() {
return false;
}
}


Se rodarmos nossa aplicação teremos nossa aplicação exibindo um mapa na tela do aparelho.

Depois pretendo editar esse post ou adicionar novos mostrando como manipular o mapa no Android.

4br4ç05,
nglauber

quinta-feira, 10 de setembro de 2009

domingo, 30 de agosto de 2009

Utilizando Banco de Dados SQLite no Android

Olá povo,

Entre as muitas coisas legais que o Android trouxe, o suporte nativo ao banco de dados SQLite foi uma das mais importantes, comercialmente falando. Com ele podemos utilizar os bons e famosos comandos SQL (SELECT, INSERT, UPDATE, DELETE, etc) no mundo mobile. Quem programou em J2ME sabe o quão complicado é implementar um repositório "digno" que suporte uma boa quantidade de registros nessa plataforma (não me falem de RMS... :)

Nesse POST, vou colocar trechos de código que utilizam esses conceitos e tentarei explicá-los. A parte da interface gráfica, vou explicar apenas o que for relevante para o uso com banco de dados.

Para criarmos um banco de dados SQLite, podemos:
  • utilizar a ferramenta SQLite Expert Personal e colocar o arquivo do banco de dados na pasta /data/data/pacote_da_minha_aplicacao/databases;
  • ou via código, utilizando uma classe "helper" do SQLite.
Utilizaremos a segunda abordagem, pois na vida real não queremos colocar esse arquivo manualmente no aparelho. Pois, caso seja necessária uma atualização, teremos que atualizar N aparelhos.

Mãos à obra! Vamos criar uma classe que herda de android.database.sqlite.SQLiteOpenHelper. Ela será responsável por criar o banco quando a aplicação for instalada (método onCreate) e atualizá-lo para novas versões da aplicação (método onUpgrade).

public class SQLiteHelper extends SQLiteOpenHelper {
  private String scriptCreate;
  private String scriptDelete;

  public SQLiteHelper(Context ctx, String nomeBd, 
      int versaoBanco, String scriptCreate, 
      String scriptDelete) {

      super(ctx, nomeBd, null, versaoBanco);
      this.scriptCreate = scriptCreate;
      this.scriptDelete = scriptDelete;
  }

  public void onCreate(SQLiteDatabase db) {
      db.execSQL(scriptCreate);
  }

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

      db.execSQL(scriptDelete);
      onCreate(db);
  }
}


Agora vamos criar a classe que realizará as operações de abrir, selecionar, inserir, atualizar e excluir registos no banco de dados:

public class RepositorioCarro {
  private SQLiteDatabase db;
  private SQLiteHelper dbHelper;
  private static final String SCRIPT_DB_DELETE = 
    "DROP TABLE IF EXISTS carros";
  private static final String SCRIPT_DB_CREATE =
    "create table carros (_id integer primary "+
    "key autoincrement, nome text not null, "+
    "placa text not null, ano text not null);";

  public RepositorioCarro (Context ctx){
    dbHelper = new SQLiteHelper(ctx, "curso", 1, 
      SCRIPT_DB_CREATE, SCRIPT_DB_DELETE);
  }

  private long inserir(Carro c){
    ContentValues cv = new ContentValues();
    cv.put("nome", c.getNome())
    cv.put("placa", c.getPlaca());
    cv.put("ano", c.getAno());
    db = dbHelper.getWritableDatabase();
    long id = db.insert("carros", null, cv);
    db.close();
    return id;
  }

  private long atualizar(Carro c){
    ContentValues cv = new ContentValues();
    cv.put("nome", c.getNome());
    cv.put("placa", c.getPlaca());
    cv.put("ano", c.getAno());

    db = dbHelper.getWritableDatabase();
    long rows = db.update("carros", cv, "_id = ?", 
       new String[]{ String.valueOf(c.getId())});
    db.close();
    return rows; // qtde. de linhas afetadas 
  } 

  public int excluir(int id){
    db = dbHelper.getWritableDatabase();
    int rows = db.delete("carros", "_id = ?", 
      new String[]{ String.valueOf(id) });
    return rows; // qtde. de linhas afetadas
  }

  public List<Carro> buscarCarroPorNome(String nome){
    List<Carro> lista = new ArrayList<Carro>();

    String[] columns = new String[]{
       "_id", "nome", "placa", "ano"};
    String[] args = new String[]{nome+"%"};

    db = dbHelper.getWritableDatabase();
    Cursor c = db.query("carros", columns, 
       "nome like ?", args, null, null, "nome");

    c.moveToFirst();
    while(!c.isAfterLast()){
      Carro carro = fillCarro(c);
      lista.add(carro);
      c.moveToNext();
    }
    c.close();
    db.close();
    return lista;
  }

  private Carro fillCarro(Cursor c) {
    Carro carro = new Carro();
    carro.setId((int)c.getLong(0));
    carro.setNome(c.getString(1));
    carro.setPlaca(c.getString(2));
    carro.setAno(c.getString(3));
    return carro;
  }
}


Como vocês podem ver, estou usando uma classe Carro também. Não vou listá-la aqui por se tratar de um POJO com os campos: id, nome, placa e ano do veículo.

Na classe acima, o construtor recebe uma referência do contexto da aplicação. Ela é repassada para nossa classe SQLHelper, que por sua vez, se encarregará de criar o nosso banco de dados se ele não existir. A classe android.database.sqlite.SQLiteOpenHelper (superclasse do nosso helper) tem o método getWritableDatabase() que retorna uma instância da classe android.database.sqlite.SQLiteDatabase, que será nosso canal de comunicação com o banco de dados.

As operações de inserir e atualizar utilizam: uma string que representa o nome da tabela no banco de dados; e um objeto da classe ContentValues, que nada mais é do que um conjunto par/valor com os parâmetros do comando SQL. Mas notem que o método update recebe ainda uma String que representa a cláusula WHERE do comando UPDATE (do SQL), bem como os parâmetros da mesma, que estão mapeados via caracter '?'.
"Por tabela", o método excluir vocês já devem ter entendido :)

O último método é o buscarCarroPorNome(String). Toda consulta SQL, retorna um objeto do tipo Cursor. Ele é bem parecido com o ResultSet do JDBC. Os parâmetros do método query são, respectivamente: String nome da tabela, String[] colunasDaTabela, String clausulaWhere, String[] paramsWhere, String groupBy, String having, String orderBy.

Para exibir os dados, precisaremos criar um Adapter, que converterá um Carro em uma linha de uma lista. Crie o arquivo de layout list_item.xml e coloque-o na pasta res/layout:

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

<LinearLayout android:layout_height="wrap_content" 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="fill_parent" 
android:id="@+id/carroRow">

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtNome" 
android:layout_weight="1" 
android:textSize="20dp"/>

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtAno" 
android:layout_marginRight="10px"/>

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtPlaca"  
android:layout_marginRight="10px"/>

</LinearLayout>


E agora, vamos criar nosso adapter.

public class CarroListAdapter extends BaseAdapter {
  private Context context;
  private List<Carro> lista;

  public CarroListAdapter(Context ctx, 
    List<Carro> carros) {

    context = ctx;
    lista = carros;
  }

  public int getCount() {
    return lista.size(); 
  }

  public Object getItem(int arg0) {
    return lista.get(arg0);
  }

  public long getItemId(int arg0) {
    return lista.get(arg0).getId();
  }

  public View getView(int pos, View convertView, 
    ViewGroup parent) {

    Carro c = lista.get(pos);
    LayoutInflater inflater = 
      (LayoutInflater)context.getSystemService(
        Context.LAYOUT_INFLATER_SERVICE);

    View view = inflater.inflate(
      R.layout.list_item, null);

    ((TextView)view.findViewById(R.id.txtNome)).
      setText(c.getNome());

    ((TextView)view.findViewById(R.id.txtAno)).
      setText(c.getAno());

    ((TextView)view.findViewById(R.id.txtPlaca)).
      setText(c.getPlaca());

    return view;
  }
}


Crie uma classe que herde de ListActivity e no onCreate, digite o seguinte código:
RepositorioCarro carrosDB = new RepositorCarro(this);
List<Carro>lista = carrosDB.buscarCarroPorNome("");
setAdapter(new CarroListAdapter(this, lista));

Para utilizar os demais métodos é facinho, não é? Basta criar objetos do tipo Carro, no qual os valores serão preenchidos por widgets (componentes de UI) e passá-los como parâmetros para os métodos do RepositorioCarro.

Espero que tenham entendido e gostado :)

Editado em 03/11/2011
Sempre que fizermos uma operação no banco de dados é importante que encerremos a conexão com ele. Tinha faltado isso nesse post, e agora lembrei de corrirgir.

4br4ç05
nglauber

sábado, 8 de agosto de 2009

JUnit no Eclipse


Olá povo,

Voltando a postar aqui no blog, vou mostrar rapidinho como usar o JUnit no Eclipse. Para quem não conhece, o JUnit é uma ferramenta freee de automação de testes unitários para Java. Esses testes unitários, visam validar o código que foi desenvolvido.

Vamos utilizar uma classe Calculadora abaixo:


public class Calculadora {

public int soma(int a, int b){
return a + b;
}

public int multiplica(int x, int y){
return x * y;
}
}


Seria muito simples testarmos essa classe não é? Para testar UMA classe sim, mas quando são CENTENAS?
Sendo assim, a idéia do JUnit é validar se os resultados de operações estão sendo satisfeitas, evitando que modificações acidentais causem um mal funcionamento no sistema.

Para utilizar o JUnit no Eclipse é bem simples. A versão Ganymede já vem com um plugin integrado. Então, vamos fazer o teste pra essa classe? No Eclipse, selecione File | New... | JUnit Test Case. Informe o nome da classe de teste, por convenção, utiliza-se o nome da classe mais a palavra "Test" no final. Então, colocaremos o nome de CalculadoraTest. Selecione a classe que estamos testando, e clique em finish.

Vamos deixar nossa classe como abaixo:


import junit.framework.TestCase;

public class CalculadoraTest2 extends TestCase {

Calculadora calc = new Calculadora();

public void testSoma() {
assertEquals(3, calc.soma(1, 2));
}

public void testMultiplica() {
assertEquals(12, calc.multiplica(4,3));
}
}


Agora vamos rodar nossos testes. Clique com o botão direito no projeto, selecione "Run as... | JUnit Test". Será exibida uma janela com o resultado dos testes. No nosso caso, os dois testes devem passar.

Como podemos notar, na classe de testes, estamos usando a classe junit.framework.TestCase que é do JUnit. A importação do JAR do JUnit é feito pelo próprio Eclipse.

Isso pode parecer muito trivial, mas imagine se acidentalmente, você muda o sinal do método multiplica? Se isso acontecer e o teste for executado novamente, você verificará que o teste do método de multipicação falhará. Alguns projetos reias realizam a execução dos testes junto com o build, para garantir que a versão gerada está funcionando corretamente.

É isso pessoal, vou tentar publicar mais posts como esse. Simples e pequenos :)

Mais informações sobre o JUnit clique aqui e acesse o site do produto.

4br4ç05,
nglauber

quarta-feira, 15 de julho de 2009

Ausência

Olá povo,

Esse post é só pra dar uma satisfação para quem acessa o blog. Estou sem postar desde Maio porque estou no mestrado e ele está sugando toda minha vida :)

No entanto, venho informar que a partir de agosto virão muitas novidades aqui no blog, nas quais, a maioria será sobre Engenharia de Software, Android e padrões de projeto e arquiteturais.

Meio estranho a combinação? Vou tentar provar que não.

Esperem, e em breve teremos novidades por aqui.

4br4ç05,
nglauber

quarta-feira, 6 de maio de 2009

Impressora 3D

Olá povo,

Estava lembrando no trabalho hoje de um conceito que eu já estava até esquecido e que muita gente não conhece. As impressoras 3D. Isso mesmo...
São impressoras que "imprimem" objetos tridimensionais a partir de um arquivo criado em uma ferramenta CAD. Vocês sabiam que já existem impressoras 3D pela "bagatela" de U$15k?

Bem, segue abaixo um vídeo de uma impressora 3D profissional.




4br4ç05,
nglauber

quinta-feira, 30 de abril de 2009

Spam e Pulhas virtuais


Olá povo,

Quem me conhece pessoalmente sabe que eu sou um cara bem chato e que me irrito facilmente. Uma das coisas que me deixam irritado são pessoas que enviam aquelas mensagens do tipo: "Passem isso para o maior número de pessoas que você puder".

Hoje mesmo, um ex-aluno me mandou um desses, dei fiz uma pesquisa e... Bingo! Lá estava mais uma pulha virtual repassada para dezenas de pessoas.

Para ajudar aqueles que adoram enviar esse tipo mensagem, segue abaixo o link de um site que tem uma enorme coleção desses tipos de e-mail. E, por favor, não mandem pra mim :)

http://www.quatrocantos.com/

4br4ç05,
nglauber

sexta-feira, 24 de abril de 2009

Instalando uma aplicação Android no G1 - A revanche

Olá povo,

Depois que escreví o post anterior, descobri uma maneira um pouco mais fácil de instalar aplicações Android no G1.

Após conectar o celular na USB do computador e instalar os drivers, basta clicar com o botão direito no seu projeto do Eclipse, Run as Android Application e... Pronto! Se o emulador estiver fechado, a aplicação será executada no aparelho.

Simples não? Palmas para o Google mais uma vez...

4br4ç05,
nglauber

quarta-feira, 15 de abril de 2009

Instalando uma aplicação Android no G1


Olá povo,

Nesse post, vou mostrar como instalar uma aplicação Android no G1 da HTC.
Partam das seguintes premissas:
- Estou usando Eclipse com o plugin do Android
- O Android SDK está instalado e seu caminho está no PATH do Windos
- JAVA_HOME está configurado e pasta bin também está no PATH do windows.

Vamos lá.

1) Baixar os drivers do G1
Faça o download dos drivers do aparelho em:
http://dl.google.com/android/android_usb_windows.zip
Descompacte o arquivo em algum lugar da sua máquina.

2) Conecte o aparelho ao PC
O windows detectará o aparelho, selecione o local onde você extraiu os drivers para instalar o telefone no PC.

3) Gerar o APK
No Eclipse, clique com o botão direito no projeto, selecione "Android Tools | Export Unsigned Application Package".
Selecione o local onde deseja salvar o arquivo APK

4) Gerar a chave (esse passo só é preciso fazer uma vez)
No prompt de comando, digite o seguinte comando:

keytool -genkey -v -keystore "C:\Documents and Settings\[seu usuário]\Local Settings\Application Data\Android\debug.keystore" -alias meuAlias -keyalg RSA -validity 10000

Se for solicitado, uma senha, digite: android.

Ele vai fazer várias perguntas para gerar a chave. Responda cada uma delas.
keytool é uma ferramenta do Java SDK (está na pasta bin).

5) Assinar a aplicação
jarsigner -verbose -keystore "C:\Documents and Settings\[seu usuário]\Local Settings\Application Data\Android\debug.keystore" MinhaApp.apk meuAlias

jarsigner é uma ferramenta do Java SDK (está na pasta bin).

6) Instalar a aplicação
Se você não estiver com o emulador aberto, digite:
adb install -r MinhaApp.apk

Caso esteja com o emulador aberto, você precisará saber o serial de qual dos dispositivos você quer instalar (emulador ou aparelho). Digite o seguinte comando para listar os seriais:
adb devices

E depois só é adicionar o serial no comando:
adb install -s SERIAL -r MinhaApp.apk

adb é uma ferramenta do Android SDK (está na pasta tools).

Espero que vocês tenham conseguido :)

4br4ç05,
nglauber

quinta-feira, 2 de abril de 2009

Aprendendo a tocar flauta

Olá povo,

Segue abaixo um vídeo mostrando uma maneira alternativa de aprender a tocar flauta.
Palmas pra Steve Jobs e seu IPhone.



4br4ç05,
nglauber

segunda-feira, 23 de março de 2009

Inovação Pura

Olá povo,

Recebi esse link, e resolvi compatilhar com vocês. A palavra é realmente essa: Inovação.



4br4ç05,
nglauber

P.S.: Créditos de Jorge Fonseca que enviou o link pra mim. Valeu Jorjão.

sábado, 14 de março de 2009

Associando um JPopupMenu em uma JTable

Olá povo,

Esse vai ser um post curto só pra mostrar como associar um menu popup a uma JTable. Eu precisava alterar e excluir um registro selecionado de uma JTable. Vamos ver como fazer isso.

Primeiro crie um objeto JPopupMenu e adicione dois JMenuItem.

JPopupMenu jPopupMenu = new JPopupMenu();
JMenuItem jMenuItemAlterar = new JMenuItem();
jMenuItemAlterar.setText("Alterar");

jMenuItemAlterar.addActionListener(
new java.awt.event.ActionListener() {
// Importe a classe java.awt.event.ActionEvent
public void actionPerformed(ActionEvent e) {
int index = getJTable().getSelectedRow();
// Faça alguma coisa com o item selecionado
}
});

JMenuItem jMenuItemExcluir = new JMenuItem();
jMenuItemExcluir.setText("Excluir");
jMenuItemExcluir.addActionListener(
new java.awt.event.ActionListener() {
// Importe a classe java.awt.event.ActionEvent
public void actionPerformed(ActionEvent e) {
int index = getJTable().getSelectedRow();
// Faça alguma coisa com o item selecionado
}
});

jPopupMenu.add(jMenuItemAlterar);
jPopupMenu.add(jMenuItemExcluir);

Depois só é exibir o menu popup quando o usuário clicar com o botão direito do mouse.


jTable.addMouseListener(
new java.awt.event.MouseAdapter() {
//Importe a classe java.awt.event.MouseEvent
public void mouseClicked(MouseEvent e) {
// Se o botão direito do mouse foi pressionado
if (e.getButton() == MouseEvent.BUTTON3){
// Exibe o popup menu na posição do mouse.
jPopupMenu.show(jTable, e.getX(), e.getY())
}
}
});

E é só isso!

4br4ç05,
nglauber

sábado, 14 de fevereiro de 2009

J2ME e Bluetooth

Olá povo,

Uma das coisas mais legais de se desenvolver aplicações e jogos para celulares é a mobilidade que eles nos proporcionam. Uma tecnologia que está cada vez mais presente nos celulares mais populares no Brasil é o Bluetooth. Por isso, hoje vou falar um pouquinho sobre como desenvolver aplicações com essa tecnologia em Java ME.

Bluetooth é uma tecnologia utilizada para criar pequenas redes sem fio, também chamadas de WPANs (Wireless Personal Area Network). O alcance das redes Bluetooth normalmente é de 10 metros. Não vou filosofar muito sobre a pilha de protocolos, potências, etc. Informações mais detalhadas acesse o site oficial da tecnologia Bluetooth.

Antes de qualquer coisa precisaremos saber se o dispositivo para qual iremos desenvolver suporta a JSR-082. É ela que define o uso da tecnologia Bluetooth com Java ME. Consulte o site do fabricante para obter essa informação. Caso seu telefone não implemente essa JSR, você poderá testar esse exemplo no WTK.

O esquema abaixo demonstra como é estabelecida uma conexão entre dispositivos Bluetooth.



Note que primeiramente devemos estabelecer quem é o servidor. Ele ficará visível para que outros dispositivos o encontrem, aguardará por clientes e os proverá um serviço. O cliente por sua vez terá mais trabalho. Ele primeiro procurará por dispositivos (inquiry) disponíveis em sua área de alcance. Esses podem ser mouses, teclados, antenas ou qualquer outro dispositivo Bluetooth. Após obter essa lista de dispositivos, escolheremos um e verificaremos se ele está disponibilizando o serviço que nos interessa. Se estiver, a conexão entre os dois será estabelecida.

Agora, mãos à obra! Vamos listar agora as classes mais importantes do pacote javax.microedition.bluetooth.*:



LocalDevice - Representa o dispositivo Bluetooth local. É um singleton que pode ser obtido pelo método getLocalDevice(). Com ele podemos, entre outras coisas, dizer se o nosso aparelho será visível para os outros dispositivos, obter o endereço ou o nome do seu dispositivo Bluetooth entre outras.

LocalDevice myPhone = LocalDevice.getLocalDevice();
System.out.println(myPhone.getFriendlyName());

DiscoveryListener - Interface que é notificada quando os eventos de busca de dispositivos e serviços acontecem. Ela tem quatro métodos:

/*
Método chamado quando um dispositivo é econtrado.
Devemos armazenar (em um Vector por exemplo) o
remoteDevice para podermos buscar pelo serviço.
*/
void deviceDiscovered(RemoteDevice remoteDevice,
DeviceClass deviceClass);

/*
Método que é chamado quando a busca de dispositivos
termina.
discType - indica o resultado da busca:
INQUIRY_COMPLETED - Busca terminada com sucesso.
INQUIRY_TERMINATED - Busca cancelada.
INQUIRY_ERROR - Erro na busca.
*/
void inquiryCompleted(int discType);

/*
Método chamado quando a busca do serviço termina.
transID - Id da transação de busca pelo serviço
respCode - indica o resultado da busca:
SERVICE_SEARCH_TERMINATED - Busca cancelada.
SERVICE_SEARCH_ERROR - Erro na busca.
SERVICE_SEARCH_NO_RECORD - Dispositivo não tem o
serviço solicitado.
SERVICE_SEARCH_DEVICE_NOT_REACHABLE - Dispositivo
remoto está fora do alcance.
SERVICE_SEARCH_COMPLETED - Serviço encontrado com
sucesso.
*/
void serviceSearchCompleted(int transID, int respCode);

/*
Método chamando quando o serviço procurado é encontrado.
transID - Id da transação de busca pelo serviço
serviceRecord - informações do serviço encontrado.
*/
void servicesDiscovered(int transID, ServiceRecord[] serviceRecord);

DiscoveryAgent - Obtido através da instância do LocalDevice é o responsável por realizar a busca de dispositivos remotos na área de alcance do dispositivo local e de localizar o serviço no dispositivo remoto.

DiscoveryAgent agent = myPhone.getDiscoveryAgent();
agent.startInquiry(DiscoveryAgent.GIAC,
classeQueImplementeDiscoveryListener);

Com a chamada do startInquiry, cada vez que um dispositivo for encontrado a classe que implementa DiscoveryListener será notificada através do método deviceDiscovered. E quando o inquiry terminar o método inquiryCompleted será chamado.

RemoteDevices - representa um dispositivo Bluetooth remoto. Objetos dessa classe são retornados quando se realiza um inquiry (busca por dispositivos). Então devemos armazena-los em memória para que possamos fazer a busca de serviços com esse objeto através do objeto discoveryAgent.

agent.searchServices(null, uuidSet, remoteDevice,
classeQueImplementeDiscoveryListene);

Com a chamada desse método, quando o serviço procurado for encontrado a classe que implementa DiscoveryListener será notificada através do método servicesDiscovered, e quando a busca por serviços terminar o método serviceSearchCompleted será chamado.

Abrindo uma conexão (Servidor)
Lembrando que toda conexão utilizando GCF (Generic Connection Framework) deve ser feita em uma Thread separada, segue abaixo um trecho de código que abre uma conexão servidora e fica aguardando por conexões de clientes.

localDevice = LocalDevice.getLocalDevice();
int initialDiscoveredMode =
localDevice.getDiscoverable();
localDevice.setDiscoverable(DiscoveryAgent.GIAC);

StreamConnectionNotifier notifier =
(StreamConnectionNotifier) Connector.open(
"btspp://localhost:0000000000000000000BE3125CDAE");
connection = notifier.acceptAndOpen();
execute(connection);

Antes de abrir a conexão, o dispositivo é colocado em modo de descoberta para que outros possam encontra-lo. Depois é aberta a conexão servidora (localhost) utilizando "btspp" que é o protocolo de comunicação (BlueTooth Serial Port Profile). O número gigante :) após os ":" é o identificador do serviço UUID. O método execute trata os dados vindos do outro dispositivo. Segue abaixo o seu código:

protected void execute(StreamConnection conn){
try {
os = connection.openDataOutputStream();
is = connection.openDataInputStream();
String message;
while (true){
message = is.readUTF();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//closeStreams();
}
}

"os" e "is" são atributos da classe e devem ser um DataOutputStream e um DataInputStream.
Por último, o método que envia as mensagens:

public void send(String message){
try {
os.writeUTF(message);
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
}

Bem pessoal, esse é só um aperitivo. Quem quiser saber mais, postem suas dúvidas.

[Editado]
Atendendo aos comentários, um exemplo bem simples pode ser baixado aqui.

[Editado 2]
E neste link vocês podem baixar um exemplo mais simplificado e comentado (mas precisando de algumas melhorias) do exemplo anterior.

4br4ç05,
nglauber

domingo, 1 de fevereiro de 2009

Vírus do Pen-Drive (BV:Autorun)


Olá povo,

Mais uma vez, depois de um longo tempo sem postar, venho deixar uma dica preciosa aqui - pelo menos foi pra mim.
Como já citei várias vezes aqui, ministro aulas de Java, e em uma dessas aulas veio a pergunta: "Professor, o senhor pode copiar esse material que o senhor tá usando pro meu pen-drive?". Ao que respondí que sim. Que mal poderia ter um inocente pen-drive? Tá, tá bom, pode ter vírus, mas pensava que o meu bom e velho Avast detectaria... Você já sabem o que aconteceu né?

Meu computador foi infectado pelo vírus BV:Autorun-G. Na verdade não é um vírus, é um malware. Ele se instala através do autorun do pen-drive e assim, qualquer pen-drive que se conecte ao computador infectado, será infectado também, da mesma forma que qualquer computador que receba o pen-drive também ficará infectado. Sua ação é de "reciclador". Ele pega os arquivos da lixeira e envia pro pen-drive infectado.

Depois de procurar em diversos sites em vão, achei a solução simples e fácil, diferentemente do que eu achei nos sites. Segue o passo-a-passo para a remoção dessa praga:
1) Usei o CCleaner (http://www.ccleaner.com/) para fazer uma limpeza geral em arquivos e entradas de registro desnecessárias.
2) Usei o FlashDesinfector (http://www.techsupportforum.com/sectools/sUBs/Flash_Disinfector.exe) para fazer com que o pen-drive não executasse mais o malware. Esse programinha exclui o arquivo autorun.inf do pen-drive infectado por um diretório do mesmo nome impedindo que o virus execute quando você "espete" o pen-drive na máquina.
3) Nesse passo é que o milagre se fez :) usei o RegRun Security Suite (http://www.greatis.com/security/). Enquanto nem o Avast e nem o Avira o detectaram, esse software encontrou de "prima". Esse malware normalmente se instala em C:\RECYCLER\[um número de registro]\exe32.exe. Quando ele encontrar esse arquivo, remova-o. Cuidado pra não fazer feito eu, que apaguei algumas DLLs do Windows e tive que restaurar o sistema e fazer o passo 3 de novo :)
4) Seja feliz novamente :)


Apesar de nessas minhas pesquisas o pessoal falar que é um vírus inofensivo, ninguém gosta de ficar com nenhuma vírus/spyware/malware no PC.

Para finalizar, segue algumas dicas para vocês não sofrerem o mesmo que eu:
- Usem todas as ferramentas de detecção que puderem (anti-vírus, anti-spyware, etc.) e as mantenham atualizadas.
- Desativem o auto-run das mídias externas (CD, DVD, pen-drive, etc) no seu PC.
- Não espetem pen-drive de estranhos na sua máquina.

Bem, acho que é só...
Espero postar mais em breve e não demorar tanto.

4br4ç05,
nglauber