Utilizando o Maps v2 no Android

Olá pessoal! Nesse post vamos ver rapidamente como é simples adicionar a (nem tão nova assim) API de mapas da Google no seu aplicativo Android. A princípio vamos simplesmente adicionar um mapa na tela (futuramente, pretendo fazer outros posts mostrando como realizar outras “façanhas” como adicionar marcadores, traçar rotas e outras coisas).

Bom, como já é de praxe, vamos criar um novo projeto no Android Studio. Para esse tutorial, estou utilizando a versão 0.8.1 da IDE, já em sua fase beta. Com o projeto criado, o primeiro passo é adicionar a biblioteca Google Play Services ao projeto. Nesse passo, é bom verificar se ela está instalada. Para isso, abra o seu SDK Manager, e verifique na categoria Extras se o Google Repository está instalado. Caso hajam atualizações, é sempre recomendado baixá-las.

Captura de tela 2014-06-30 21.56.47

Após certificar-se de que está instalado, temos duas possibilidades para adicioná-la ao projeto. Primeiramente, você pode ir até o menu File -> Project Structure…, selecionar o módulo que corresponde ao seu app (no meu caso, app mesmo), selecionar a aba Dependencies e adicionar o Google Play Services clicando no botão + e selecionando Library dependency. Procure por Google Play Services na lista que surgir.

Captura de tela 2014-06-30 22.00.33

Captura de tela 2014-06-30 22.00.55

Outra forma, é simplesmente adicionar a dependência no arquivo build.gradle do módulo do seu app, ficando assim:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.android.gms:play-services:+'
}

Nesse caso, a IDE te oferecerá para sincronizar o projeto para que as dependências sejam atualizadas.

O próximo passo é autorizarmos o nosso app a receber o maps. Acesse este link para ir até o console de APIs do Google. Faça login com a sua conta Google e, então, clique sobre o botão Create Project. Preencha o nome do projeto e escolha um ID (ou deixe como está) para que seja criado um projeto. Com o projeto criado, ele deverá aparecer na sua lista de projetos.

Captura de tela 2014-06-30 22.06.52

Clique sobre ele e selecione a opção Enable an API.

Captura de tela 2014-06-30 22.07.04

Procure na lista pela opção Google Maps Android API v2 e habilite-a.

Captura de tela 2014-06-30 22.07.32

Agora, clique sobre a opção Credentials no menu esquerdo e clique sobre a opção Create new key, selecionando a opção Android key logo em seguida. Por fim, clique sobre o botão Create. Você pode, nessa tela, limitar essa sua chave a uma determinada combinação de assinatura + identificador (pacote) do seu app. Como a API é gratuita somente até certo ponto, é extremamente recomendável criar esse controle quando seu aplicativo for publicado / distribuído. Com a chave criada, copie o valor da API key gerada.

Voltando ao seu app, vamos configurar nosso arquivo AndroidManifest.xml. Primeiramente, precisamos adicionar algumas permissões:

<permission
    android:name="net.rafaeltoledo.exemplomapa.permission.MAPS_RECEIVE"
    android:protectionLevel="signature" />

<uses-permission android:name="net.rafaeltoledo.exemplomapa.permission.MAPS_RECEIVE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- Opcionais, caso necessitar da localização do usuário -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Vale ficar atento nesse ponto de que criamos uma permissão específica ali que deve começar com o pacote do seu app (no meu caso, net.rafaeltoledo.exemplomapa). Em seguida, vamos adicionar uma restrição ao AndroidManifest, já que a nova API de mapas requer OpenGL ES 2 ou superior. Adicione a seguinte tag logo antes do nó application:

<uses-feature
    android:glEsVersion="0x00020000"
    android:required="true" />

Por último, basta adicionar duas tags meta-data ao nó application (dentro dele):

<!-- Requerido pelo Google Maps -->
<meta-data
    android:name="com.google.android.maps.v2.API_KEY"
    android:value="SUA API KEY AQUI" />

<!-- Requerido pelo Google Play Services -->
<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />

E pronto. Agora, basta adicionar a tag com o mapa em seu XML de layout:

<fragment
    android:id="@+id/map"
    class="com.google.android.gms.maps.MapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Uma última dica: caso esteja suportando versões abaixo do Android 3.0, substitua MapFragment por SupportMapFragment. E é isso! Ao executar, já teremos nosso mapa na tela!

Captura de tela 2014-06-30 22.31.11

Por hoje é só pessoal!

Criando um Navigation Drawer customizado no Android

Olá pessoal! Voltando a postar aqui no blog, hoje vamos ver como criar um Navigation Drawer customizado no Android. Navigation Drawer, pra quem não sabe, é aquele menuzinho deslizante no Android que vem do lado esquerdo do app, sinalizado pelos três traços horizontais ao lado do ícone do aplicativo na Action Bar.

Captura de tela 2014-06-24 20.06.17

Nas últimas versões do Android Studio, nós já temos um modelo de Activity que cria um Navigation Drawer pra gente, um modelo simplezinho com uma lista de opções. Nesse post, vamos ver como criar um Navigation Drawer com uma View customizada e fazer aquele visual bacana pro seu app.

Pra início de conversa, crie um projeto no seu Android Studio (estou utilizando para esse post a versão 0.6.1) e utilize o modelo de Activity com o Navigation Drawer.

Captura de tela 2014-06-24 20.12.13

Ao criar uma Activity com esse modelo, uma porção de código é gerada (classes e XMLs de layout). Vamos analisar alguns pontos principais.

  • Activity: Simplificadamente, possui como layout um DrawerLayout (para o efeito de deslize do Navigation Drawer), tendo como filhos o Fragment do Navigation Drawer e um FrameLayout onde o conteúdo das telas será colocado. Já no código, o ponto mais importante é o método onNavigationDrawerItemSelected(), método de callback chamado toda vez que um elemento do drawer é selecionado.
  • NavigationDrawerFragment: a classe que representa o nosso drawer. No wizard é gerada uma tonelada de código, mas podemos destacar o método onCreateView() no qual criamos o layout do drawer e o método selectItem(), responsável por notificar o que foi selecionado nele.

