Mostrando postagens com marcador Google. Mostrar todas as postagens
Mostrando postagens com marcador Google. Mostrar todas as postagens

segunda-feira, 30 de junho de 2014

Google I/O 2014

Olá povo,

No último dia 21/06 viajei para San Francisco para o meu primeiro Google I/O, conferência anual do Google. O evento aconteceu nos dias 25 e 26/06, e nesse post vou contar um pouquinho de como foi a viagem, pois todas as palestras podem ser vistas aqui ;)
Peguei um vôo da American Airlines de Recife para Miami (6 horas), e de Miami para San Francisco (mais 6 horas). Chegando em SF, fui recepcionado pelos meus estimados colegas Neto Marin e José Papo, que me levaram para conhecer o Pier 39, um ponto turístico da cidade.
Almoçamos por lá e depois demos uma volta pela cidade de carro. Depois eles me deixaram na casa onde fiquei hospedado (ótima por sinal) que fica a 15 minutos do centro. Dividi a casa com mais dois colegas: Alexandre Tarifa e Paulo Fernandes.
Na segunda-feira (23/06), fomos conhecer o Google, e foi simplesmente fantástico com vários prédios bacanas pra caramba. Mas minha principal meta era: tirar fotos no prédio onde ficam as estátuas do Android. Missão cumprida! :)


Assisti a vitória da seleção brasileira por 4x1 sobre Camarões no refeitório do Google, onde tinham vários brasileiros torcendo pela nossa seleção. Foi massa! :) Em seguida conheci outros prédios do Google Plex.
Lá dentro tem uma loja que vende camisas, bonecos, casacos com estampas das coisas do Google. Aí lascou! Comprei um monte :)
Depois do passeio no Google, fui conhecer um pouquinho do centro de SF. Uma coisa curiosa (e boa ao meu ver) é que as 20:00 ainda está claro, só começa a escurecer entre 20:30 e 21:00 :)
Jantei na Cheesecake Factory (infelizmente Penny não trabalha lá) e além de a comida ser excelente (e não tão cara) a vista de lá é bem bacana.
Terça-feira foi o dia de fazer o checkin no local do evento o Moscone Center West que estava vestido para o Google I/O.

Fiz a confirmação da minha presença e ganhei aquele kit básico de evento (crachá, camisa e garrafinha). Em seguida, fui para uma festa exclusiva para os GDEs onde conheci outros GDEs do resto mundo. Depois de algumas horas, fui pra casa pra descansar e me preparar para o primeiro dia do evento.

Acordei cedo na quarta-feira preparado para enfrentar a maratona de palestras que estava por vir. Peguei uma fila que deu a volta no quarteirão e perdi os 20 primeiros minutos do keynote de abertura (fiquei puto, porque GDE não precisava pegar essa fila) mas tudo bem, assisti quando cheguei em casa.
No keynote de abertura, os principais tópicos do Android foram:
- Android L Developer Preview que ficará no lugar do KitKat;
- Android Wear, o Android para dispositivos vestíveis, em especial, relógios e o glass;
- Android Auto, distribuição da plataforma para carros;
- Android TV, mais uma tentativa do Google em emplacar nas smartTVs.
Como é de costume o Google dá um "presentinho" a todos os participantes do I/O, e esse ano o participantes poderiam escolher entre o LG G watch ou Samsung Gear Live. Eu escolhi o segundo, (aos assaltantes de plantão, digo logo que não vou usa-lo :) A promessa é que depois (não sei quando) cada participante receberá também o Moto 360 (ficarei na torcida).
Uma coisa que foi impressionante pra mim foi o telão e a quantidade de pessoas (eu ouvi algo em torno de 6 mil).

Assisti palestras a tarde toda, e no final do primeiro dia, teve uma festinha bem legal. E o que é mais bacana nesse tipo de evento são as pessoas que você conhece. Conversei com um ex-gerente do NetFlix, com um TL do Hotel Urbano, uma designer do LinkedIn...
O segundo dia foi mais uma maratona de palestras, mas o que mais se falava era no novo visual/tema do Android: o Material. Muito bonito e consistente, ele foca muito em animações e promete trazer várias APIs (além do framework de animação existente desde o Honeycomb) para facilitar esse trabalho.

Um outro assunto recorrente eram os wearables, mais especificamente os relógios. Penso que o Google está investindo pesado e eu acho que a moda dos smartwatches vai mesmo pegar. Eles funcionam muito bem integrados com o smartphone, e inclusive o SDK já está disponível para download. Eu baixei o Android Studio 0.8 e já comecei a brincar com ele (já fiz até um app pro meu relógio :)

Ia esquecendo que não resisti e dei uma "tietada" em dois caras que eu sou muito fã: o Reto Meier e o Romain Guy. Se você mexe com Android e não conhece esses caras, bem... deveria conhecer :)
Bem pessoal, é isso. Quis resumir nesse post a história do meu primeiro Google I/O. Se vocês tiverem dúvidas sobre a viagem ou sobre o que aconteceu no evento, deixem seus comentários que eu vou editando aqui.

4br4ç05,
nglauber

segunda-feira, 19 de maio de 2014

Google Developer Experts

Olá povo,

Tenho a enorme satisfação de compartilhar com vocês que fui nomeado como primeiro Google Developer Experts (GDE) de Android da América Latina*. Esse é um programa mundial do Google que reconhece desenvolvedores especializados em tecnologias da empresa, e que compartilham seus conhecimentos com a comunidade. Mais informações sobre o programa podem ser obtidas em https://developers.google.com/experts/.

Estou muito feliz com esse reconhecimento do Google e quero continuar meu trabalho no C.E.S.A.R. e na Unibratec, bem como passar um pouco do meu conhecimento com os posts aqui do blog.


A lista com todos os GDEs pode ser visualizada em https://developers.google.com/experts/members/.

Aproveito esse post para dar outra boa notícia. Como vocês devem ter notado, faz um tempinho que não posto aqui no blog. O motivo é que estou escrevendo um livro sobre Android (estou divulgando aqui em primeira mão \o/ ) que será publicado pela editora Novatec. Ainda não tenho previsão concreta para o lançamento, mas assim que tiver novidades divulgarei aqui, no meu Twitter e no Google+.

