sexta-feira, 23 de dezembro de 2011

Agradecimento

Olá povo,

Esse será o último post de 2011. Um ano fantástico para mim, Graças a Deus! Consegui atingir vários objetivos, e por isso resolvi agradecer a todos que acreditaram em mim e no meu trabalho.

Primeiramente ao CESAR, onde trabalho desde 2006, e me dá a oportunidade de aprender muito todos os dias. Mexer com diversas tecnologias e em um ambiente diferenciado, são coisas que me faz gostar e querer continuar nesse time fantástico.

Um agradecimento Especial a Especializa :) onde dei os primeiros paços como instrutor. Foi uma grande escola, e continua sendo uma satisfação ministrar cursos em uma instituição que tem como meta, formar especialistas com cursos de qualidade.


A LinuxFi me permitiu conhecer a bela capital da Paraíba. E tive a satisfação de ministrar o primeiro curso de Android de João Pessoa. Depois tive a oportunidade de voltar, e espero voltar mais vezes :)


Após terminar o mestrado no CESAR.edu, tive a felicidade de iniciar minha carreira como professor universitário na própria instituição. Dando aulas de Java ME e Android na pós-graduação em Tecnologias para Desenvolvimento de Aplicações Móveis, aprendi muito com meus alunos, e isso gerou muitos posts aqui do blog :)


Esse é o meu mais novo desafio. Esse ano eu comecei a ministrar a disciplina de Programação Móvel no curso de Análise e Desenvolvimento de Software (ADS) na Unibratec. Totalmente diferente de cursos isolados e de pós-graduação, está sendo bem gratificante e desafiador.

Venho escrevendo matérias para as revistas Java Magazine e Mobile Magazine da DevMedia. Essa parceria tem me ajudado a escrever melhor (inclusive aqui pro blog) e falar melhor também durante as aulas. Além é claro de me forçar a estudar para enriquecer as aulas.

Para finalizar, agradeço a todos vocês. Colegas de trabalho, Alunos, Leitores do Blog e/ou dos meus artigos. Espero contar com todos vocês em 2012.

8045 F35745!

4br4ç05,
nglauber

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, 5 de dezembro de 2011

Resoluções de Tela

Olá povo,

Frequentemente nas aulas de Android o pessoal tem dificuldade de lembrar as diversas resoluções de tela disponíveis para os smartphones e tablets atuais. Hoje meu colega de trabalho André mandou uma imagem com os tamanhos e siglas que as representam.

No Android, a grande maioria dos dispositivos utilizam as resoluções: QVGA, HVGA, WVGA e WXGA.

4br4ç05,
nglauber

Fonte: http://samuelvarela.wordpress.com/2009/03/20/resolucao-de-tela-vga-qvga-etc/