Nesse código gerado, temos um fragment chamado PlaceholderFragment, utilizado apenas para criar o efeito de mudança nas opções. Além disso, o layout do drawer é simplesmente uma lista semi-transparente.

Captura de tela 2014-06-24 21.12.45

Para este exemplo, então, vamos criar um Navigation Drawer mais interessante, com um cabeçalho para um resumo de um perfil de usuário (fictício), vamos deixar essas três opções de navegação que já estão ali (ou mesmo podemos adicionar mais algumas) e um rodapé para créditos de desenvolvimento. Além disso, vamos fazer com que o Navigation Drawer mantenha destacada a página de navegação atual do usuário.

Vamos lá. O primeiro passo é modificarmos o arquivo de layout do Navigation Drawer, no caso, o arquivo fragment_navigation_drawer.xml. Adicionei um RelativeLayout ao redor da lista e a configurei para ficar entre o TextView de topo (perfil do usuário) e rodapé (créditos). Ficou assim:

<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:background="@android:color/white"
    tools:context="net.rafaeltoledo.hellospringapi.NavigationDrawerFragment">

    <TextView
        android:id="@+id/headerView"
        style="?android:attr/textAppearanceLarge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"        
        android:drawableLeft="@drawable/photo"
        android:gravity="center_vertical"
        android:padding="25dp"
        android:text="Rafael Toledo" />

    <TextView
        android:id="@+id/footerView"
        style="?android:attr/textAppearanceMedium"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"        
        android:gravity="center"
        android:padding="20dp"
        android:text="Desenvolvido por Rafael Toledo"
        android:textStyle="bold" />

    <ListView
        android:id="@+id/navigationItems"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/footerView"
        android:layout_below="@+id/headerView"
        android:background="#cccc"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp" />

</RelativeLayout>

O próximo passo é modificarmos a forma como o NavigationDrawerFragment lida com seu layout (já que anteriormente ele o tratava apenas como uma lista). Vamos fazer uma leve modificação no método onCreateView() para que ele busque a lista no layout. Fica assim:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(
            R.layout.fragment_navigation_drawer, container, false);

    mDrawerListView = (ListView) view.findViewById(R.id.navigationItems);
    mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
        }
    });
    mDrawerListView.setAdapter(new ArrayAdapter<String>(
            getActionBar().getThemedContext(),
            android.R.layout.simple_list_item_activated_1,
            android.R.id.text1,
            new String[]{
                    getString(R.string.title_section1),
                    getString(R.string.title_section2),
                    getString(R.string.title_section3),
            }));
    mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
    return view;
}

Veja que não modificamos muita coisa. Só fizemos alguns ajustes devido ao fato de que o elemento raiz do layout do drawer não é mais a lista e sim o RelativeLayout. Dessa forma, precisamos fazer o lookout nele pra buscar a lista. Se executarmos o app nesse ponto, teremos algo assim:

Captura de tela 2014-06-24 21.30.59

Percebam que o conteúdo está um pouco “espremido” no Navigation Drawer devido a sua largura. Segundo as boas práticas de design do Android, um Navigation Drawer pode ter entre 240dp 320dp. Vamos alterar a sua largura no arquivo values/dimens.xml para 300dp, alterando o valor de navigation_drawer_width.

Captura de tela 2014-06-24 21.35.23

Já ficou mais interessante, né? O negócio agora é fazermos as opções serem, de fato, selecionadas quando forem clicadas, ficando fácil pro usuário saber em qual tela ele está. Pra isso, basta fazermos uma pequena modificação no método onCreateView() do NavigationDrawerFragment, alterando o layout do adapter de simple_list_item_1 para simple_list_item_activated_1. Simples não?

Captura de tela 2014-06-24 22.06.59

E é isso minha gente! Ah, é bom ficar atento que esse último passo não funciona no Android 2.3! Se bem que, hoje, pra um app novo, já não compensa (na minha opinião) suportar o Gingerbread… Bola pra frente devs!

Até a próxima!

Tutorial Windows Phone #5 – Transferindo Dados entre Páginas

Olá pessoal!

No último tutorial sobre Windows Phone, criamos um sistema simples de navegação entre duas páginas. Hoje, veremos como passar dados entre as páginas. Para início de conversa, crie um novo projeto chamado NavegacaoComParametros (ou um outro nome qualquer que você queira). Após criar, adicione uma nova página, chamada Pagina2.xaml, da mesma forma como foi feito no último tutorial.

Nosso aplicativo será constituído de três Hyperlinks, que direcionarão para a segunda página, onde será exibida qual opção foi selecionada.

Continuar lendo

Tutorial Android #25 – Fazendo Ligações (Chamadas)

Olá pessoal! Tudo bem?

Sei que é triste a dor da partida, mas esse é o último tutorial dessa primeira série de tutoriais sobre Android. Mas como disse no último post, isso não significa que o tema não será mais abordado aqui no blog. Estou considerando seriamente a possibilidade de, em breve, voltar a falar de Android e as novidades que temos ao utilizar a versão 4.

Mas por enquanto, vamos fechar essa primeira série com chave de ouro. Como toda a série foi voltada para Android, um sistema utilizado massivamente em aparelhos celulares, vamos ver hoje como realizar chamadas.

Nesta última sessão de modificações em nosso aplicativo, vamos adicionar um campo chamado telefone aos nossos restaurantes e, a partir dele, vamos possibilitar que o usuário faça uma chamada diretamente do nosso aplicativo. Bacana, não?

Bom, começando, temos inicialmente que atualizar o nosso modelo de dados para que armazene o novo dado necessário (o número de telefone). Primeiramente, modifique o método onCreate() da classe GerenciadorRestaurantes para abrigar o novo campo:

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE restaurantes (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
" nome TEXT, endereco TEXT, tipo TEXT, anotacoes TEXT, twitter TEXT," +
" latitude REAL, longitude REAL, telefone TEXT);");
}
view raw snippet01.java hosted with ❤ by GitHub