4br4ç05,
nglauber

*Ninguém me disse isso, tomei essa conclusão olhando a lista dos GDEs :)

domingo, 23 de fevereiro de 2014

Google Cloud Messaging

Olá povo,

Depois de um bom tempo sem postar, resolvi voltar com um tópico muito bacana: Google Cloud Messaging, ou simplesmente GCM. Esse serviço gratuito permite que sua aplicação web envie mensagens de notificação para sua aplicação Android. Nesse tutorial vou tentar mostrar como aplicar esse recurso na sua aplicação.

Cada aplicação deve ter um ID associado. Com esse ID, a aplicação solicita um token para o servidor do GCM, esse token é único por dispositivo+aplicação. De posse desse token, a aplicação Android deve enviar esse valor para a aplicação servidora. Esse token deve ser armazenado de alguma forma no servidor, pois quando ele precisar/desejar enviar uma mensagem, o fará através de uma requisição via POST para o servidor do GCM que se encarregará de despachá-las para os dispositivos Android.
Nada muito complexo não é? :) Agora vamos para a prática!

Gerando as chaves de acesso

Acesse o Google Developers Console e crie um novo projeto. Precisaremos do número do projeto para podermos registrar a app+device no servidor do GCM.
Uma vez criado o projeto, selecione a opção APIs dentro do menu APIs & auth. Agora, habilite o Google Cloud Messaging.

Agora vamos gerar o SHA1 que será usado para gerar a chave para acessarmos o serviço do GCM. Digite o comando abaixo na pasta bin do JDK (Java Development Kit) (ou em qualquer lugar se esse caminho estiver no PATH do seu sistema operacional).

keytool -list -v -keystore ~/.android/debug.keystore

Aqui, estou usando o debug.keystore que por padrão fica no diretório usuário/.android (no Windows seria C:\Users\usuario). O próximo passo é criar duas chaves, uma para ser usada pela app Android e outra para app Web. Para isso, selecione a opção Credentials no menu APIs & auth. Clique na opção Create new key e selecione Android key. Na janela que for exibida, digite o SHA1 gerado pelo comando keytool, seguido de ";" e o pacote do projeto.
Exemplo:
41:F7:05:0F:72:29:99:28:55:BA:4B:79:AB:27:BC:7A:62:03:85:2C;ngvl.android.exemplogcm

Clique novamente em Create new key e selecione Server key. Aqui podemos filtrar o uso dessa chave por IP. Na prática utilizaríamos o IP real do servidor, mas para esse exemplo, não usaremos. É só confirmar para criar a nova chave.

Aplicação Cliente

Para a aplicação cliente primeiro temos que adicionar a referência ao projeto do Google Play Services, disponível na pasta ANDROID_SDK/extras/google/google_play_services/libproject. Depois adicione a dependência ao seu projeto.
No AndroidManifest.xml do seu projeto você deve adicionar as seguintes informações:
<manifest
  package="ngvl.android.exemplogcm"
  ...>
  <uses-permission android:name=
    "com.google.android.c2dm.permission.RECEIVE"/>
  <uses-permission android:name=
    "android.permission.INTERNET"/>
  <uses-permission android:name=
    "android.permission.GET_ACCOUNTS"/>
  <uses-permission android:name=
    "android.permission.WAKE_LOCK"/>

  <permission android:name=
    "ngvl.android.exemplogcm.permission.C2D_MESSAGE"
    android:protectionLevel="signature"/>

  <uses-permission android:name=
    "ngvl.android.exemplogcm.permission.C2D_MESSAGE"/>

  <application
    ...>

    ...

    <receiver 
      android:name=
        "ngvl.android.exemplogcm.GcmBroadcastReceiver"
      android:permission=
        "com.google.android.c2dm.permission.SEND">
      <intent-filter>
        <action 
          android:name=
            "com.google.android.c2dm.intent.RECEIVE"/>
        <category 
          android:name="ngvl.android.exemplogcm"/>
      </intent-filter>
    </receiver>

    <service 
      android:name=
        "ngvl.android.exemplogcm.GcmIntentService"/>
        
    <meta-data 
      android:name="com.google.android.gms.version"      
      android:value="@integer/google_play_services_version"/>
  </application>
</manifest>
Basicamente, adicionamos algumas permissões:
- Para receber mensagens do GCM;
- Acessar internet;
- Ter acesso às contas cadastradas do aparelho (para usar o GCM, temos que ter uma conta Google criada no aparelho);
- Wake Lock para ligar a tela quando chegar uma notificação;
Além disso, declaramos um Broadcast Receiver que recebe as mensagens e delega o tratamento para o Intent Service que também adicionamos.

Abaixo temos o código do BroadcastReceiver. Ele é um WakefulBroadcastReceiver para evitar que o aparelho apague a tela enquanto ele está executando, que nesse caso é receber a notificação. Quando uma mensagem GCM é recebida esse componente é disparado, e então delega o tratamento para o GcmIntentService.
public class GcmBroadcastReceiver 
  extends WakefulBroadcastReceiver {

  @Override
  public void onReceive(Context context, 
    Intent intent) {

    ComponentName comp = new ComponentName(
      context.getPackageName(),
      GcmIntentService.class.getName());

    startWakefulService(
      context, (intent.setComponent(comp)));

    setResultCode(Activity.RESULT_OK);
  }
}
O IntentService é um serviço especial do Android onde cada chamada ao mesmo é empilhada e executada sequencialmente em uma Thread separada. A requisição é tratada no método onHandleIntent e nele checamos se a mensagem que estamos recebendo é uma mensagem de dados (o GCM pode enviar mensagens de erro). Se for uma mensagem de dados, disparamos uma notificação.
public class GcmIntentService extends IntentService {
  public static final int NOTIFICATION_ID = 1;
  private NotificationManager mNotificationManager;
  private NotificationCompat.Builder builder;

  public GcmIntentService() {
    super("GcmIntentService");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    Bundle extras = intent.getExtras();
    GoogleCloudMessaging gcm = 
      GoogleCloudMessaging.getInstance(this);

    String messageType = gcm.getMessageType(intent);

    if (!extras.isEmpty()) {

      if (GoogleCloudMessaging.
          MESSAGE_TYPE_MESSAGE.equals(messageType)) {

          sendNotification("Mensagem: " + 
            extras.toString());
      }
    }
    GcmBroadcastReceiver.completeWakefulIntent(intent);
  }

  private void sendNotification(String msg) {
    mNotificationManager = (NotificationManager)
      this.getSystemService(
        Context.NOTIFICATION_SERVICE);

    PendingIntent contentIntent = 
      PendingIntent.getActivity(
        this, 0, new Intent(), 0);

    NotificationCompat.Builder mBuilder =
        new NotificationCompat.Builder(this)
      .setSmallIcon(R.drawable.ic_launcher)
      .setContentTitle("GCM Notification")
      .setStyle(new NotificationCompat.BigTextStyle()
      .bigText(msg))
      .setContentText(msg);

    mBuilder.setContentIntent(contentIntent);
    mNotificationManager.notify(
      NOTIFICATION_ID, mBuilder.build());
  }
}
Logo abaixo, criei uma classe utilitária para realizar as operações com o GCM. O método checkPlayService verifica se o google play está instalado, atualizado e se existe uma conta google criada no aparelho. Já o método getRegistrationId obtém o chave de acesso que o GCM retornou e que no nosso exemplo, estamos armazenando em uma SharedPreference. Isso está sendo feito no método storeRegistrationId.
O método registerInBackground tenta obter a chave de acesso do GCM usando uma AsyncTask. Ou seja, em uma Thread separada. Quando obtermos essa chave, a enviamos para nossa aplicação web e salvamos na SharedPreference.
Nesse exemplo, estamos enviando essa chave via requisição GET para o servidor. Obviamente, isso deve ser melhorado em uma aplicação de produção.
public class GcmHelper {

  public static final String 
    EXTRA_MESSAGE = "message";
  public static final String 
    PROPERTY_REG_ID = "registration_id";
    
  private static final String 
    PROPERTY_APP_VERSION = "appVersion";
  private static final String 
    SENDER_ID = "ID_DA_SUA_APP";
  private static final String 
    IP = "IP_DO_SERVIDOR";
 
  public static boolean checkPlayServices(
    Activity activity, int requestCode) {

    int resultCode = GooglePlayServicesUtil
       .isGooglePlayServicesAvailable(activity);

    if (resultCode != ConnectionResult.SUCCESS) {
      if (GooglePlayServicesUtil
          .isUserRecoverableError(resultCode)) {

        GooglePlayServicesUtil.getErrorDialog(
          resultCode, activity, requestCode).show();

      } else {
        Toast.makeText(activity, 
            "Dispositivo não suportado.", 
            Toast.LENGTH_SHORT).show();
        activity.finish();
      }
      return false;
    }
    return true;
  }
 
  public static String getRegistrationId(
    Context context) {

    final SharedPreferences prefs = 
      getGCMPreferences(context);

    String registrationId = 
      prefs.getString(PROPERTY_REG_ID, "");

    if (registrationId.isEmpty()) {
      return "";
    }
    int registeredVersion = 
      prefs.getInt(PROPERTY_APP_VERSION, 
        Integer.MIN_VALUE);

    int currentVersion = getAppVersion(context);
    if (registeredVersion != currentVersion) {
      return "";
    }
    return registrationId;
  }
 
  private static void storeRegistrationId(
    Context context, String regId) {

    final SharedPreferences prefs = 
      getGCMPreferences(context);

    int appVersion = getAppVersion(context);

    SharedPreferences.Editor editor = prefs.edit();
    editor.putString(PROPERTY_REG_ID, regId);
    editor.putInt(PROPERTY_APP_VERSION, appVersion);
    editor.commit();
  }

  public static void registerInBackground(
    final Context ctx, 
    final GoogleCloudMessaging gcm,
    final RegisterListener listener) {
  
    new AsyncTask<Void, Void, String>() {
      @Override
      protected String doInBackground(
        Void... params) {

        String regid = null;
        try {
          regid = gcm.register(SENDER_ID);

          sendRegistrationIdToBackend(regid);

          storeRegistrationId(ctx, regid);
                 
        } catch (IOException ex) {
          ex.printStackTrace();
        }
        return regid;
      }

      @Override
      protected void onPostExecute(String msg) {
        listener.onRegisterComplete(msg);
      }
    }.execute();
  }
 
  private static void sendRegistrationIdToBackend(
    String key) throws IOException {

    URL url = new URL(
      "http://"+ IP +"/SeuServer/registrar?token="+ key);

    HttpURLConnection conexao = 
      (HttpURLConnection)url.openConnection();
    conexao.connect();
  }
 
  private static SharedPreferences 
    getGCMPreferences(Context context) {

    return context.getSharedPreferences(
      "GDE", Context.MODE_PRIVATE);
  }
 
  private static int getAppVersion(Context context) {
    try {
      PackageInfo packageInfo = 
        context.getPackageManager()
          .getPackageInfo(context.getPackageName(), 0);
      return packageInfo.versionCode;
         
    } catch (NameNotFoundException e) {
      throw new RuntimeException(e);
    }
  }

  public interface RegisterListener {
    void onRegisterComplete(String s);
  }
}
A Activity né bem simples. Ela checa se o device suporta o Google Play (e se está atualizado), tenta obter a chave de acesso do GCM e depois a envia para o servidor.
public class ClientActivity extends Activity 
  implements RegisterListener {

  private static final int REQUEST_CODE_GOOGLEPLAY = 0;
 
  GoogleCloudMessaging gcm;
 
  String regid;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_client);
  
    if (GcmHelper.checkPlayServices(
        this, REQUEST_CODE_GOOGLEPLAY)) {

      gcm = GoogleCloudMessaging.getInstance(this);
      regid = GcmHelper.getRegistrationId(this);

      if (regid.isEmpty()) {
        GcmHelper.registerInBackground(this, gcm, this);
      }
    } else {
      Log.i("NGVL", "Google Play não instalado.");
    }
  }
  
  @Override
  public void onRegisterComplete(String s) {
    Toast.makeText(this, "Registered: "+ s, 
      Toast.LENGTH_LONG).show();
  }
}