Em seguida, altere o método onUpgrade() para atualizar o modelo de dados do banco, caso o usuário esteja vindo de uma versão anterior:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
db.execSQL("ALTER TABLE restaurantes ADD COLUMN twitter TEXT");
}
if (oldVersion < 3) {
db.execSQL("ALTER TABLE restaurantes ADD COLUMN latitude REAL");
db.execSQL("ALTER TABLE restaurantes ADD COLUMN longitude REAL");
}
if (oldVersion < 4) {
db.execSQL("ALTER TABLE restaurantes ADD COLUMN telefone TEXT");
}
}
view raw snippet02.java hosted with ❤ by GitHub

Pronto. Agora, vamos procurar pelos métodos inserir(), atualizar(), obterTodos() e obterPorId() para adicionar o campo telefone a eles também.

public void inserir(String nome, String endereco, String tipo, String anotacoes, String twitter,
String telefone) {
ContentValues valores = new ContentValues();
valores.put("nome", nome);
valores.put("endereco", endereco);
valores.put("tipo", tipo);
valores.put("anotacoes", anotacoes);
valores.put("twitter", twitter);
valores.put("telefone", telefone);
getWritableDatabase().insert("restaurantes", "nome", valores);
}
public void atualizar(String id, String nome, String endereco, String tipo, String anotacoes,
String twitter, String telefone) {
ContentValues valores = new ContentValues();
String[] argumentos = {id};
valores.put("nome", nome);
valores.put("endereco", endereco);
valores.put("tipo", tipo);
valores.put("anotacoes", anotacoes);
valores.put("twitter", twitter);
valores.put("telefone", telefone);
getWritableDatabase().update("restaurantes", valores, "_id=?", argumentos);
}
public Cursor obterTodos(String ordenacao) {
return getReadableDatabase().rawQuery("select _id, nome, endereco, tipo, " +
"anotacoes, twitter, latitude, longitude, telefone FROM restaurantes ORDER BY " +
ordenacao, null);
}
public Cursor obterPorId(String id) {
String[] argumentos = {id};
return getReadableDatabase().rawQuery(
"SELECT _id, nome, endereco, tipo, anotacoes, twitter, latitude," +
" longitude, telefone FROM restaurantes WHERE _id = ?", argumentos);
}
view raw snippet03.java hosted with ❤ by GitHub

Para concluir as alterações nesta classe, crie o método obterTelefone():

public String obterTelefone(Cursor c) {
return c.getString(8);
}
view raw snippet04.java hosted with ❤ by GitHub

Pronto. Com relação a persistência, já estamos aptos a prosseguir. Vamos agora adicionar o campo telefone aos nossos formulários. Como devem estar lembrados, temos 2 layouts, um para o modo retrato, outro para o modo paisagem. Primeiro no modo retrato, adicione o seguinte trecho logo após o campo de endereço, no arquivo res/layout/form_detalhes.xml.

<TableRow>
<TextView android:text="@string/telefone" />
<EditText android:id="@+id/telefone" />
</TableRow>
view raw snippet05.xml hosted with ❤ by GitHub

Agora no res/layout-land/form_detalhes.xml. Também, logo depois do trecho do campo de endereço.

<TableRow>
<TextView android:text="@string/telefone" />
<EditText
android:id="@+id/telefone"
android:layout_span="2" />
</TableRow>
view raw snippet06.xml hosted with ❤ by GitHub

Pronto. Nossas modificações agora serão na classe FormularioDetalhes. Primeiramente adicione o atributo telefone à classe:

EditText telefone = null;
view raw snippet07.java hosted with ❤ by GitHub

Agora, precisamos atualizar os métodos onCreate(), salvar(), carregar(), onSaveInstanceState() e onRestoreInstanceState(). Pode parecer bastante coisa, mas são apenas ajustes leves para adicionar o novo campo.

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.form_detalhes);
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
gerenciador = new GerenciadorRestaurantes(this);
nome = (EditText) findViewById(R.id.nome);
endereco = (EditText) findViewById(R.id.end);
telefone = (EditText) findViewById(R.id.telefone);
anotacoes = (EditText) findViewById(R.id.anotacoes);
twitter = (EditText) findViewById(R.id.twitter);
tipos = (RadioGroup) findViewById(R.id.tipos);
localizacao = (TextView) findViewById(R.id.localizacao);
idRestaurante = getIntent().getStringExtra(ListaRestaurantes._ID);
if (idRestaurante != null) {
carregar();
}
}
private void salvar() {
String tipo = null;
switch (tipos.getCheckedRadioButtonId()) {
case R.id.rodizio:
tipo = "rodizio";
break;
case R.id.fast_food:
tipo = "fast_food";
break;
case R.id.a_domicilio:
tipo = "a_domicilio";
break;
}
if (tipo != null && endereco.getText().toString() != null &&
nome.getText().toString() != null) {
if (idRestaurante == null) {
gerenciador.inserir(nome.getText().toString(),
endereco.getText().toString(),
tipo, anotacoes.getText().toString(),
twitter.getText().toString(), telefone.getText().toString());
} else {
gerenciador.atualizar(idRestaurante,
nome.getText().toString(),
endereco.getText().toString(),
tipo, anotacoes.getText().toString(),
twitter.getText().toString(), telefone.getText().toString());
}
}
finish();
}
private void carregar() {
Cursor c = gerenciador.obterPorId(idRestaurante);
c.moveToFirst();
nome.setText(gerenciador.obterNome(c));
endereco.setText(gerenciador.obterEndereco(c));
telefone.setText(gerenciador.obterTelefone(c));
anotacoes.setText(gerenciador.obterAnotacoes(c));
twitter.setText(gerenciador.obterTwitter(c));
if (gerenciador.obterTipo(c).equals("rodizio")) {
tipos.check(R.id.rodizio);
} else if (gerenciador.obterTipo(c).equals("fast_food")) {
tipos.check(R.id.fast_food);
} else {
tipos.check(R.id.a_domicilio);
}
latitude = gerenciador.obterLatitude(c);
longitude = gerenciador.obterLongitude(c);
localizacao.setText(String.valueOf(gerenciador.obterLatitude(c)) +
", " + String.valueOf(gerenciador.obterLongitude(c)));
c.close();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("nome", nome.getText().toString());
outState.putString("endereco", endereco.getText().toString());
outState.putString("telefone", telefone.getText().toString());
outState.putString("anotacoes", anotacoes.getText().toString());
outState.putInt("tipo", tipos.getCheckedRadioButtonId());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
nome.setText(savedInstanceState.getString("nome"));
endereco.setText(savedInstanceState.getString("endereco"));
telefone.setText(savedInstanceState.getString("telefone"));
anotacoes.setText(savedInstanceState.getString("anotacoes"));
tipos.check(savedInstanceState.getInt("tipo"));
}
view raw snippet08.java hosted with ❤ by GitHub