Aplicação Servidora

Você pode usar qualquer tecnologia do lado do servidor para usar o GCM. Aqui vou usar um Servlet Java para receber o token do cliente Android e enviar mensagens para o mesmo. Aqui só estou salvando um device. Você terá que fazer a sua lógica para salvar vários.

@WebServlet("/gcm")
public class GcmServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  protected void doGet(
    HttpServletRequest request,
    HttpServletResponse response) 
      throws ServletException, IOException {

    PrintWriter writer = response.getWriter();
    writer.print("Current key:"+ retrieveToken());
  
    String key = request.getParameter("token");
    if (key != null && !key.isEmpty()){
      saveToken(key);
      response.getWriter().print("\nRegistrado:"+ key);
    }
  }

  protected void doPost(
    HttpServletRequest request,
    HttpServletResponse response) 
      throws ServletException, IOException {
  
    String msgJson = 
      "{ \"data\": { \"mensagem\":\""+ 
      request.getParameter("msg") +"\"}, "+
      "\"registration_ids\": [ \""+ retrieveToken() +
      "\" ] }";

    byte[] data = msgJson.getBytes("UTF-8");
  
    URL url = new URL(
      "https://android.googleapis.com/gcm/send");

    HttpsURLConnection conexao = 
      (HttpsURLConnection)url.openConnection();

    conexao.addRequestProperty("Authorization", 
      "key="+"sua_chave_web_aqui");
    conexao.addRequestProperty(
      "Content-Type", "application/json");
    conexao.setRequestMethod("POST");
    conexao.setDoOutput(true);
    conexao.setUseCaches(false);
    conexao.connect();
  
    OutputStream os = conexao.getOutputStream();
    os.write(data);
    os.close();
  
    PrintWriter writer = response.getWriter();
    writer.print("Trying to send: "+ msgJson);
  
    if (conexao.getResponseCode() == 
      HttpURLConnection.HTTP_OK){

      writer.print("\nDONE");
    } else {
      writer.print(
        "\nERRO:"+ conexao.getResponseCode() +" - "+ 
      conexao.getResponseMessage());
    }
  }

  private void saveToken(String key)
    throws IOException {

    String serverDir = 
      getServletContext().getRealPath("/");

    File f = new File(serverDir, "devices");
    if (!f.exists())
      f.createNewFile();

    FileOutputStream fos = new FileOutputStream(f);
    BufferedWriter out = new BufferedWriter(
      new FileWriter(f, true));
    out.write(key);

    out.close();
    fos.close();
  }
 
  private String retrieveToken() throws IOException{
    String serverDir = 
      getServletContext().getRealPath("/");

    File f = new File(serverDir, "devices");
    if (f.exists()){
      InputStream is = new FileInputStream(f);
      BufferedReader rd = new BufferedReader(
        new InputStreamReader(is));
   
      String line = rd.readLine();
      rd.close();
      is.close();
      return line;
    }
    return null;
  }
}
Para fins de exemplo, o método GET desse servidor está recebendo o token do cliente. Aqui só estou armazenando 1 única chave em um arquivo TXT, mas na prática, devemos armazenar isso em um banco de dados. Para enviar uma mensagem para o Android, basta abrir a página abaixo, digitar a mensagem e clicar em enviar.
<html>
<head>
<title>Exemplo GCM</title>
</head>
<body>
<form method="post" action="gcm">
  Mensagem:<input type="text" name="msg"><br>
  <input type="submit" value="OK">
</form>
</body>
</html>

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

quarta-feira, 13 de novembro de 2013

Google DevBus Brasil

Olá povo,

A Google está promovendo um evento denominado Developer Bus que já passou pelas cidades de Buenos Aires, Cidade do México e Bogotá. Agora chegou a vez do Brasil receber esse evento, que acontecerá nos próximos dias 21, 22 e 23 de Novembro em São Paulo.
Mais de 2200 pessoas entre Desenvolvedores, Designers e Gerentes de Projetos se inscreveram e passaram por 3 etapas de seleção, onde apenas 40 foram selecionadas pela equipe do Google para fazerem parte desse evento, que na verdade é uma mistura de Hackaton com Reality Show. Hackaton porque os selecionados terão que desenvolver soluções para PEM (Pequenas e Médias Empresas) da América Latina utilizando as tecnologias do Google. E reality por que será transmitido ao vivo via stream diretamente do evento.
Para conseguir o objetivo em tão pouco tempo, haverão mentores do Google que são especialistas em cada tecnologia auxiliando as equipes. Como premiação, a equipe vencedora irá para Mountain View conhecer o HQ do Google.

Eu estarei lá \o/ e quando voltar vou contar como foi a experiência de ser passageiro do #DevBusBrasil.

4br4ç05,
nglauber

Mais informações:
http://codigo-google.blogspot.com.br/2013/10/o-developer-bus-chega-ao-brasil.html
http://developerbus.withgoogle.com/sao-paulo/

segunda-feira, 12 de dezembro de 2011

Google Maps: Traçando Rotas

[EDITADO EM 03/04/2013: atualizado para Google Api v2]

Olá povo,

A API do Google Maps disponível para Android é muito bacana, e incorporar esse recurso na sua aplicação pode ser um bom atrativo. Em um dos projetos que trabalhei, precisava mostrar a rota entre dois pontos no mapa, e a Google disponibilizou em 27/07/12 uma API Web que retorna a rota entre duas coordenadas geográficas no formato JSON (ou XML se preferir).

Nesse arquivo, temos uma lista com as coordenadas geográficas que devem ser percorridas desde o ponto de partida até o destino. Com essas coordenadas lidas do JSON, criamos uma lista de LatLng que devem ser desenhadas no objeto GoogleMap através de uma PolyLine.

Não vou detalhar muito a configuração do Google Maps v2 no Android pois já falei nesse post aqui. Também não vou entrar em detalhes sobre leitura de JSON pois falei nesse post aqui. E se você tiver dúvidas sobre AsyncTask, dê uma olhada nesse post.

Vou começar pelo código da Activity mostrado abaixo. No onCreate pegamos a instância de GoogleMap e definimos a posição inicial no mapa e um zoom padrão. Em seguida iniciamos a AsyncTask passando a GoogleMap no construtor e informando a latitude e longitude de origem e destino.
public class TesteRotaMapaActivity 
  extends FragmentActivity {
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
  
    SupportMapFragment fragment = 
      (SupportMapFragment)
        getSupportFragmentManager()
          .findFragmentById(R.id.map);

    GoogleMap map = fragment.getMap();

    LatLng latLng = new LatLng(-8.102739,-34.89917);
  
    map.animateCamera(
      CameraUpdateFactory.newLatLngZoom(latLng, 12.0f));
  
    new RotaAsyncTask(this, map).execute(
      // Latitude, Logintude de Origem
      latLng.latitude, latLng.longitude,    
      // Latitude, Longitude de Destino
      -23.604262,-46.676513);  
    }
}

A classe RotaAsyncTask é quem faz o trabalho de acessar o site do Google Maps, baixar o JSON com a informação da rota.
public class RotaAsyncTask extends 
  AsyncTask<Double, Void, Void>{

  private ProgressDialog dialog;
  private GoogleMap mapView;
  private Context context;
  private Route rota;

  public RotaAsyncTask(Context ctx, GoogleMap mapa) {
    mapView = mapa;
    context = ctx;
  }
 
  @Override
  protected void onPreExecute() {
    super.onPreExecute();
    dialog = ProgressDialog.show(
      mapView.getContext(), "Aguarde", 
      "Calculando rota");
  }
 
  @Override
  protected Void doInBackground(Double... params) {

    rota = directions(
      new LatLng(params[0], params[1]), 
      new LatLng(params[2], params[3]));
    return null;
  }
 
  @Override
  protected void onPostExecute(Void result) {
    super.onPostExecute(result);
    PolylineOptions options = new PolylineOptions()
      .width(5)
      .color(Color.RED)
      .visible(true);

    for (LatLng latlng : rota.getPoints()) {
      options.add(latlng);
    }

    mapView.addPolyline(options);
    dialog.dismiss();
  }
 
  private Route directions(
    final LatLng start, final LatLng dest) {
  
    // Formatando a URL com a latitude e longitude  
    // de origem e destino.  
    String urlRota = String.format(Locale.US,
      "http://maps.googleapis.com/maps/api/"+
      "directions/json?origin=%f,%f&"+
      "destination=%f,%f&" +
      "sensor=true&mode=driving",   
      start.latitude, 
      start.longitude, 
      dest.latitude, 
      dest.longitude); 
  
    GoogleParser parser;
    parser = new GoogleParser(urlRota);
    return parser.parse();
  }
}

Quem viu o post anterior pode notar que houveram algumas modificações. Delegamos o parse do JSON para a classe GoogleParser e exibimos as rotas utilizando a classe PoluLineOptions (da API v2) com ajuda da classe Route. Essas novas classes foram extraídas desse post aqui. Graças ao comentário do Hadson Gomes.
Eu peguei essas classes e deixei apenas o necessário. Vejamos elas abaixo.
public class Route {
  private final List<LatLng> points;
  private String polyline;

  public Route() {
    points = new ArrayList<LatLng>();
  }

  public void addPoints(final List<LatLng> points) {
    this.points.addAll(points);
  }

  public List<LatLng> getPoints() {
    return points;
  }

  public void setPolyline(String polyline) {
    this.polyline = polyline;
  }

  public String getPolyline() {
    return polyline;
  }
}
Note que a classe acima tem uma propriedade chamada polyline. Nesse JSON, a rota retorna algumas coordenadas "macro" digamos assim. Por isso os comentários abaixo informando que eram traçadas linhas retas no caminho. A rota com as coordenadas mas precisas estão codificadas em base64 no atributo polyline. A classe GoogleParser faz essa conversão de base64 para GeoPoint conforme abaixo.
public class GoogleParser {

  protected URL feedUrl;

  public GoogleParser(String feedUrl) {
    try {
      this.feedUrl = new URL(feedUrl);
    } catch (MalformedURLException e) {
    }
  }
 
  public Route parse() {
    // Cria uma rota vazia
    final Route route = new Route();
    try {
      // Obtém a String do JSON
      final String result = 
        convertStreamToString(
          feedUrl.openConnection()
            .getInputStream());

      // Transforma a string em JSON
      JSONObject json = new JSONObject(result);
      // Pega a primeira rota retornada
      JSONObject jsonRoute = 
        json.getJSONArray("routes")
          .getJSONObject(0);
      JSONObject leg = jsonRoute
        .getJSONArray("legs").getJSONObject(0);

      // Obtém os passos do caminho
      JSONArray steps = leg.getJSONArray("steps");

      final int numSteps = steps.length();
      /*
       * Itera através dos passos, decodificando 
       * a polyline e adicionando à rota.
       */
      JSONObject step;
      for (int i = 0; i < numSteps; i++) {
        // Obtém o passo corrente
        step = steps.getJSONObject(i);
        // Decodifica a polyline e adiciona à rota
        route.addPoints(
          decodePolyLine(
            step.getJSONObject("polyline")
              .getString("points")));
      }
    } catch (Exception e) {
    }
    return route;
  }

  private String convertStreamToString(
    final InputStream input) {

    final BufferedReader reader = 
      new BufferedReader(
        new InputStreamReader(input));
    final StringBuilder sBuf = new StringBuilder();

    String line = null;
    try {
      while ((line = reader.readLine()) != null) {
        sBuf.append(line);
      }
    } catch (IOException e) {
    } finally {
      try {
        input.close();
      } catch (IOException e) {
      }
    }
    return sBuf.toString();
  }