Ufa! Perceberam como as alterações foram mínimas? 😀

Prosseguindo, precisamos dizer ao Android que nossa aplicação deseja realizar chamadas. Para isso, adicione a seguinte linha às permissões no arquivo AndroidManifest.xml.

<uses-permission android:name="android.permission.CALL_PHONE" />
view raw snippet09.xml hosted with ❤ by GitHub

Agora, vamos criar a opção ao menu para realizar as chamadas. Edite o arquivo res/menu/opcao_detalhes.xml para acomodar a nova opção (é… vai ficar meio espremido em telas pequenas…).

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/twitter"
android:title="@string/timeline_twitter"
android:icon="@drawable/twitter" />
<item
android:id="@+id/localizacao"
android:title="@string/salvar_localizacao"
android:icon="@drawable/gps" />
<item
android:id="@+id/mapa"
android:title="@string/exibir_mapa"
android:icon="@drawable/mapa" />
<item
android:id="@+id/chamar"
android:title="@string/chamar"
android:icon="@drawable/chamada" />
</menu>
view raw snippet10.xml hosted with ❤ by GitHub

O arquivo de ícone utilizado foi o ic_menu_call.png encontrado na pasta de instalação do Android e devidamente renomeado para chamada.png.

Agora precisamos definir as novas strings utilizadas no formulário e no menu. Adicione-as ao arquivo res/values/strings.xml

<string name="telefone">Telefone:</string>
<string name="chamar">Telefonar</string>
view raw snippet11.xml hosted with ❤ by GitHub

… e no res/values-es/strings.xml.

<string name="telefone">Teléfono:</string>
<string name="chamar">Llamar</string>
view raw snippet12.xml hosted with ❤ by GitHub

Por fim, vamos fazer com que a opção de menu realize a chamada. Adicione o seguinte trecho aos encadeamentos de ifs no método onOptionsItemSelected().

} else if (item.getItemId() == R.id.chamar) {
String numero = "tel:" + telefone.getText().toString();
if (numero.length() > 4) {
startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(numero)));
}
}
view raw snippet13.java hosted with ❤ by GitHub

Execute a aplicação para conferir as modificações.

Caso queira que a ligação seja realizada diretamente, sem exibir o discador, modifique, na linha 158 do trecho ali em cima, ACTION_DIAL por ACTION_CALL.

Bom pessoal, é isso! Como não podia deixar de ser, para baixar o projeto, basta clicar aqui.

Agradeço a todos que vem acompanhando o blog. Continuem sempre por aqui, pois sempre tem coisa nova! Comentem, critiquem, cliquem nos banners e enviem sugestões!

Até logo!!!

Tutorial Android #24 – Widgets (II)

Olá pessoal! Como estão?

No último post, vimos como criar um widget simples com o nome do restaurante. Hoje, vamos incrementá-lo em dois pontos principais: (1) ele terá um botão que mudará o restaurante que é exibido, mostrando outro aleatoriamente e; (2) ao tocar sobre o nome do restaurante no widget, é aberto o formulário para que você possa ver as outras informações sobre aquele restaurante.

Então, mãos à massa!

O primeiro passo é adicionarmos o botão ao layout de nosso widget. Dessa forma, abra o arquivo widget.xml que está em res/layout e faça a adição da imagem do botão, do tipo ImageButton:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/frame">
<TextView
android:id="@+id/nome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:textSize="10pt"
android:textColor="#FFFFFF" />
<ImageButton
android:id="@+id/proximo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:src="@drawable/proximo" />
</RelativeLayout>
view raw snippet01.xml hosted with ❤ by GitHub

Os atributos, no ponto em que estamos, não devem ser nenhuma surpresa. Neste ponto, já deve ser possível vê-lo no layout ao recompilar a aplicação.

O arquivo de imagem do ícone pode ser baixado junto com o projeto no final deste post.

No post anterior, toda a nossa lógica de consulta ao banco estava dentro do método onUpdate() do nosso widget. Isso funciona bem, mas, caso a lógica se expanda muito, corremos o risco da atualização demorar mais que o esperado e comprometer o desempenho da aplicação, já que o método é chamado em nossa thread principal.

Para melhorar isso, vamos novamente fazer uso de um IntentService que nos possibilita a realizar a consulta de modo assíncrono, sem comprometer o sistema. Portanto, crie uma classe chamada WidgetService e coloque-a no pacote net.rafaeltoledo.restaurante. Esta classe, que estenderá IntentService abrigará a lógica que estávamos executando no widget.

package net.rafaeltoledo.restaurante;
import android.app.IntentService;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.database.Cursor;
import android.widget.RemoteViews;
public class WidgetService extends IntentService {
public WidgetService() {
super("WidgetService");
}
@Override
protected void onHandleIntent(Intent intent) {
ComponentName cn = new ComponentName(this, WidgetAplicativo.class);
RemoteViews atualizarFrame = new RemoteViews("net.rafaeltoledo.restaurante", R.layout.widget);
GerenciadorRestaurantes gerenciador = new GerenciadorRestaurantes(this);
AppWidgetManager mgr = AppWidgetManager.getInstance(this);
try {
Cursor c = gerenciador.getReadableDatabase().rawQuery("SELECT COUNT(*) FROM restaurantes", null);
c.moveToFirst();
int contador = c.getInt(0);
c.close();
if (contador > 0) {
int deslocamento = (int) (contador * Math.random());
String args[] = {String.valueOf(deslocamento)};
c = gerenciador.getReadableDatabase().rawQuery("SELECT _id, nome FROM restaurantes LIMIT 1 OFFSET ?", args);
c.moveToFirst();
atualizarFrame.setTextViewText(R.id.nome, c.getString(1));
c.close();
} else {
atualizarFrame.setTextViewText(R.id.nome, getString(R.string.vazio));
}
} finally {
gerenciador.close();
}
mgr.updateAppWidget(cn, atualizarFrame);
}
}
view raw snippet02.java hosted with ❤ by GitHub