  private List<LatLng> decodePolyLine(final String poly) {
    int len = poly.length();
    int index = 0;
    List<LatLng> decoded = 
      new ArrayList<LatLng>();

    int lat = 0;
    int lng = 0;

    while (index < len) {
      int b;
      int shift = 0;
      int result = 0;
      do {
        b = poly.charAt(index++) - 63;
        result |= (b & 0x1f) << shift;
        shift += 5;
      } while (b >= 0x20);
      int dlat = 
        ((result & 1) != 0 ? 
          ~(result >> 1) : 
          (result >> 1));
      lat += dlat;

      shift = 0;
      result = 0;
      do {
        b = poly.charAt(index++) - 63;
        result |= (b & 0x1f) << shift;
        shift += 5;
      } while (b >= 0x20);

      int dlng = ((result & 1) != 0 ? 
        ~(result >> 1) : 
        (result >> 1));
      lng += dlng;

      decoded.add(
        new LatLng(
          (float)(lat / 1E5),
          (float)(lng / 1E5)));
    }
    return decoded;
  }
}
Quem viu o post anterior deve lembrar que a classe RouteOverlay não existe mais. Quem faz seu papel é a classe PolyLine da nova versão da Google Maps Api.
A imagem abaixo mostra nossa aplicação em execução.



Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

segunda-feira, 21 de novembro de 2011

Google Maps e GPS

Olá povo,

Nesse post de 2009 mostrei como fazer um "Hello World" com a API do Google Maps. Agora vamos incrementar um pouco esse exemplo colocando uma imagem para indicar o seu local baseado na posição GPS.

Pré-requisitos
A configuração inicial para obter a chave e exibir o mapa na tela é idêntica a apresentada no post de dois anos atrás, mas caso não consiga gerar a chave, dê uma olhada nesse post aqui (Dica 5). Outra ressalva é que, caso você esteja usando o emulador para testar sua aplicação, certifique-se de que ele esteja usando a "Google APIs" no "Android AVD Manager" conforme a figura abaixo:



O código do Overlay
Uma coordenada geográfica é representada pela classe GeoPoint. Ela tem a latitude e longitude de um ponto no mapa. Entretanto essa classe não é visual, ou seja, ela não serve para enxergarmos o ponto no mapa. Quando queremos adicionar no mapa alguma indicação para uma coordenada geográfica, devemos utilizar a classe Overlay, que é uma subclasse de View. O código abaixo cria um Overlay que desenhará uma imagem em um dado ponto no mapa (representado por um GeoPoint).
public class MeuOverlay extends Overlay {

private Bitmap imagem;
private GeoPoint geopoint;

public MeuOverlay(Bitmap img ) {
imagem = img;
}

public void setGeopoint(GeoPoint geopoint) {
this.geopoint = geopoint;
}

@Override
public void draw(Canvas canvas, MapView mapView,
boolean shadow) {
super.draw(canvas, mapView, shadow);

if (geopoint != null){
Point pontoNaTela = mapView.getProjection()
.toPixels(geopoint, null);

canvas.drawBitmap(imagem,
pontoNaTela.x - (imagem.getWidth() / 2),
pontoNaTela.y - (imagem.getHeight()), null);
}
}
}

Essa classe tem dois atributos: um Bitmap e um GeoPoint. O Bitmap será a imagem que desenharemos para representar a o ponto no mapa; já o GeoPoint, como o próprio nome diz, é um ponto geográfico, que obteremos a partir da posição do GPS.
No método draw() é desenhado o ponto no mapa. Inicialmente checamos se o GeoPoint foi setado, em caso positivo, convertemos essa coordenada geográfica em um ponto na tela, que é representado pela classe Point. Quem faz essa conversão é a classe Projection obtida através do método getProjection() do MapView recebido como parâmetro do método. Depois, é só desenhar a imagem na tela. O cálculo feito na chamada do método é para que a imagem fique imediatamente acima do ponto geográfico.

O arquivo de layout
O arquivo de layout não tem nada de mais, apenas a declaração da MapView com a API key (veja no post de 2009 como obter a sua API key).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Exemplo GPS e MapActivity" />

<com.google.android.maps.MapView
android:id="@+id/mapa"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="SUA_API_KEY" />
</LinearLayout>

A Activity
O código abaixo representa a Activity da aplicação e está todo comentado e só vou fazer alguns comentários no final. A nossa classe herda de MapActivity e implementa a interface LocationListener, que é utilizada para receber as notificações do GPS.
public class Aula09Activity extends MapActivity
// Interface que tem os métodos para tratar
// os eventos do GPS
implements LocationListener {

private MapView mapa;
private MapController controller;
private MeuOverlay overlay;
private LocationManager locationManager;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// O LocationManager vai registrar e desregistrar
// a classe para ouvir eventos do GPS
locationManager = (LocationManager)
getSystemService(LOCATION_SERVICE);

// Configurando a MapView
mapa = (MapView)findViewById(R.id.mapa);
// Habilita os botões de Zoom
mapa.setBuiltInZoomControls(true);
// Mostra em modo satélite
mapa.setSatellite(true);

// O MapController 'controla' o mapa :)
controller = mapa.getController();
controller.setZoom(17);

// Cria a imagem que vai representar o Overlay
Bitmap marcador = BitmapFactory.decodeResource(
getResources(), R.drawable.ponto);

// Cria o Overlay e adiciona ao mapView
overlay = new MeuOverlay(marcador);
mapa.getOverlays().add(overlay);

// Determinando um ponto inicial
int latitude = (int)(-8.058698 * 1E6);
int longitude = (int)(-34.872129 * 1E6);

GeoPoint geopoint = new GeoPoint(latitude, longitude);
controller.setCenter(geopoint);
overlay.setGeopoint(geopoint);
}

@Override
protected void onResume() {
super.onResume();
// Registrando a Activity para receber notificações
// de mudança na posição GPS
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);

locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, this);
}

@Override
protected void onPause() {
super.onPause();
// Desregistrando a Activity para receber
// notificações de mudança na posição GPS
locationManager.removeUpdates(this);
}

@Override
protected boolean isRouteDisplayed() {
return false;
}

@Override
public void onLocationChanged(Location location) {
// Método chamado quando a posição GPS muda
int latitude = (int)(location.getLatitude() * 1E6);
int longitude = (int)(location.getLongitude() * 1E6);

GeoPoint geopoint = new GeoPoint(latitude, longitude);
overlay.setGeopoint(geopoint);
controller.animateTo(geopoint);
}

@Override
public void onProviderDisabled(String provider) {
// Método chamado quando o GPS é desabilitado
}

@Override
public void onProviderEnabled(String provider) {
// Método chamado quando o GPS é habilitado
}

@Override
public void onStatusChanged(String provider,
int status, Bundle extras) {
// Método chamado quando o status do GPS muda.
// Pode ser: OUT_OF_SERVICE,
// TEMPORARILY_UNAVAILABLE e AVAILABLE
}
}

No código acima fazemos algumas inicializações no método onCreate. A classe LocationManager é responsável por registrar uma classe para "ouvir" os eventos de GPS (definidos na interface LocationListener que nossa Activity está implementando). Para criar um GeoPoint devemos passar a latitude e longitude em microdegrees, para tal devemos multiplicar os valores por 1.000.000 ou em notação científica 1E6 (10 elevado a 6).
No método onResume registramos nossa classe para ouvir os eventos do GPS. O método requestLocationUpdates recebe como parâmetros:
- o provedor de informações de posição GPS: aqui podemos utilizar o GPS do telefone, que demora mais a obter os dados, porém eles são mais precisos; e o da rede de dados, que obtém a posição baseado no esquema de triangulação de antenas da operadora de telefonia;
- a distância mínima em metros na posição GPS;
- o intervalo de tempo em milisegundos para receber a atualização;
- o objeto de uma classe que implemente a interface LocationListener (no nosso caso, a própria Activity).
No método onPause, desregistramos nossa classe para não ouvir mais os eventos de GPS. Isso evitará que ao sair da aplicação o Android tente ficar enviando as coordenadas mesmo com a aplicação inativa.
O método onLocationChanged é o mais importante, pois a cada vez que a posição GPS muda, esse método é chamado pelo Android. A partir do objeto Location que vem como parâmetro, obtemos a latitude e longitude para atualizarmos a posição do Overlay.
AndroidManifest.xml
No manifest, adicionamos as permissões INTERNET e ACCESS_FINE_LOCATION para podermos utilizar o Google Maps e obter a posição GPS respectivamente. Outra informação relevante é a tag <uses-library> que adicionamos para usar a API do Google Maps, uma vez que ela não é "padrão" da plataforma Android.
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="br.edu.cesar.aula09"
android:versionCode="1"
android:versionName="1.0" >

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

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

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".Aula09Activity" >
<intent-filter >
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

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

Para testar o código da aplicação, você deve habilitar as opções de GPS no dispositivo através da opção Settings > Location & Security > My Location. Notem que temos duas opções nessa seção: GPS satélites e redes móveis. A primeira é mais precisa, porém demora mais; já a segunda obtém a posição baseada triangulação entre as distâncias das redes de telefonia ou wireless.

Mas se você estiver usando o emulador, use a aba "Emulator control" da perspectiva DDMS do Eclipse (para exibir, acesse Window > Open perspective > DDMS).

Digite uma coordena válida e clique em "Send". Para ober uma posição, vá no Google Maps, clique com o botão direito e selecione "O que há aqui? | What's here?". Na barra de busca, ficará a latitude e a longitude, aí é só copiar e testar.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

sexta-feira, 2 de setembro de 2011

Android: Dicas 4

Olá povo,

Segue mais um POST da série "dicas de Android".

Dica 1 - Obter o espaço disponível no aparelho

File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
long ocupedBlocks =
stat.getBlockCount() - availableBlocks;

String s1 = Formatter.formatFileSize(
this, availableBlocks * blockSize);
String s2 = Formatter.formatFileSize(
this, ocupedBlocks * blockSize);
System.out.println("---------------");
System.out.println("s = "+ s1);
System.out.println("s = "+ s2);

Dica 2 - Teclado Virtual Sobre o Layout
Ao exibir o teclado virtual, o Android automaticamente redimensiona a tela do aparelho para exibir todos os elementos. Então se você tiver, por exemplo uma barra com botões na parte inferior, eles irão aparecer imediatamente em cima do teclado virtual. Para evitar isso, basta colocar a seguinte configuração na declaração da sua Activity no AndroidManifest.xml.

<activity
android:name=".ui.MainActivity"
android:windowSoftInputMode="adjustPan"
/>

Dica 3 - Evitando abrir a mesma atividade 2 vezes
Os engenheiros de teste com frequência tentam "destruir" a aplicação de algum jeito :) E um bem comum é clicar rapidamente em um botão. Se esse botão chamar uma Activity serão abertas duas em sequência. Para evitar isso, basta colocar a seguinte configuração na declaração da sua Activity no AndroidManifest.xml.


<activity
android:name=".ui.MainActivity"
android:launchMode="singleTop"
/>

Isso faz com que só seja possível apenas uma instância no topo da pilha de atividades.

Dica 4 - Alterando a linha abaixo da TabWidget
Essa dica é um complemento da dica sobre como mudar a imagem das abas da TabWidget. O código abaixo mostra como mudar a linha que fica na parte inferior da TabWidget. Ela tem um aspecto interessante, pois a API só disponibilizou um método "digno" pra isso a partir do Android 2.2, para as versões anteriores, essa operação tem que ser feita via Reflection.


// Esse código deve ser chamado dentro de
// uma TabActivity
TabWidget tabWidget = getTabWidget();

if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.FROYO){
// For Android 2.2
tabWidget.setLeftStripDrawable(
R.drawable.barra_menu);
tabWidget.setRightStripDrawable(
R.drawable.barra_menu);
} else {
// For Android 2.1 or before
try {
Field stripLeft = tabWidget.getClass().
getDeclaredField("mBottomLeftStrip");
Field stripRight = tabWidget.getClass().
getDeclaredField("mBottomRightStrip");

if (!stripLeft.isAccessible())
stripLeft.setAccessible(true);
if (!stripRight.isAccessible())
stripRight.setAccessible(true);

stripLeft.set(tabWidget,
getResources().getDrawable(
R.drawable.barra_menu));

stripRight.set(tabWidget,
getResources().getDrawable(
R.drawable.barra_menu));

} catch (Exception e) {
e.printStackTrace();
}
}