Basicamente o que fizemos foi passar a lógica para o nosso WidgetService. O próximo passo é adicionar esse nosso serviço lá no arquivo AndroidManifest.xml para que ele possa funcionar. Assim, adicione ao fim do nó application a seguinte linha:

<service android:name=".WidgetService" />
view raw snippet03.xml hosted with ❤ by GitHub

Agora, vamos atualizar a classe WidgetAplicativo para que faça uso do nosso serviço.

package net.rafaeltoledo.restaurante;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
public class WidgetAplicativo extends AppWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
context.startService(new Intent(context, WidgetService.class));
}
}
view raw snippet04.java hosted with ❤ by GitHub

Pronto. A parte mais “complicada” está feita. Agora precisamos apenas gerenciar os toques no botão e no nome do restaurante. Primeiramente, vamos fazer com que um novo restaurante seja exibido caso seja acionado o botão. Para esse processo, utilizaremos um PendingIntent para acionar a nossa Intent quando o “clique” for realizado.

Para isso, adicione as seguintes linhas ao final do método onHandleEvent() da classe WidgetService:

Intent i = new Intent(this, WidgetService.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
atualizarFrame.setOnClickPendingIntent(R.id.proximo, pi);
mgr.updateAppWidget(cn, atualizarFrame);
view raw snippet05.java hosted with ❤ by GitHub

Com isso, toda vez que o botão for acionado, o nosso serviço de carregamento será executado novamente.

Por fim, precisamos agora gerenciar para que, ao tocar sobre o nome do restaurante, seja aberta a tela com o formulário. Para isso, precisaremos de realizar algumas mudanças sutis no método onHandleIntent(). As principais são que, além do nome, precisaremos também do identificador, pois através dele iremos carregar os dados no formulário. Além disso, também utilizaremos um PendingIntent para acionar a exibição do formulário.

@Override
protected void onHandleIntent(Intent intent) {
ComponentName cn = new ComponentName(this, WidgetAplicativo.class);
RemoteViews atualizarFrame = new RemoteViews("net.rafaeltoledo.restaurante", R.layout.widget);
GerenciadorRestaurantes gerenciador = new GerenciadorRestaurantes(this);
AppWidgetManager mgr = AppWidgetManager.getInstance(this);
try {
Cursor c = gerenciador.getReadableDatabase().rawQuery("SELECT COUNT(*) FROM restaurantes", null);
c.moveToFirst();
int contador = c.getInt(0);
c.close();
if (contador > 0) {
int deslocamento = (int) (contador * Math.random());
String args[] = {String.valueOf(deslocamento)};
c = gerenciador.getReadableDatabase().rawQuery("SELECT _id, nome FROM restaurantes LIMIT 1 OFFSET ?", args);
c.moveToFirst();
atualizarFrame.setTextViewText(R.id.nome, c.getString(1));
Intent i = new Intent(this, FormularioDetalhes.class);
i.putExtra(ListaRestaurantes._ID, c.getString(0));
PendingIntent pi = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
atualizarFrame.setOnClickPendingIntent(R.id.nome, pi);
c.close();
} else {
atualizarFrame.setTextViewText(R.id.nome, getString(R.string.vazio));
}
} finally {
gerenciador.close();
}
Intent i = new Intent(this, WidgetService.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
atualizarFrame.setOnClickPendingIntent(R.id.proximo, pi);
mgr.updateAppWidget(cn, atualizarFrame);
}
view raw snippet06.java hosted with ❤ by GitHub

E… prontinho! É isso. Como sempre, para baixar o código do aplicativo, só clicar aqui.

O próximo tutorial de Android muito provavelmente será o último. Não porque eu ache que não há mais o que ser tratado, mas porque a ideia dos tutoriais era mostrar uma visão geral, com alguns conceitos básicos. 25 tutoriais é um bom número. E isso também me permitirá abordar outros assuntos. (estou com ideia de pelo menos mais 6 séries de tutoriais!)

Então… aguardem pelo gran finale Android! Até lá! 🙂

Tutorial Android #23 – Widgets (I)

Olá pessoal! No post de hoje vamos ver a primeira parte sobre como criarmos widgets no Android.

Pra quem não sabe, widgets é uma espécie de miniatura do aplicativo que você pode deixar em uma das áreas de trabalho do Android, colocando à disposição do usuário informações de maneira mais rápida e prática. O widget também pode redirecionar o usuário para o aplicativo principal, funcionando como uma espécie de “atalho”.

Bom, vamos então começar a colocar a mão na massa!

Primeiramente, precisamos definir o layout do nosso widget. Para isso, crie o arquivo widget.xml dentro da pasta res/layout. Ele será bastante simples, inicialmente apenas exibindo o nome de um restaurante cadastrado. Sendo assim, ele terá apenas um TextView em sua estrutura:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/frame">
<TextView android:id="@+id/nome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:textSize="10pt"
android:textColor="#FFFFFF" />
</RelativeLayout>
view raw snippet01.xml hosted with ❤ by GitHub

De diferente do que já fizemos das outras vezes, somente as propriedades que modificam o tamanho e a cor do texto. No mais, tudo dentro dos conformes. O arquivo frame.9.png pode ser baixado junto com o projeto no fim do post. Por que este 9? Porque a imagem é uma NinePatch, ideal para compor fundos de frames. Entenda melhor como funciona aqui.

O próximo passo é criarmos uma classe para gerenciar o conteúdo do widget. Inicialmente, apenas crie uma classe chamada WidgetAplicativo dentro do pacote net.rafaeltoledo.restaurante, estendendo AppWidgetProvider.

package net.rafaeltoledo.restaurante;
import android.appwidget.AppWidgetProvider;
public class WidgetAplicativo extends AppWidgetProvider {
}
view raw snippet02.java hosted with ❤ by GitHub

Continuando, vamos agora definir algumas propriedades do widget em um arquivo XML. Crie dentro da pasta res/xml o arquivo provedor_widget.xml.

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="300dip"
android:minHeight="79dip"
android:updatePeriodMillis="1800000"
android:initialLayout="@layout/widget" />
view raw snippet03.xml hosted with ❤ by GitHub

Basicamente definimos a largura e altura mínimas, o tempo de atualização das informações do widget (no caso, dos restaurantes – a cada 30 minutos) e qual o layout a ser utilizado por ele (no caso, o que definimos no XML anterior).

Em seguida, precisamos atualizar o AndroidManifest.xml para que o nosso aplicativo suporte o widget. Adicione o seguinte nó receiver ao final do nó application.

<receiver android:name=".WidgetAplicativo"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/provedor_widget" />
</receiver>
view raw snippet04.xml hosted with ❤ by GitHub

Neste trecho, definimos que a classe que representa o widget é a WidgetApp, que o nome e o ícone a serem exibidos nas opções são os mesmos da aplicação no menu (app_name e ic_launcher). Além disso, definimos que o widget realizará operações de atualização e que suas propriedades estão definidas no arquivo provedor_widget dentro da pasta xml.

Por fim, vamos implementar o método onUpdate() para a classe WidgetApp. É este método que fará a busca em nosso banco de dados para exibir o nome de um restaurante.

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
ComponentName cn = new ComponentName(context, WidgetApp.class);
RemoteViews atualizarFrame = new RemoteViews("net.rafaeltoledo.restaurante", R.layout.widget);
GerenciadorRestaurantes gerenciador = new GerenciadorRestaurantes(context);
try {
Cursor c = gerenciador.getReadableDatabase().rawQuery("SELECT COUNT(*) FROM restaurantes", null);
c.moveToFirst();
int count = c.getInt(0);
c.close();
if (count > 0) {
int offset = (int) (count * Math.random());
String args[] = {String.valueOf(offset)};
c = gerenciador.getReadableDatabase().rawQuery("SELECT nome FROM restaurantes LIMIT 1 OFFSET ?", args);
c.moveToFirst();
atualizarFrame.setTextViewText(R.id.nome, c.getString(0));
} else {
atualizarFrame.setTextViewText(R.id.nome, context.getString(R.string.vazio));
}
} finally {
gerenciador.close();
}
appWidgetManager.updateAppWidget(cn, atualizarFrame);
}
view raw snippet05.java hosted with ❤ by GitHub

Resumidamente, esse trecho de código:

  • Cria um objeto RemoteView, que nos permite modificar o widget;
  • Estabelece uma conexão com o banco de dados;
  • Verifica quantos restaurantes salvos existem;
  • Carrega um restaurante aleatório (por isso o uso de Math.random());
  • Exibe o nome do restaurante, ou uma mensagem dizendo que não existem restaurantes cadastrados;
  • Atualiza o widget propriamente dito.

A mensagem “vazio” (R.string.vazio) deve ser definida nos seus arquivos string.xml nas pastas values que você tem. No meu caso, vou defini-la em português e em espanhol (idiomas que minha aplicação suporta).

<string name="vazio">Nenhum registro.</string>
view raw snippet06.xml hosted with ❤ by GitHub
<string name="vazio">Ningún registro.</string>
view raw snippet07.xml hosted with ❤ by GitHub

E pronto! Para ativar o widget, clique e segure sobre a área de trabalho para aparecer o menu e a opção de inserir widget.

Pra baixar o projeto, só clicar aqui.

Bom, é isso pessoal! No próximo post vamos melhorar esse widget! Até lá!

Tutorial Android #22 – Internacionalização (i18n)

Olá pessoal! No tutorial de hoje, vamos ver como traduzir nossa aplicação para que ela se torne multi-linguagem. Isso é bastante interessante caso você tenha a intenção de colocar sua aplicação lá no Google Play.

Bom, o grande “segredo” da internacionalização consiste na pasta values do projeto. Nessa pasta, no caso do nosso projeto, temos hoje o arquivo arrays.xml e strings.xml. Esses arquivos irão conter valores correspondentes a cada um dos idiomas que nossa aplicação suportará. Hoje, temos nossa aplicação com o idioma padrão Português. Como o emulador do Android 2.2 não tem o idioma português, vamos deixá-lo como padrão mesmo, e adicionar suporte ao idioma Espanhol. (nota: se você sabe espanhol, releve as sentenças que eu vou mostrar. Foram todas traduzidas com o auxílio do Google Translator. Eu não falo espanhol!)

Para início de conversa, vamos preparar nosso aplicativo para a internacionalização. Modifique o arquivo strings.xml para que contenha os seguintes valores:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, ListaRestaurantes!</string>
<string name="app_name">Lista de Restaurantes</string>
<string name="nome">Nome:</string>
<string name="endereco">Endereço:</string>
<string name="tipo">Tipo:</string>
<string name="rodizio">Rodízio</string>
<string name="fast_food">Fast Food</string>
<string name="a_domicilio">A Domicílio</string>
<string name="localizacao">Localização </string>
<string name="nao_atribuido">(não atribuída)</string>
<string name="anotacoes">Anotações</string>
<string name="conta_twitter">Conta do Twitter</string>
<string name="hora_do_almoco">Hora do Almoço!</string>
<string name="descartado">Descartado.</string>
<string name="sem_internet">Conexão com a Internet indisponível</string>
<string name="local_salvo">Localização salva</string>
<string name="definir">Definir</string>
<string name="cancelar">Cancelar</string>
<string name="notificacao">Hora do Almoço! Está com fome?</string>
<string name="erro_timeline">Erro manipulando timeline Twitter</string>
<string name="erro_activity">Erro enviando dados para a Activity</string>
<string name="timeline_twitter">Timeline Twitter</string>
<string name="salvar_localizacao">Salvar Localização</string>
<string name="exibir_mapa">Exibir Mapa</string>
<string name="adicionar">Adicionar</string>
<string name="configuracoes">Configurações</string>
<string name="modo_listagem">Modo de Listagem</string>
<string name="modo_listagem_desc">Escolha o modo de listagem a ser utilizado</string>
<string name="modo_listagem_titulo">Escolha o modo de listagem</string>
<string name="alarme">Tocar Alarme no Almoço</string>
<string name="alarme_desc">Marque se deseja ser informado sobre a hora do almoço</string>
<string name="alarme_hora">Horário do Alarme do Almoço</string>
<string name="alarme_hora_desc">Configure seu horário desejado para o alarme</string>
<string name="notificacao_titulo">Ativar Notificação</string>
<string name="notificacao_desc">Marque caso deseje um ícone na barra de status, ou desmarque para a notificação em tela cheia</string>
</resources>
view raw snippet01.xml hosted with ❤ by GitHub

Basicamente, definimos alguns alias para as strings do nosso aplicativo. Porém, em alguns trechos do código, ainda temos strings como constantes de texto. Para isso, vamos alterá-los para utilizar os resources deste arquivo. Primeiramente, nos formulários e menus em XML, vamos alterar os arquivos form_detalhes.xml, tanto na pasta layout

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1">
<TableRow>
<TextView android:text="@string/nome" />
<EditText android:id="@+id/nome" />
</TableRow>
<TableRow>
<TextView android:text="@string/endereco" />
<EditText android:id="@+id/end" />
</TableRow>
<TableRow>
<TextView android:text="@string/tipo" />
<RadioGroup android:id="@+id/tipos">
<RadioButton
android:id="@+id/rodizio"
android:text="@string/rodizio" />
<RadioButton
android:id="@+id/fast_food"
android:text="@string/fast_food" />
<RadioButton
android:id="@+id/a_domicilio"
android:text="@string/a_domicilio" />
</RadioGroup>
</TableRow>
<TableRow>
<TextView android:text="@string/localizacao" />
<TextView
android:id="@+id/localizacao"
android:text="@string/nao_atribuido" />
</TableRow>
<EditText
android:id="@+id/anotacoes"
android:singleLine="false"
android:gravity="top"
android:lines="2"
android:scrollHorizontally="false"
android:maxLines="2"
android:maxWidth="200sp"
android:hint="@string/anotacoes"
android:layout_marginTop="4dip" />
<EditText
android:id="@+id/twitter"
android:hint="@string/conta_twitter" />
</TableLayout>
view raw snippet02.xml hosted with ❤ by GitHub

… quanto na pasta layout-land

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="2">
<TableRow>
<TextView android:text="@string/nome" />
<EditText
android:id="@+id/nome"
android:layout_span="2" />
</TableRow>
<TableRow>
<TextView android:text="@string/endereco" />
<EditText
android:id="@+id/end"
android:layout_span="2" />
</TableRow>
<TableRow>
<TextView android:text="@string/tipo" />
<RadioGroup android:id="@+id/tipos">
<RadioButton
android:id="@+id/rodizio"
android:text="@string/rodizio" />
<RadioButton
android:id="@+id/fast_food"
android:text="@string/fast_food" />
<RadioButton
android:id="@+id/a_domicilio"
android:text="@string/a_domicilio" />
</RadioGroup>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<EditText
android:id="@+id/anotacoes"
android:singleLine="false"
android:gravity="top"
android:lines="4"
android:scrollHorizontally="false"
android:maxLines="4"
android:maxWidth="140sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/anotacoes" />
<EditText
android:id="@+id/twitter"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/conta_twitter" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:text="@string/localizacao"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/localizacao"
android:text="@string/nao_atribuido"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</TableRow>
</TableLayout>
view raw snippet03.xml hosted with ❤ by GitHub

… além dos arquivos opcao.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/adicionar"
android:title="@string/adicionar"
android:icon="@drawable/adicionar" />
<item
android:id="@+id/prefs"
android:title="@string/configuracoes"
android:icon="@drawable/menu_preferencias" />
</menu>
view raw snippet04.xml hosted with ❤ by GitHub

e opcao_detalhes.xml, da pasta menu

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/twitter"
android:title="@string/timeline_twitter"
android:icon="@drawable/twitter" />
<item
android:id="@+id/localizacao"
android:title="@string/salvar_localizacao"
android:icon="@drawable/gps" />
<item
android:id="@+id/mapa"
android:title="@string/exibir_mapa"
android:icon="@drawable/mapa" />
</menu>
view raw snippet05.xml hosted with ❤ by GitHub

Nas nossas classes, também temos alguns trechos que precisam ser adaptados. Primeiramente na classe FormularioDetalhes

Toast.makeText(FormularioDetalhes.this, getString(R.string.descartado), Toast.LENGTH_SHORT);
view raw snippet06.java hosted with ❤ by GitHub
Toast.makeText(this, getString(R.string.sem_internet), Toast.LENGTH_LONG).show();
view raw snippet07.java hosted with ❤ by GitHub
Toast.makeText(FormularioDetalhes.this, getString(R.string.local_salvo), Toast.LENGTH_LONG);
view raw snippet08.java hosted with ❤ by GitHub

… na classe PreferenciaHorario

setPositiveButtonText(getContext().getString(R.string.definir));
setNegativeButtonText(getContext().getString(R.string.cancelar));
view raw snippet09.java hosted with ❤ by GitHub

… na classe ReceptorAlarme

Notification nota = new Notification(R.drawable.notificacao, context.getString(R.string.hora_do_almoco), System.currentTimeMillis());
view raw snippet10.java hosted with ❤ by GitHub
nota.setLatestEventInfo(context, context.getString(R.string.app_name), context.getString(R.string.notificacao), i);
view raw snippet11.java hosted with ❤ by GitHub

… e na classe TwitterService

Log.e(getString(R.string.app_name), getString(R.string.erro_timeline), ex);
view raw snippet12.java hosted with ❤ by GitHub
Log.w(getString(R.string.app_name), getString(R.string.erro_activity), ex);
view raw snippet13.java hosted with ❤ by GitHub

Caso a aplicação seja executada agora, ela deve rodar normalmente como rodava antes. Agora, vamos criar os arquivos relativos ao novo idioma. Crie um novo diretório no projeto chamado values-es na pasta res. Dentro dele, teremos 2 arquivos XML, arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="nomes_ordenacao">
<item>Por Nombre, Ascendente</item>
<item>Por Nombre, Descendente</item>
<item>Por Tipo</item>
<item>Por Dirección, Ascendente</item>
<item>Por Dirección, Descendente</item>
</string-array>
<string-array name="opcoes_ordenacao">
<item>nome ASC</item>
<item>nome DESC</item>
<item>tipo, nome ASC</item>
<item>endereco ASC</item>
<item>endereco DESC</item>
</string-array>
</resources>
view raw snippet14.xml hosted with ❤ by GitHub

e strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, ListaRestaurantes!</string>
<string name="app_name">Lista de Restaurantes</string>
<string name="nome">Nombre:</string>
<string name="endereco">Dirección:</string>
<string name="tipo">Tipo:</string>
<string name="rodizio">Rotación</string>
<string name="fast_food">Comida Rápida</string>
<string name="a_domicilio">Ubicación</string>
<string name="localizacao">Posición </string>
<string name="nao_atribuido">(sin asignar)</string>
<string name="anotacoes">Anotaciones</string>
<string name="conta_twitter">Cuenta de Twitter</string>
<string name="hora_do_almoco">La Hora del Almuerzo!</string>
<string name="descartado">Tirado.</string>
<string name="sem_internet">Conexión com la Internet no está disponible</string>
<string name="local_salvo">Posición salva</string>
<string name="definir">Definir</string>
<string name="cancelar">Cancelar</string>
<string name="notificacao">La Hora del Almuerzo! ¿Tienes hambre?</string>
<string name="erro_timeline">Error de Línea de Tiempo de Twitter</string>
<string name="erro_activity">Erros al enviar datos a la Activity</string>
<string name="timeline_twitter">Línea de Tiempo de Twitter</string>
<string name="salvar_localizacao">Guardar Posición</string>
<string name="exibir_mapa">Ver Mapa</string>
<string name="adicionar">Añadir</string>
<string name="configuracoes">Ajustes</string>
<string name="modo_listagem">Modo de Lista</string>
<string name="modo_listagem_desc">Elija el modo de lista que se utiliza</string>
<string name="modo_listagem_titulo">Elija el modo de lista</string>
<string name="alarme">Reproducir Alarma en el Almuerzo</string>
<string name="alarme_desc">Selecciona se desea recibir información sobre la hora del almuerzo</string>
<string name="alarme_hora">La Hora del Almuerzo</string>
<string name="alarme_hora_desc">Ajuste el tiempo deseado para el alarma</string>
<string name="notificacao_titulo">Habilitar Notificación</string>
<string name="notificacao_desc">Seleccione si desea un icono en la barra de estado o borrar la notificación en pantalla completa</string>
</resources>
view raw snippet15.xml hosted with ❤ by GitHub

E pronto! Para ver seu aplicativo no idioma espanhol, siga até o menu principal, Settings -> Language & keyboard -> Select language e selecione Español.

Como de costume, pra baixar o aplicativo, basta clicar aqui.

Até a próxima!

Tutorial Android #21 – Notificações

Olá pessoal! No último post vimos como utilizar alarmes em nossos aplicativos utilizando o AlarmManager. Porém, o alarme que definimos era exibido em tela cheia e, talvez, isso nem sempre é interessante. No tutorial de hoje, vamos adicionar a opção para o caso do usuário preferir uma notificação simples em vez de exibir ela em tela cheia.

O primeiro passo é adicionarmos mais essa opção no menu de opções. Assim, edite o arquivo preferencias.xml para adicionar um novo CheckBoxPreference.

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:key="listagem"
android:title="Modo de Listagem"
android:summary="Escolha o modo de listagem a ser utilizado"
android:entries="@array/nomes_ordenacao"
android:entryValues="@array/opcoes_ordenacao"
android:dialogTitle="Escolha o modo de listagem" />
<CheckBoxPreference
android:key="alarme"
android:title="Tocar Alarme no Almoço"
android:summary="Marque se deseja ser informado sobre a hora do almoço" />
<net.rafaeltoledo.restaurante.PreferenciaHorario
android:key="horario_alarme"
android:title="Horário do Alarme do Almoço"
android:defaultValue="12:00"
android:summary="Configure seu horário desejado para o alarme"
android:dependency="alarme" />
<CheckBoxPreference
android:key="usar_notificacao"
android:title="Ativar Notifocação"
android:defaultValue="true"
android:summary="Marque caso deseje um ícone na barra de status, ou desmarque para a notificação em tela cheia"
android:dependency="alarme" />
</PreferenceScreen>
view raw snippet01.xml hosted with ❤ by GitHub

Por fim, vamos editar o método onReceive da classe ReceptorAlarme para realizar a exibição da notificação, caso esta opção esteja selecionada.

@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences preferencias = PreferenceManager.getDefaultSharedPreferences(context);
boolean usarNotificacao = preferencias.getBoolean("usar_notificacao", true);
if (usarNotificacao) {
NotificationManager gerenciador = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification nota = new Notification(R.drawable.notificacao, "Hora do Almoço!", System.currentTimeMillis());
PendingIntent i = PendingIntent.getActivity(context, 0, new Intent(context, AlarmeActivity.class), 0);
nota.setLatestEventInfo(context, "Lista de Restaurantes", "Hora do Almoço! Está com fome?", i);
nota.flags |= Notification.FLAG_AUTO_CANCEL;
gerenciador.notify(ID_NOTIFICACAO, nota);
} else {
Intent i = new Intent(context, AlarmeActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
view raw snippet02.java hosted with ❤ by GitHub

Para essa operação, será necessário adicionarmos um atributo na classe, que é basicamente um número único para diferenciar esta notificação de outras que, eventualmente, venhamos utilizar em nosso aplicativo.

O ícone referenciado no aplicativo é o ícone de notificação do GTalk, que pode ser obtido no diretório de instalação da SDK sob o nome de stat_notify_chat. Caso deseje utilizar outro, fique à vontade.

E pronto! Já temos nosso aplicativo funcionando. Configure o alarme e realize seus testes.

Como de costume, para baixar o projeto, basta clicar aqui. Até logo! 🙂