Nesse exemplo, estou usando a API level 8 (Android 2.2) no Eclipse para poder ter acesso aos métodos setLeftStripDrawable e setRightStripDrawable. Porém ele pode ser executado em aparelhos 2.1, que neste caso executará o código via Reflection. Para dar suporte a API 2.1, mesmo desenvolvendo com a 2.2 basta colocar a tag abaixo no AndroidManifest.xml.

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

Dica 5 - Salvar estado do scroll da ListView
Na aplicação que estou desenvolvendo, sempre estava reatribuindo o Adapter à ListView no onResume da Activity. O problema era que eu clicava em um item da lista lá do final pra edita-lo era aberta a tela pra edição. Quando fechava essa tela, aí abria a tela de detalhes, mas quando voltava pra tela de listagem queria manter a posição do scroll.


// savedIndex e savedY é um atributo int

// Salvar estado
// Esse código coloquei no onPause
savedIndex = getListView().getFirstVisiblePosition();
View v1 = getListView().getChildAt(0);
savedY = (v1 == null) ? 0 : v1.getTop();

// Restaurando estado
// Esse código eu coloquei no onResume
setListAdapter(novoAdapter);
getListView().setSelectionFromTop(savedIndex, savedY);

Dica 6 - Checar se o GPS está habilitado
Existe basicamente duas formas de obter a posição geográfica no Android: por GPS e pela Rede de Dados. Essa configuração fica no menu Configurações > Local > Meu Local. Se sua aplicação usa geolocalização, você deve checar se uma dessas opções está habilitada.

public boolean isEnabled() {
LocationManager lm = (LocationManager)
context.getSystemService(
Context.LOCATION_SERVICE);

return
locationManager.isProviderEnabled(
LocationManager.GPS_PROVIDER) ||
locationManager.isProviderEnabled(
LocationManager.NETWORK_PROVIDER);
}
Dica 7 - Problema no GoogleMaps
Essa dica foi mandada pelo meu aluno da LinuxFI, Sávio (Sávio saviojp@gmail.com).
Durante a aula de Mapas eu passei pelo seguinte problema: Mesmo com todas as configurações da API Key e permissões definidas de modo correto, o meu emulador não carregava o mapa. Para solucionar esse problema fiz o seguinte:

Customizei a execução da aplicação no eclipse por meio do Run Configurations/Android Application /na aba Target no campo Additional Emulator Command Line Options, setei o comando: -dns-server 8.8.8.8.

4br4ç05,
nglauber

segunda-feira, 15 de agosto de 2011

Google compra Motorola Mobility


Olá povo,

Você chega na segunda de manhã e se depara com uma notícia que balança o mundo mobile: a compra da Motorola Mobility pela Google.
Não tô afim de escrever muito, mas vou colocar minha opinião pessoal. Achei essa aquisição bastante oportuna e ao meu ver até esperada. A Apple tem seu próprio SO (iOS) e fabrica seu hardware; a Nokia fecha parceria com a Microsoft para o Windows Phone; nada mais justo do que a Google se juntar com alguém, e com alguém que apostou muito no Android como a Motorola.

Aí pode vir a pergunta: e porque não a HTC ou a Samsung? Na minha opinião, pode ser porque esses dois fabricantes em especial "dão tiros pra todo lado". Digo isso porque podemos encontrar celulares Samsung com Bada, e da HTC com Windows, além do próprio sistema da Google. Então eu creio que eles continuarão assim, diversificando seus produtos.

Outro ponto importante é que o Google/Android/Fabricantes estavam sendo processados por diversos concorrentes por questões de patentes, e em muitos casos, perdendo essas disputas na justiça. Com a compra da Motorola, o Google amplia seu leque de patentes e protege a si e aos parceiros (fabricantes) que adotam o Android, uma vez que a Google reiterou sua posição de deixa-lo Open Source.

Sendo assim, acho que isso fortalecerá o Android e aquecerá ainda mais o mercado mobile. E nós, como desenvolvedores, devemos estar preparados para onde quer que o vento leve este barco.
E vocês, o que acham? Deixem seus comentários.

4br4ç05,
nglauber

*imagem Gizmodo Brasil

segunda-feira, 4 de julho de 2011

Google Cloud Print + Android

Olá povo,

O post de hoje tem todos os créditos do meu colega Felipe Vasconcelos, que precisou adicionar a funcionalidade de imprimir um documento através de um aparelho Android.
A solução utilizada por ele foi o Google Cloud Print (http://www.google.com/cloudprint), um serviço que permite com que você compartilhe a(s) impressora(s) conectadas à sua máquina através da sua conta do Google.


O serviço está disponível apenas para máquinas Windows, por enquanto. Para utiliza-lo, é necessário fazer o download do Chrome, em seguida, habilitar o serviço. Para tal, basta seguir esse tutorial (do próprio Google).

Após habilitar o serviço, basta entrar no site do cloud print e "automagicamente" as impressoras que estão conectadas à sua máquina estarão "nas núvens", prontas para receber impressões.

Para imprimir documentos a partir da sua aplicação Android, basta adicionar os arquivos print_dialog.xml e PrintDialog.Activity.java ao seu projeto Android (na pasta res/layout e src respectivamente). Não esqueça de declarar a Activity e adicionar a permissão android.permission.INTERNET ao AndroidManifest.xml. Esses arquivos são do próprio Google (veja aqui).

Agora, para imprimir um arquivo TXT que esteja no SDCard, por exemplo, basta utilizar o código abaixo:


Intent printIntent =
new Intent(this, PrintDialogActivity.class);

printIntent.setDataAndType(
Uri.parse("file:///mnt/sdcard/arquivo.txt"),
"text/plain");

// Job da impressora
printIntent.putExtra("title", "Meu arquivo");
startActivity(printIntent);
Como vc deve imaginar, se fosse imprimir outro tipo de arquivo, bastaria mudar o MIME type.
Será exibida a tela abaixo. Faça o login no aparelho Android, selecione uma impressora, clique em "Print" e pronto!





Parabéns Felipe! Grande dica. E qualquer dúvida, falem com ele :)

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

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