terça-feira, 25 de novembro de 2014

Android - Trabalhando com Google Maps

Como marcar pontos em um mapa com a API do Google Maps? Como traçar rotas? Como customizar marcadores?

Sem mais enrolação, vamos ao passo-a-passo!

1) Este tutorial é uma continuação do tutorial Android - Iniciando Com Google Maps encontrado AQUI. Se você tem dúvidas de como incluir a API do Google Maps em sua aplicação, como adquirir a assinatura digital de sua aplicação, etc, volte e siga a primeira parte deste tutorial AQUI. Se você já incluiu o mapa em sua aplicação e apenas tem dúvidas de como utilizá-lo, siga para o passo 2) deste tutorial.

2) Como estamos continuando o tutorial anterior vale ressaltar nossa Activity está vazia e que nosso arquivo de layout contém apenas o fragmento referente ao mapa:

 
<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"  
   tools:context="com.jonathan.projetomapas.ProjetoMapasActivity" >  
   
   <fragment  
     android:id="@+id/mapa"  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     android:name="com.google.android.gms.maps.MapFragment"/>  
   
 </RelativeLayout>  


Sendo assim, o próximo passo é vincular o nosso componente de tela referente ao mapa com um objeto em nossa Activity para que seja possível manipulá-lo conforme desejamos. Para isso, edite sua Activity criando um objeto do tipo "com.google.android.gms.maps.GoogleMap" e recuperando um "GoogleMap" de nosso fragmento que incluímos em nosso arquivo de layout anteriormente. Para melhor visualização e manutenção do código, eu criei um método para inicializar os componentes da tela e o invoquei no método "onCreate" de nossa Activity. Segue código de como ficou nossa Activity:

 
package com.jonathan.projetomapas;  
   
import android.app.Activity;  
import android.os.Bundle;  
import com.google.android.gms.maps.GoogleMap;  
import com.google.android.gms.maps.MapFragment;  
   
   
public class ProjetoMapasActivity extends Activity {  
        
    private GoogleMap mapa;  
        
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
         setContentView(R.layout.activity_projeto_mapas);  
       
         inicializarComponentes();  
    }  
     
    private void inicializarComponentes(){  
         mapa = ((MapFragment) getFragmentManager().findFragmentById(R.id.mapa)).getMap();  
    }  
}  
   



3) Adicionando marcadores.
Adicionar marcadores em seu mapa é uma tarefa simples, você precisa apenas de uma latitude e longitude do local que você quer marcar em seu mapa. Para efeito de aprendizado irei abrir o Google Maps e digitar um endereço qualquer para recuperar sua longitude e latitude. Nesse caso utilizarei o endereço da Google do Brasil que fica localizada na Av. Brigadeiro Faria Lima, 3477, São Paulo. Sua latitude e longitude é -23.586328 e -46.682012 respectivamente. Agora que temos a localização, criaremos um objeto do tipo "com.google.android.gms.maps.model.LatLng" com as coordenadas que recuperamos:

 
private final LatLng cordGoogleSp = new LatLng(-23.586328, -46.682012);  


Feito isso, no método "inicializarComponentes", logo após inicializar o nosso atributo "mapa", adicionaremos este marcador com a seguinte linha de código:

 
mapa.addMarker(new MarkerOptions().position(cordGoogleSp)); 
 



Marcador adicionado! Simples, não?

4) Zoom e posicionamento em animação.
Nós adicionamos o marcador porém não demos o zoom necessário no mapa para visualizarmos onde realmente está posicionado o nosso marcador e também não deixamos o nosso marcador centralizado na tela. Faremos isso agora de forma "animada". Utilizaremos o método "animateCamera", porém se você quiser realizar a mesma tarefa de forma não animada, utilize o método "moveCamera". O método "animateCamera" que utilizaremos, recebe 3 parâmetros: Um objeto do tipo CameraUpdate, um inteiro representado a duração da animação em milissegundos e um Objeto do tipo CancelableCallback responsável por tratar os evento disparado quando tal animação é cancelada.
Como iremos atualizar o posicionamento da "câmera" de nosso mapa, criaremos um objeto do tipo "com.google.android.gms.maps.model.CameraPosition" da seguinte forma:

 
CameraPosition update = new CameraPosition(cordGoogleSp, 15, 0, 0); 
 

Onde o primeiro parâmetro é a coordenada que será colocada no centro de nossa tela do mapa, o segundo parâmetro é o nível de zoom que daremos no mapa, o terceiro é o "tilt" que seria um efeito de rotação na vertical de até 90º e o quarto é o "bearing" que seria um efeito de rotação na horizontal em até 360º. No nosso caso, assumimos que a coordenada é a mesma que utilizamos anteriormente onde adicionamos o marcador e o zoom será de nível 15. Não utilizaremos Tilt nem Bearing.
Feito isso agora podemos utilizar o método animateCamera. Usaremos a classe "com.google.android.gms.maps.CameraUpdateFactory" para criar um objeto CameraUpdate a partir de nosso CameraPosition.

 
mapa.animateCamera(CameraUpdateFactory.newCameraPosition(update), 5000, null); 
 

Definimos que ao abrir nosso mapa em nossa aplicação, o mesmo será direcionado para as coordenadas estabelecidas e dará um zoom de nível 15. Isso de forma "animada" com duração de 3 segundos! Segue como ficou o código:

 
package com.jonathan.projetomapas;  
   
import android.app.Activity;  
import android.os.Bundle;  
   
import com.google.android.gms.maps.CameraUpdateFactory;  
import com.google.android.gms.maps.GoogleMap;  
import com.google.android.gms.maps.MapFragment;  
import com.google.android.gms.maps.model.CameraPosition;  
import com.google.android.gms.maps.model.LatLng;  
import com.google.android.gms.maps.model.MarkerOptions;  
   
   
public class ProjetoMapasActivity extends Activity {  
        
     private GoogleMap mapa;  
     private final LatLng cordGoogleSp = new LatLng(-23.586328, -46.682012);  
        
     @Override  
     protected void onCreate(Bundle savedInstanceState) {  
          super.onCreate(savedInstanceState);  
          setContentView(R.layout.activity_projeto_mapas);  
       
          inicializarComponentes();  
     }  
     
     private void inicializarComponentes(){  
          mapa = ((MapFragment) getFragmentManager().findFragmentById(R.id.mapa)).getMap();  
          mapa.addMarker(new MarkerOptions().position(cordGoogleSp));  
          
          CameraPosition update = new CameraPosition(cordGoogleSp, 15, 0, 0);  
          mapa.animateCamera(CameraUpdateFactory.newCameraPosition(update), 3000, null);  
     }  
}  

5) Estruturando o código.
Antes de avançarmos nosso estudo sobre o funcionamento do Google Maps API, iremos "organizar" melhor o nosso código/aplicativo. Iremos colocar um item de menu em nossa "ActionBar" para cada item aprendido neste tutorial. Sendo assim, precisamos fazer algumas alterações em nosso código.

* A - Nossa Activity deverá estender de ActionBarActivity ao invés de Activity;
* B - Criar o arquivo ".xml" referente ao arquivo de menu;
* C - Sobrescrever os métodos "onCreateOptionsMenu" e "onOptionsItemSelected";

Vamos lá!

* A - Apenas substitua Activity por ActionBarActivity na declaração de sua Activity:

 
public class ProjetoMapasActivity extends ActionBarActivity { 
 

* B - Dentro da pasta "/res/menu" (se não existir crie-a), crie um arquivo do tipo XML com o seguinte código:

 
<menu xmlns:android="http://schemas.android.com/apk/res/android"  
   xmlns:tools="http://schemas.android.com/tools">  
   
   <item  
     android:id="@+id/action_fixe_location"  
     android:orderInCategory="100"  
     android:showAsAction="never"  
     android:title="Localização Fixa"/>  
     
   <item  
     android:id="@+id/action_my_location"  
     android:orderInCategory="100"  
     android:showAsAction="never"  
     android:title="Minha Localização"/>  
     
   <item  
     android:id="@+id/action_map_types"  
     android:orderInCategory="100"  
     android:showAsAction="never"  
     android:title="Tipos de Mapa"/>  
     
   <item  
     android:id="@+id/action_personalized_mark"  
     android:orderInCategory="100"  
     android:showAsAction="never"  
     android:title="Marcador Personalizado"/>  
     
   <item
     android:id="@+id/action_personalized_infowindow"
     android:orderInCategory="100"
     android:showAsAction="never"
     android:title="Balão de Info Personalizado"/>

   <item  
     android:id="@+id/action_map_rote"  
     android:orderInCategory="100"  
     android:showAsAction="never"  
     android:title="Traçar Rota"/>  
 </menu> 
 

* C - Adicione o seguinte trecho de código em sua Activity para manipular os itens de menu que criamos acima:

  
   @Override  
   public boolean onCreateOptionsMenu(Menu menu) {  
        //Constrói nosso menu na ActionBar de nossa aplicação  
        getMenuInflater().inflate(R.menu.projeto_mapas, menu);  
        return super.onCreateOptionsMenu(menu);  
   }  
     
   @Override  
   public boolean onOptionsItemSelected(MenuItem item) {  
        switch (item.getItemId()) {  
            case R.id.action_fixe_location:  
                 //QUANDO A OPÇÃO 'LOCALIZAÇÃO FIXA' FOR CLICADA  
                 break;  
            case R.id.action_my_location:  
                 //QUANDO A OPÇÃO 'MINHA LOCALIZAÇÃO' FOR CLICADA  
                 break;  
            case R.id.action_map_types:  
                 //QUANDO A OPÇÃO 'TIPOS DE MAPA' FOR CLICADA  
                 break;  
            case R.id.action_personalized_mark:  
                 //QUANDO A OPÇÃO 'MARCADOR PERSONALIZADO' FOR CLICADA  
                 break;
            case R.id.action_personalized_infowindow:  
                 //QUANDO A OPÇÃO 'BALÃO DE INFO PERSONALIZADO' FOR CLICADA  
                 break;
            case R.id.action_map_rote:  
                 //QUANDO A OPÇÃO 'TRAÇAR ROTA' FOR CLICADA  
                 break;  
        }  
        return super.onOptionsItemSelected(item);  
   }  


A primeira opção de nosso menu (Localização Fixa) já foi criada no passo anterior deste tutorial, sendo assim, mova o código que antes colocamos no método "inicializarComponentes" referentes à adição do marcador e à animação de posicionamento e zoom para onde comentamos "QUANDO A OPÇÃO 'LOCALIZAÇÃO FIXA' FOR CLICADA". Apenas adicionaremos mais uma linha de código no início do "case" para limpar os marcadores anteriormente posicionados. Segue código completo da Activity depois das configurações:

 
 package com.jonathan.projetomapas;  
   
 import android.os.Bundle;  
 import android.support.v7.app.ActionBarActivity;  
 import android.view.Menu;  
 import android.view.MenuItem;  
   
 import com.google.android.gms.maps.CameraUpdateFactory;  
 import com.google.android.gms.maps.GoogleMap;  
 import com.google.android.gms.maps.MapFragment;  
 import com.google.android.gms.maps.model.CameraPosition;  
 import com.google.android.gms.maps.model.LatLng;  
 import com.google.android.gms.maps.model.MarkerOptions;  
   
   
 public class ProjetoMapasActivity extends ActionBarActivity {  
        
      private GoogleMap mapa;         
      private final LatLng cordGoogleSp = new LatLng(-23.586328, -46.682012);  
        
      @Override  
      protected void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
         setContentView(R.layout.activity_projeto_mapas);  
       
         inicializarComponentes();  
      }  
     
      private void inicializarComponentes(){  
         mapa = ((MapFragment) getFragmentManager().findFragmentById(R.id.mapa)).getMap();  
      }  
     
      @Override  
      public boolean onCreateOptionsMenu(Menu menu) {  
         //Constrói nosso menu na ActionBar de nossa aplicação  
         getMenuInflater().inflate(R.menu.projeto_mapas, menu);  
         return super.onCreateOptionsMenu(menu);  
      }  
     
      @Override  
      public boolean onOptionsItemSelected(MenuItem item) {  
         switch (item.getItemId()) {  
             case R.id.action_fixe_location:  
                  mapa.clear();
                  mapa.addMarker(new MarkerOptions().position(cordGoogleSp));  
                  CameraPosition update = new CameraPosition(cordGoogleSp, 15, 0, 0);  
                  mapa.animateCamera(CameraUpdateFactory.newCameraPosition(update), 3000, null);  
                    
                     break;  
             case R.id.action_my_location:  
                  //QUANDO A OPÇÃO 'MINHA LOCALIZAÇÃO' FOR CLICADA  
                     break;  
             case R.id.action_map_types:  
                  //QUANDO A OPÇÃO 'TIPOS DE MAPA' FOR CLICADA  
                     break;  
             case R.id.action_personalized_mark: 
                  //QUANDO A OPÇÃO 'MARCADOR PERSONALIZADO' FOR CLICADA  
                     break;  
             case R.id.action_map_rote:  
                  //QUANDO A OPÇÃO 'TRAÇAR ROTA' FOR CLICADA  
                     break;  
          }  
          return super.onOptionsItemSelected(item);  
      }  
 }  
     


6) Recuperando minha localização.
A primeira coisa que vamos fazer ao clicarmos na opção "Minha Localização" do menu é limpar toda a configuração atual do mapa (não limpa o zoom, tilt ou bearing, porém, se por exemplo dermos mais 15 de zoom, o mesmo não é adicional ao zoom já configurado). Para isso, como primeira instrução no "case" referente à esta opção no menu, faremos o seguinte:

 
 mapa.clear();  


O processo de recuperar nossa localização da melhor forma possível exige alguns passos. Para facilitar a leitura do tutorial e minimizar o tempo de aprendizado, comentei cada passo que devemos realizar. Segue código do "case" de "Minha Localização" comentado:

 
    case R.id.action_my_location:  
         //Limpamos o mapa atual retirando marcadores, elipses, etc.  
         mapa.clear();  
        
         //Ativamos a identificação de nossa localização no google maps (pontinho azul no mapa)  
         mapa.setMyLocationEnabled(true);  
   
         //Criamos um 'Listener' para tratar o evento disparado todas as vezes que a localização é alterada  
         mapa.setOnMyLocationChangeListener(new OnMyLocationChangeListener() {  
              @Override  
              public void onMyLocationChange(Location minhaLocalizacao) {  
                   //Limpamos novamente o mapa atual retirando marcadores, elipses, etc.  
                   mapa.clear();  
                                 
                   //Criamos o abjeto LatLng a partir de nossa localizacao  
                   LatLng latLngMinhaLocalizacao = new LatLng(minhaLocalizacao.getLatitude(),   
                                                            minhaLocalizacao.getLongitude());  
                        
                   //Adicionamos o marcador no mapa para a nossa localização
                   mapa.addMarker(new MarkerOptions().position(latLngMinhaLocalizacao));  
                        
                   //Repetimos o processo de animação para a nossa posição no mapa  
                   CameraPosition updateMinhaLocalizacao = new CameraPosition(latLngMinhaLocalizacao, 15, 0, 0);  
                   mapa.animateCamera(CameraUpdateFactory.newCameraPosition(updateMinhaLocalizacao), 3000, null);  
              }  
         });  
   break;  


Já que adicionamos um "Listener" que irá criar a nossa "animação" todas as vezes que a localização for alterada, temos que lembrar de tirar esse "Listener" nas outras opções de nosso menu. Adicione a seguinte linha no inicio de todas as outras opções que temos no menu (menos no item referente aos "tipos de mapa" pois nesse item não mudaremos a localização no mapa):

 
 mapa.setOnMyLocationChangeListener(null);  
   



7) Mudando o tipo do mapa.
Mudar o tipo de mapa é uma tarefa simples e fácil. Como utilizaremos apenas um botão (item de menu) para mudar o tipo do mapa para os quatro tipos (Híbrido, Terreno, Satélite, Normal), apenas faremos uma verificação de qual o tipo atual e mudaremos para o próximo tipo respeitando a sequencia citada acima. Para isso utilizaremos o método "setMapType" para alterar o tipo de mapa e o método "getMapType" para verificar qual o tipo atual do mapa.

 
case R.id.action_map_types:  
                       
    if(mapa.getMapType() == GoogleMap.MAP_TYPE_HYBRID){  
        mapa.setMapType(GoogleMap.MAP_TYPE_TERRAIN);  
                            
    }else if(mapa.getMapType() == GoogleMap.MAP_TYPE_TERRAIN){  
        mapa.setMapType(GoogleMap.MAP_TYPE_SATELLITE);  
                            
    }else if(mapa.getMapType() == GoogleMap.MAP_TYPE_SATELLITE){  
        mapa.setMapType(GoogleMap.MAP_TYPE_NORMAL);  
                            
    }else{  
        mapa.setMapType(GoogleMap.MAP_TYPE_HYBRID);  
    }  
break; 
 

Os tipos de mapa a seguir são Hibrido, Satélite e Terreno respectivamente:



8) Customizando marcadores.
Podemos customizar os marcadores que colocamos em nosso mapa adicionando títulos, descrição, alterando cores, colocando ícones, etc. Para este exemplo, irei colocar um marcador em meu endereço residencial atual e customizarei este marcador.
Primeiro, vá ao Google Maps e recupere a latitude e longitude de sua residência. Feito isso, crie um atributo em sua Activity do tipo "LatLng" com esses valores (já fizemos isso antes):

 
 private final LatLng cordMyHome = new LatLng(-23.555997,-46.481778);  


Como já definimos anteriormente, retiramos o "Listener" que manipula a mudança de localização de nosso mapa:

 
 mapa.setOnMyLocationChangeListener(null);  
   

Limpe os marcadores colocados no mapa anteriormente:

 
 mapa.clear(); 
 

Agora crie o marcador adicionando-o ao seu mapa:

 
 Marker meuMarcadorMarker = mapa.addMarker(new MarkerOptions().position(cordMyHome));
  

Adicione um título e descrição de sua preferência ao seu marcador através dos métodos "setTitle" e "setSnippet" para configurar o título e a descrição respectivamente:

 
 meuMarcadorMarker.setTitle("Minha Casa");  
 meuMarcadorMarker.setSnippet("Esta é minha residência..."); 
 

Para alterar o ícone do marcador, recupere o ícone que deseja (o ícone que coloquei pode ser encontrado AQUI), e o coloque na pasta "/res/drawable-hdpi" de sua aplicação. Não esqueça de redimensionar a imagem para o tamanho adequado (no caso da imagem que utilizei eu a redimensionei para 40x39 pixels e de renomea-la para que o nome da mesma não contem caracteres especiais nem letras maiúsculas). Depois de colocá-la na pasta indicada, compile seu aplicativo e adicione a seguinte linha na personalização de seu marcador:

 
 meuMarcadorMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.home_icon_custom));  


Onde "home_icon_custom" será o nome que você deu à sua imagem e a classe R é a do seu projeto, não a do pacote do Android.
Tornamos a nossa "Janela de Informações" (balão onde fica o título e descrição do marcador) visível por padrão. Caso queira que esta janela fique visível apenas com o clique no marcador ignore esta linha de código.

 
 meuMarcadorMarker.showInfoWindow();  
   

Repetimos a animação para o local que indicamos no mapa:

 
 CameraPosition updateMinhaCasa = new CameraPosition(cordMyHome, 15, 0, 0);  
 mapa.animateCamera(CameraUpdateFactory.newCameraPosition(updateMinhaCasa), 3000, null);  




9) Customizando 'InfoWindow' (balão de informações do marcador).
Para customizar o balão de informações do marcador precisaremos de um "Adapter". Na declaração de nossa Activity iremos definir que a mesma irá implementar a interface "com.google.android.gms.maps.GoogleMap.InfoWindowAdapter":

 
 public class ProjetoMapasActivity extends ActionBarActivity implements InfoWindowAdapter 
 

Com isso, temos que implementar os seguintes métodos:

   
 @Override  
 public View getInfoContents(Marker arg0) {  
             
      return null;  
 }  
   
 @Override  
 public View getInfoWindow(Marker arg0) {  
             
      return null;  
 }  
 

O primeiro método a ser invocado será o "getInfoWindow" que se retornado "null" então será invocado o método "getInfoContents" que por sua vez, se retornado "null" então o balão de informações padrão será utilizado.
A diferença é que o "getInfoWindow" permite a você oferecer uma view para ser utilizada. Já o método "getInfoContent" permite a você customizar os conteúdos do balão porém sem alterar o seu frame e seu background.
Utilizaremos a segunda opção que é mais simples para efeito de aprendizado.

Crie um arquivo XML de Layout para o seu balão de informações dentro da pasta "/res/layout/" do mesmo modo que é feito com Layouts referentes às Activities. Neste exemplo criei um arquivo chamado "infowindow_map_custom" com o seguinte layout:

 <?xml version="1.0" encoding="utf-8"?>  
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="match_parent"  
   android:layout_height="wrap_content"   
   android:padding="20dp"  
   android:background="@color/wallet_holo_blue_light">  
   
   <ImageView  
     android:id="@+id/infowindow_imageview_imagem"  
     android:layout_width="40dp"  
     android:layout_height="40dp"  
     android:layout_alignParentLeft="true"  
     android:layout_alignParentTop="true"  
     android:src="@drawable/home_icon_custom" />  
   
   <LinearLayout   
     android:layout_width="fill_parent"  
     android:layout_height="wrap_content"  
     android:orientation="vertical"  
     android:layout_toRightOf="@+id/infowindow_imageview_imagem" 
     android:gravity="center"  
     android:layout_alignBottom="@+id/infowindow_imageview_imagem"  
     android:layout_alignTop="@+id/infowindow_imageview_imagem">  
       
     <TextView  
          android:id="@+id/infowindow_textview_titulo"  
          android:layout_width="wrap_content"  
          android:layout_height="wrap_content"  
          android:gravity="center"  
          android:text="Título"  
          android:textSize="15sp"  
          android:textStyle="bold"  
          android:textColor="@android:color/black"/>  
        
        <TextView  
          android:id="@+id/infowindow_textview_descricao"  
          android:layout_width="wrap_content"  
          android:layout_height="wrap_content"  
          android:text="Descrição aqui..."  
          android:textSize="12sp"  
          android:textStyle="italic"  
          android:textColor="@android:color/black"/>  
   </LinearLayout>  
 </RelativeLayout>
  

Crie um atributo em sua Activity do tipo "View" da seguinte forma:

 
 private View markerInfo; 
 

Implementamos o método "getInfoContents" recuperando os dados de nosso marcador e exibindo-os em nossa view personalizada:

 
 @Override
 public View getInfoContents(Marker arg0) {
     //Inflamos o nosso arquivo de layout em uma view
     markerInfo = getLayoutInflater().inflate(R.layout.infowindow_map_custom, null);
  
     //Configuramos o tamanho de nossa view
     markerInfo.setLayoutParams(new RelativeLayout.LayoutParams(200, RelativeLayout.LayoutParams.WRAP_CONTENT));
  
     //Vinculamos objetos com os componentes de tela em nossa view
     TextView titulo = (TextView) markerInfo.findViewById(R.id.infowindow_textview_titulo);
     TextView descricao = (TextView) markerInfo.findViewById(R.id.infowindow_textview_descricao);
  
     //Recuperamos os valores de nosso marcador e o colocamos em nossa view
     titulo.setText(arg0.getTitle());
     descricao.setText(arg0.getSnippet());
  
     //Retornamos para a tela a nossa view personalizada
     return markerInfo;
 }


Agora no "case" referente à opção de "Balão de Info Personalizado" configuramos o nosso Adapter em nosso mapa e repetimos o processo feito no "case" de "Marcador Personalizado" apenas para termos uma posição no mapa com informações no marcador:

 
 case R.id.action_personalized_infowindow:  
                       
     //Configuramos nosso adaptador em nosso mapa  
     mapa.setInfoWindowAdapter(this);  
                       
     //Repitimos o processo do passo anterior apenas para termos um marcador com informações a mostrar  
     mapa.setOnMyLocationChangeListener(null);  
     mapa.clear();  
                       
     Marker marcadorMinhaCasa = mapa.addMarker(new MarkerOptions().position(cordMyHome));  
                       
     marcadorMinhaCasa.setTitle("Minha Casa");  
     marcadorMinhaCasa.setSnippet("Esta é minha residência...");  
     marcadorMinhaCasa.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.home_icon_custom));  
     marcadorMinhaCasa.showInfoWindow();  
                       
     CameraPosition updateMyHome = new CameraPosition(cordMyHome, 15, 0, 0);  
     mapa.animateCamera(CameraUpdateFactory.newCameraPosition(updateMyHome), 3000, null);  
 break;
  

Agora, em todos os outros "cases" (menos o case de Tipos de mapa) devemos limpar esta configuração de Adapter que colocamos, assim como limpamos a configuração do Listener anteriormente. Para isso adicione a seguinte linha no inicio dos outros "cases":

 
 mapa.setInfoWindowAdapter(null);  


Resultado do nosso "balão de informações" do marcador personalizado:



10) Traçando rotas.
Para traçarmos uma rota em nosso mapa utilizaremos o serviço The Google Directions API onde faremos uma requisição HTTP e o mesmo nos retornará um JSON com o trajeto a ser "pintado".
Crie um método em sua Activity que recebe as latitudes e longitudes de origem e destino. O mesmo será responsável por montar a URL de chamada ao serviço. Segue código:

 
 private String montarURLRotaMapa(double latOrigen, double lngOrigen, double latDestino, double lngDestino){  
     //Base da URL  
     String url = "http://maps.googleapis.com/maps/api/directions/json?origin=";  
     //Local de origem  
     url += latOrigen + "," + lngOrigen;  
     url += "&destination=";  
     //Local de destino  
     url += latDestino + "," + lngDestino;  
     //Outros parametros  
     url += "&sensor=false&mode=driving&alternatives=true";  
       
     return url;  
 }  


Agora desenvolveremos o método que fará a chamada ao serviço The Google Directions API através desta URL e converterá o resultado em JSON. Segue código comentado:

 
 public JSONObject requisicaoHTTP(String url) {  
     JSONObject resultado = null;  
             
     try {  
          //Criamos um cliente HTTP para que possamos realizar a   
          //requisição a um Servidor HTTP  
          DefaultHttpClient httpClient = new DefaultHttpClient();  
          //Definimos o método de requisição como sendo POST  
          HttpPost httpPost = new HttpPost(url);  
          //Recuperamos a resposta do Servidor HTTP  
          HttpResponse httpResponse = httpClient.execute(httpPost);  
          //Recuperamos o conteúdo enviado do Servidor HTTP  
          HttpEntity httpEntity = httpResponse.getEntity();  
          //Transformamos tal conteúdo em 'String'  
          String conteudo = EntityUtils.toString(httpEntity);   
         
          //Transformamos a 'String' do conteúdo em Objeto JSON  
          resultado = new JSONObject(conteudo);  
         
     } catch (UnsupportedEncodingException e) {  
          Log.e("ProjetoMapas", e.getMessage());  
     } catch (ClientProtocolException e) {  
          Log.e("ProjetoMapas", e.getMessage());  
     } catch (IOException e) {  
          Log.e("ProjetoMapas", e.getMessage());  
     } catch (JSONException e) {  
          Log.e("ProjetoMapas", e.getMessage());  
     }  
       
     //Retornamos o conteúdo em formato JSON  
     return resultado;  
 } 
 

Desenvolvemos também o método que irá literalmente "pintar o caminho" entre os dois pontos que escolheremos em nosso mapa. Segue código comentado:

 
 public void pintarCaminho(JSONObject json) {  
     try {  
         //Recupera a lista de possíveis rotas  
         JSONArray listaRotas = json.getJSONArray("routes");  
         //Para efeito de aprendizado iremos utilizar apenas a primeira opção  
         JSONObject rota = listaRotas.getJSONObject(0);  
         //Recuperamos os pontos a serem pintados para que surga a 'linha' no mapa  
         String pontosPintar = rota.getJSONObject("overview_polyline").getString("points");  
         //Recuperamos a lista de latitudes e longitudes para sabermos exatamente onde pintar  
         List<LatLng> listaCordenadas = extrairLatLngDaRota(pontosPintar);  
   
         //Percorremos por cada cordenada obtida  
         for(int ponto = 0; ponto < listaCordenadas.size()-1 ; ponto++){  
             //Definimos o ponto atual como origem  
             LatLng pontoOrigem= listaCordenadas.get(ponto);  
             //Definimos o próximo ponto como destino  
             LatLng pontoDestino= listaCordenadas.get(ponto + 1);  
             //Criamos um objeto do tipo PolylineOption para adicionarmos os pontos de origem e destino  
             PolylineOptions opcoesDaLinha = new PolylineOptions();  
             //Adicionamos os pontos de origem e destino da linha que vamos traçar  
             opcoesDaLinha.add(new LatLng(pontoOrigem.latitude, pontoOrigem.longitude),   
                    new LatLng(pontoDestino.latitude,  pontoDestino.longitude));  
             //Criamos a linha de acordo com as opções que configuramos acima e adicionamos em nosso mapa  
             Polyline line = mapa.addPolyline(opcoesDaLinha);  
             //Mudamos algumas propriedades da linha que acabamos de adicionar em nosso mapa  
             line.setWidth(5);  
             line.setColor(Color.BLUE);  
             line.setGeodesic(true);  
         }  
     }   
     catch (JSONException e) {  
         Log.e("ProjetoMapas", e.getMessage());  
     }  
 }   


Percebe-se que invocamos uma função chamada "extrairLatLngDaRota" que ainda não existe. Como o nome já é bastante intuitivo, esta função será responsável por decodificar e nos retornar a lista de Longitudes e Latitudes da rota que será traçada. A API retornará para nós algo parecido com isso:

 
~wwnCxmuzG{GSDg@HyASWiAE?j@BLNLb@JH@Iz@E|@AlBJrBb@dDF`A?dAIpAGb@e@~Ak@`CUlBQvBy@vPYnFEh@CREBUJ}ChAqAh@i@f@[l@i@tAuBbHk@fAoBrCaAlAsCdEcDfEQT]n@Wj@e@rAOp@e@jCMpBGpAMlAm@xCa@dCYtAs@jByCrGoF~LuA|C_AfBaD~EyCjEaApAqBzCuChEoAfBwDxFmBtCu@`AoBtCqAhBkBlBmFzFk@j@eAp@s@Z}Bv@mEdBaF|BUFaBPY@LvALhALjBFhDJ`FXjFf@hITvEBzACvAMhCa@~FIzD@`DFnA`@`EfAfK\xB\lBXnBZtCLfAV|AZ`BVfBXhCLjBPdCt@|GfAnMh@bFLxBPxAjAvHdCxRt@dFVrAtAjMf@nE`BrNdBrN~@rHlAbLLnCJjAvAjNz@nHN~@tAnFp@rCd@hCXfBPfBrAnKPv@lAvDf@vBZzBTvBZbDN|@n@tE`@nDzBdRPbCT~AnBfPpAbLjBdOTtCLjAR`Az@fDp@rBdAbCrE|JpBhEfA~BdBjD~DrIbEpJ`AzBhDlHnArCd@nAb@tAf@`Cj@tCFp@Dv@EvBm@pMg@pM[~DmApQM|BSdE[zE@r@F^Vt@`@j@rAv@|C`BzDbBnF~Bp@`@h@j@b@dARz@Dz@LrFV~IDNJ|CJnJ?|@I~AOzBORIf@UpAe@|EMjAGHSHKAOEyCyBYQCGGUBULIP?bANV?tBn@j@N~@V|HzBlCx@|@\rA^l@b@~BnAzAl@|@V`AP|C^hBHdADlDR|Ch@lE`@v@@nBKtBKfCBxDVxCNbALzBHlITvDJfGZ`@Bj@J`AR^PZVRVvBlD`D|F|JxOtB|CtCfElDrF\h@fBnCfAnBXf@B\@HbAnBr@bAdAlAbAbBj@rATxAZnB^zAp@|A~@zAf@r@pCzDzHrLRf@dAxET|ABlBKdBa@|D_@xEKhASlAo@hBeAfBeBrCSHEHQTaBjCc@pAMj@Ed@Ch@EpE@jADz@|@fE|@vCZ`AfBvD^n@t@bAp@t@|BzBlDhD@PjAhA^nAN~@jAjH~@`FdA~FJr@o@Lq@LkDn@yA`@cCb@_C`@mDn@ 
 

Para decodificar isso utilizaremos um código bem famoso e facilmente encontrado. Não me atreverei a comentá-lo para não acabar interpretando de maneira errônea e ensinando besteiras (rsrs).
Segue código:

 
 private List<LatLng> extrairLatLngDaRota(String pontosPintar) {  
     List<LatLng> listaResult = new ArrayList<LatLng>();  
     int index = 0, len = pontosPintar.length();  
     int lat = 0, lng = 0;  
    
     while (index < len) {  
               
          int b, shift = 0, result = 0;  
          do {  
               b = pontosPintar.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 = pontosPintar.charAt(index++) - 63;  
               result |= (b & 0x1f) << shift;  
               shift += 5;  
          } while (b >= 0x20);  
            
          int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));  
          lng += dlng;  
   
          LatLng p = new LatLng( (((double) lat / 1E5)),  
               (((double) lng / 1E5) ));  
          listaResult.add(p);  
     }  
   
     return listaResult;  
 }  


Estamos com "quase" toda a estrutura montada para ser testada. Sabemos que nada relacionado à tráfego de rede pode ser disparado diretamente na Thread Main, sendo assim, usaremos uma "AsyncTask" para efetuarmos a requisição HTTP que estruturamos anteriormente. Para saber mais sobre como funciona a AsyncTask clique aqui.
Crie uma classe private que seja inner (aninhada) em sua classe. Para isso, simplesmente declare uma nova classe dentro de sua Activity:

 
 private class MinhaAsyncTask extends AsyncTask<String, Void, JSONObject>{  

 }


Onde o primeiro parâmetro (String) será o tipo de parâmetro que nossa tarefa assíncrona irá precisar para ser executada, o segundo (Void) será o tipo de parâmetro que indicará qual o status em nível de completude a nossa tarefa se encontra (não utilizaremos neste tutorial) e o terceiro (JSONObject), de qual tipo será o retorno de nosso método principal.
Toda AsyncTask precisa que ao menos um método seja implementado, o "doInBackground" onde (no nosso caso) começará tudo. Sobrescreva este método com a seguinte assinatura:

 
 @Override  
 protected JSONObject doInBackground(String... params) {  
            
      return null;  
 }  


Sobrescreva também o método "onPostExecute" que será invocado automaticamente logo após o retorno do método acima. Segue assinatura:

 
 @Override  
 protected void onPostExecute(JSONObject result) {  
            
       super.onPostExecute(result);    
 }  

Em nosso método "doInBackground" iremos apenas realizar a requisição HTTP e retornar o resultado de nossa requisição:


 @Override  
 protected JSONObject doInBackground(String... params) {  
      JSONObject resultJSON = requisicaoHTTP(params[0]);  
            
      return resultJSON;  
 }  


E em nosso método "onPostExecute" apenas faremos a verificação se o resultado de nossa requisição não é nulo e caso não seja, chamaremos a função responsável por pintar a nossa rota em nosso mapa:

 
 @Override  
 protected void onPostExecute(JSONObject resultadoRequisicao) {  
    super.onPostExecute(resultadoRequisicao);    
      
    if(resultadoRequisicao != null){  
       pintarCaminho(resultadoRequisicao);  
    }  
 }  


Nossa AsyncTask completa:

 
 private class MinhaAsyncTask extends AsyncTask<String, Void, JSONObject>{  
   
     @Override  
     protected JSONObject doInBackground(String... params) {  
       JSONObject resultJSON = requisicaoHTTP(params[0]);  
         
       return resultJSON;  
     }  
          
     @Override  
     protected void onPostExecute(JSONObject resultadoRequisicao) {  
       super.onPostExecute(resultadoRequisicao);    
      
       if(resultadoRequisicao != null){  
         pintarCaminho(resultadoRequisicao);  
       }  
     }  
 }  


Feito isso, vamos executar nossa tarefa assíncrona no clique de nosso botão "Traçar Rota" do menu que definimos. Dependendo de qual versão de API do Android estamos usando a forma de executar uma AsyncTask muda. Segue código comentado referente à ação a ser executada pelo clique do botão "Traçar Rota":

 
 case R.id.action_map_rote:
      //Limpamos quaisquer configurações anteriores em nosso mapa
      mapa.setInfoWindowAdapter(null);
      mapa.setOnMyLocationChangeListener(null);
      mapa.clear();  

      //Recuperamos a URL passando as cordenadas de origem como sendo a cordenada que definimos   
      //para a nossa residência e as coordenadas de destino como sendo a do escritório da Google em SP.  
      String url = montarURLRotaMapa(cordMyHome.latitude, cordMyHome.longitude, cordGoogleSp.latitude, cordGoogleSp.longitude);  
      //Criamos uma instância de nossa AsyncTask (para cada requisição deverá ser criada uma nova instância).  
      MinhaAsyncTask tarefa = new MinhaAsyncTask();  
                       
      //Se a versão de SDK do Android do aparelho que está executando o aplicativo for menor que a HONEYCOMB (11)  
      if(Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB){  
          //Executa a tarefa passando a URL recuperada  
          tarefa.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);  
      }else{  
          //Executa a tarefa passando a URL recuperada  
          tarefa.execute(url);  
      }  
      
      //Adicionamos um marcador no ponto de partida e no ponto de destino
      mapa.addMarker(new MarkerOptions().position(cordMyHome));
      mapa.addMarker(new MarkerOptions().position(cordGoogleSp));

      //Animação para visualizar ponto de partida
      CameraPosition updateRota = new CameraPosition(cordMyHome, 15, 0, 0);
      mapa.animateCamera(CameraUpdateFactory.newCameraPosition(updateRota), 3000, null);
              
 break;  


Execute o código e terá a rota de "sua residência" ao escritório da Google em SP traçada no mapa em seu dispositivo!



É isso aí pessoal!! Agora você está apto a trabalhar com a API do Google Maps para Android! Aprendemos a adicionar marcadores, personalizar marcadores, personalizar o "balão de informações" do marcador, a recuperar sua localização, traçar rotas, etc..

Se você seguiu todo o passo-a-passo você terá em mãos um aplicativo com um mapa e suas principais ações!! Até o próximo!!

terça-feira, 21 de outubro de 2014

JAVA WEB - Hibernate + JPA + MySQL + JBoss AS 7 - Configuração

Como incluir o Hibernate em seu projeto WEB? Como configurá-lo com o JBoss AS 7 utilizando o MySQL como Banco de Dados?
Estou tendo exatamente esta necessidade neste exato momento e não estou encontrando um tutorial simples e didático o suficiente para configurar meu projeto com tais tecnologias de forma rápida e fácil, sendo assim, vou "mastigar" os trechos de tutoriais que encontrei por aí e vou colocar aqui pra você o que absorvi em forma de um SIMPLES passo-a-passo! Vamos lá?

1) Você primeiramente precisará ter o MySql e seu Connector instalado em sua máquina (ou qualquer outro Banco de Dados que queira utilizar).

Clique aqui para baixar o MySQL e depois o instale (Next, Next, Next... rsrsrs porém guarde a senha que você irá fornecer durante a instalação. Iremos precisar dela);
Clique aqui para baixar o MySQL Connector/J e depois o instale (Next, Next, Next... rsrsrsrs).
Clique aqui para baixar o arquivo ".jar" referente ao conector. Usaremos ele mais adiante.

2) Depois de baixar e instalar as ferramentas de Banco de Dados agora precisamos das bibliotecas do Hibernate e do JPA (já vêm inclusas dentro do Hibernate).

Clique aqui para baixar o Hibernate ORM (Model de Objeto Relacional).

3) Você precisará também do Servidor de Aplicação JBoss 7. Baixe e descompacte a versão "JBoss AS 7.1.1.Final".

4) Iremos considerar que você já tem um projeto WEB e quer apenas configurar/adicionar tais tecnologias, PORÉM, se você está criando um projeto também neste exato momento, pode acessar este nosso tutorial onde criamos um projeto WEB do zero para criar seu projeto e depois siga pra o passo 5) deste tutorial. Clique aqui para acessar o tutorial.

5) Descompacte e abra a pasta do Hibernate que você baixou e copie os arquivos ".jar" da pasta "hibernate-release-4.3.6.Final/lib/required" para a pasta "WebContent/WEB-INF/lib" (se esta pasta não existir, crie-a!). Feito isso, clique com o botão direito do mouse em cima do seu projeto, posicione sobre "Build Path" e selecione "Configure Build Path...":


Clique em "Add JARs...", selecione os arquivos que você copiou para a pasta "WebContent/WEB-INF/lib" e clique em "OK":


Clique em "OK".

6) Dentro do diretório do JBoss AS 7 que baixamos anteriormente, na pasta "/modules/com/" crie uma pasta chamada "mysql" e dentro desta pasta crie uma outra chamada "main". Seguindo essa estrutura, dentro da pasta "/modules/com/mysql/main/" crie um arquivo XML chamado "module.xml" com o seguinte conteúdo:

 <?xml version="1.0" encoding="UTF-8"?>  
 <module xmlns="urn:jboss:module:1.1" name="com.mysql">  
   <resources>  
     <resource-root path="mysql-connector-java-5.1.15-bin.jar"/>  
   </resources>  
   <dependencies>  
     <module name="javax.api"/>  
     <module name="javax.transaction.api"/>  
     <module name="javax.servlet.api" optional="true"/>  
   </dependencies>  
 </module>  

Salve o arquivo e dentro da mesma pasta coloque o "mysql-connector-java-5.1.15-bin.jar" que baixamos anteriormente. Note que o arquivo é referenciado no XML acima então o mesmo deve conter exatamente este nome, ou então você pode alterar o XML acima com o nome do arquivo que foi baixado.

7) Abra o arquivo "module.xml" localizado dentro da pasta do JBoss AS 7 no caminho "modules/org/hibernate/main" e adicione dentro da sessão de <dependencies> o seguinte trecho:

 
<module name="com.mysql"/>  
 

O arquivo ficará assim:

 
<module xmlns="urn:jboss:module:1.1" name="org.hibernate">  
   <resources>  
     <resource-root path="hibernate-core-4.0.1.Final.jar"/>  
     <resource-root path="hibernate-commons-annotations-4.0.1.Final.jar"/>  
     <resource-root path="hibernate-entitymanager-4.0.1.Final.jar"/>  
     <resource-root path="hibernate-infinispan-4.0.1.Final.jar"/>  
   </resources>  
   
   <dependencies>  
        
           <module name="com.mysql"/>  
             
     <module name="asm.asm"/>  
     <module name="javax.api"/>  
     <module name="javax.persistence.api"/>  
     <module name="javax.transaction.api"/>  
     <module name="javax.validation.api"/>  
     <module name="org.antlr"/>  
     <module name="org.apache.commons.collections"/>  
     <module name="org.dom4j"/>  
     <module name="org.infinispan" optional="true"/>  
     <module name="org.javassist"/>  
     <module name="org.jboss.as.jpa.hibernate" slot="4" optional="true"/>  
     <module name="org.jboss.logging"/>  
     <module name="org.hibernate.envers" services="import" optional="true"/>  
   </dependencies>  
 </module>  


8) Agora temos que criar nossa Base de Dados antes de continuarmos com as configurações. Essa tarefa é simples. Ao chegar neste ponto você já deve ter instalado o MySQL em seu computador (se ainda não o fez, faça rsrs), sendo assim, abra o MySQL 5.6 Command Line Client, ele irá pedir uma senha que é a mesma que você utilizou no momento da instalação. Forneça a senha e em seguida digite o comando "create database nome_que_vc_quiser_para_o_banco ;" e dê "enter". No meu caso, para este tutorial, utilizarei o nome "ZapZap", deverá aparecer o seguinte comando indicando que a base de dados foi criada:


9) Agora iremos criar a conexão entre o JBoss e sua Base de Dados MySQL. Para isso, abra o arquivo "standalone.xml" localizado na pasta "/standalone/configuration/" do JBoss. Localize dentro do arquivo a tag "<datasources>" (no plural) e logo abaixo dela insira o seguinte código:

 
<datasource jta="false" jndi-name="java:jboss/datasources/zapzap_datasource"   
               pool-name="zapzap_datasource" enabled="true" use-ccm="false">  
    <connection-url>jdbc:mysql://localhost:3306/ZapZap</connection-url>  
    <driver-class>com.mysql.jdbc.Driver</driver-class>  
    <driver>mysql</driver>  
    <security>  
        <user-name>root</user-name>  
        <password>root</password>  
    </security>  
</datasource>
  

Trocando "ZapZap" pelo nome que você deu na criação da sua Base de Dados e "zapzap_datasource" por um nome qualquer que identifique esta conexão (lembrando que este "nome qualquer" será utilizado mais tarde).
Ainda dentro da sessão "<datasources>" (no plural rsrs), você encontrará a sessão "<drivers>" (também no plural), dentro dela, coloque o seguinte trecho de código:

 
<driver name="mysql" module="com.mysql">  
    <xa-datasource-class>com.mysql.jdbc.Driver</xa-datasource-class>  
</driver>
  

10) Toda a configuração na parte do JBoss está concluída, agora vamos ao projeto. Crie um arquivo chamado "persistence.xml" contendo o código abaixo e salve-o no diretório "/src/META-INF/" dentro do diretório principal de seu projeto (se o diretório "META-INF" não existir, crie-o. Se o mesmo estiver em outra localização, mova-o).

 
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence  
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">  
   
      <persistence-unit name="zapzap" transaction-type="RESOURCE_LOCAL">  
           <provider>org.hibernate.ejb.HibernatePersistence</provider>
           <non-jta-data-source>java:jboss/datasources/zapzap_datasource</non-jta-data-source>  
          
           <class>com.jonathan.zapzapws.util.hibernate.EntityManagerUtil</class>  
           <class>com.jonathan.zapzapws.model.CriticasSugestoesModel</class>  
             
           <properties>  
                <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />  
                <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />  
                <property name="hibernate.format_sql" value="true" />  
                <property name="hibernate.show_sql" value="true" />  
                <property name="hibernate.hbm2ddl.auto" value="update" />  
           </properties>  
   </persistence-unit>  
 </persistence>
  

Faça as seguintes alterações:

* Na tag "persistence-unit" altere o valor do atributo "name" de "zapzap" para qualquer outro identificador que você queira, sabendo que utilizaremos este identificador mais tarde;

* O valor da tag "non-jta-data-source" será o caminho do data-source que definimos no arquivo "standalone.xml" do JBoss no passo 9) deste tutorial.

* As tags "class" que inserimos serve para mapear as classes que se utilizarão do contexto Hibernate/JPA para injeção do EntityManager e para mapeamento das entidades do Banco de Dados. A classe EntityManagerUtil será utilizada para fornecer o EntityManager em nosso projeto e a classe CriticasSugestoesModel será utilizada para testar nossa configuração criando uma tabela e inserindo dados na base de dados. (criaremos essas classes no próximo passo).

* A propriedade "hibernate.transaction.jta.platform" tem o valor padrão pra quando se está utilizando o JBoss como Servidor de Aplicação e gerenciador de transações. Mude para o equivalente ao Servidor de Aplicação que você está usando.

* A propriedade "hibernate.hbm2ddl.auto" está definido como "update" (que atualiza o banco de dados automaticamente de acordo com as entidades criadas no código) apenas para testarmos. Em desenvolvimento eu desencorajo fortemente o seu uso e encorajo definir o valor para "validate" que valida as entidades definidas no código de acordo com o banco.

11) Crie uma classe chamada "EntityManagerUtil" que será responsável por fornecer o EntityManager para nossa aplicação. Segue o código simples da classe:

 
 public class EntityManagerUtil {  
   
      /** entity */  
      private static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("zapzap");  
   
      /** @return the entity */  
      public static EntityManager getEntity() {  
           return entityManagerFactory.createEntityManager();  
      }  
 }  


Crie uma classe chamada "CriticasSugestoesModel" que será nossa entidade de testes do banco de dados. Segue o código da classe:


 //Indica que é uma entidade do banco de dados 
 @Entity  
 //Define o nome da tabela do banco correspondente
 @Table(name="criticas_sugestoes")  
 public class CriticasSugestoesModel{  
   
      /** id */
      //Indica que está é a chave primária da tabela  
      @Id 
      //Nome da coluna correspondente
      @Column(name="id")  
      private Long id;  
        
      /** texto */
      //Nome da coluna correspondente
      @Column(name="texto")  
      private String texto;  
        
      @Override  
      public String toString() {  
           return this.userId.toString();  
      }  
        
      @Override  
      public boolean equals(Object obj) {  
           if(obj != null && obj instanceof CriticasSugestoesModel){  
                if(((CriticasSugestoesModel)obj).getId() != null && ((CriticasSugestoesModel)obj).getId().equals(this.id)){  
                     return true;  
                }  
           }  
             
           return false;  
      }  
        
        
        
        
      /**  
       * @return the id  
       */  
      public Long getId() {  
           return id;  
      }  
   
      /**  
       * @param id the id to set  
       */  
      public void setId(Long id) {  
           this.id = id;  
      }  
   
      /**  
       * @return the texto  
       */  
      public String getTexto() {  
           return texto;  
      }  
   
      /**  
       * @param texto the texto to set  
       */  
      public void setTexto(String texto) {  
           this.texto = texto;  
      }  
 }  
   

Altere seu arquivo "persistence.xml" com o caminho correto das classes criadas acima.

12) Pronto! Para testar sua configuração inicie seu Servidor de Aplicação, execute seu projeto e utilize o seguinte trecho de código:

     
     CriticasSugestoesModel critica = new CriticasSugestoesModel();  
     EntityManager em = EntityManagerUtil.getEntity();  
             
      em.getTransaction().begin();  
             
      try {  
           critica.setTexto("Testando");  
           critica.setId(new Long(1));  
                  
           em.persist(critica);  
           em.getTransaction().commit();  
      } catch (Exception e) {  
           em.getTransaction().rollback();  
      }            
      em.close();  
   




É isso aí!! Se tudo ocorreu bem e você seguiu todo o passo-a-passo, certamente terá em mãos uma aplicação WEB configurada para utilizar os frameworks Hibernate e JPA com Base de Dados MySQL gerenciados em um Servidor de Aplicação JBoss!

domingo, 12 de outubro de 2014

Android - Iniciando com Google Maps

Como incluir mapas em minha aplicação? O que devo fazer para trabalhar com o Google Maps? Como faço para traçar rotas e customizar marcadores no mapa? Parece ser simples, mas não é! Resumindo, nós iremos instalar e configurar o Google Play Services SDK, registrar seu aplicativo no Google Developers APIs Console para obter a chave da API de sua aplicação, configurar o arquivo "AndroidManifest.xml" de acordo com o exigido pela Google, e adicionar o mapa em sua aplicação!

Sem mais enrolação, vamos ao passo-a-passo!


1) Neste tutorial vamos considerar que você já tem uma aplicação e quer apenas incluir mapas nela, sendo assim, não irei abordar a criação de uma nova aplicação. Caso você ainda não tenha uma aplicação e pretende criar uma nova aplicação neste momento para utilização do Google Maps, você pode seguir o nosso tutorial de como criar uma nova aplicação Android aqui. Também estaremos considerando que você está utilizando o ADT Eclipse IDE para desenvolver, que já vem com o SDK Android. Se você não está utilizando o ADT Eclipse IDE, você pode fazer o download e instalação seguindo o tutorial da própria Google para isso.

2) O Google Maps Android é disponibilizado como parte do Google Play Service SDK, sendo assim, vamos adicionar o Google Play Services como biblioteca em nossa aplicação. Para isso, clique com o botão direito do mouse na área livre do Package Explorer e selecione "import..." como mostra a imagem abaixo:


Feito isso, na próxima tela que se segue, expanda a opção Android e selecione "Existing Android Code Into Workspace" como mostrado abaixo:



Agora indique o caminho da biblioteca que fica dentro da pasta do SDK Android que vem junto com o ADT Eclipse ou que você baixou separadamente no passo 1) deste tutorial. A biblioteca do Google Play Services fica no diretorio "sdk/extras/google/google_play_services/libproject":



Feito isso, a biblioteca aparecerá "checkada" na wisard. Certifique-se de marcar a opção "Copy projects into worspace" e clique em "Finish":



A biblioteca deverá ter sido importada e agora estará sendo mostrada no Package Explorer junto com seu projeto. Agora para utilizar esta biblioteca dentro do seu projeto é necessário referenciá-la. Para isso, clique com o botão direito em seu projeto e selecione "Properties":



Nas opções do menu da esquerda escolha Android e desça a barra de rolagem até o fim para mostrar a opção de adicionar bibliotecas externas. Clique em "Add...":



Na tela que irá abrir, selecione a opção "google-play-services_lib" e clique em OK:



A biblioteca deverá ser mostrada na lista de bibliotecas externas utilizadas em seu projeto. Clique em "OK" para que o projeto seja atualizado contendo a referencia para a nova biblioteca:



3) Vamos adicionar o versionamento de nossa biblioteca em nossa aplicação. Para isso inclua o seguinte trecho de código dentro da sessão "<application>" do arquivo "AndroidManifest.xml" de sua aplicação:


  <meta-data   
    android:name="com.google.android.gms.version"  
    android:value="@integer/google_play_services_version"/>  

4) Para acessar o Google Maps Server com o Google Maps API você precisa adicionar uma chave de API em sua aplicação. Essa chave é gratuita, você pode utilizá-la em qualquer aplicação sua que utilize o Google Maps, e ela suporta um número ilimitado de usuários. Para adquirir sua chave, clique aqui para abrir o Console de APIs do Google (vale lembrar que você precisa ter uma conta de usuário na Google. Caso não tenha, crie uma. É gratuita!).

Antes de continuarmos, vamos esclarecer algumas coisas. 
Todas as aplicações Android devem ser assinaladas com um certificado digital para que você tenha uma chave privada de sua aplicação. Pelo fato desse certificado ser único ele fornece um jeito de identificar o seu aplicativo. Isso permite, de forma muito útil, rastrear sua aplicação em sistemas como o Google Play Store e o uso de recursos por sua aplicação como o Google Maps Server. Chaves da API do Google Maps são "linkadas com um específico par certificado/pacote. Nós precisamos apenas de uma chave para cada certificado, não importando quantos usuários teremos em nossa aplicação. Aplicativos que usam o mesmo certificado podem utilizar a mesma chave, mas o bom é registrar cada aplicativo com seu próprio certificado. (fonte dessas informações aqui). 

5) Sendo assim, vamos criar a assinatura digital de nosso aplicativo para assim obter a chave necessária para utilizar o Google Maps.

Existem dois tipo de assinatura, em modo Debug e em modo Release. Em modo Debug você assina quando ainda está desenvolvendo sua aplicação (nosso caso) e em modo Release é feito quando você finalmente irá distribuir sua aplicação na loja. Vale ressaltar que você não conseguirá distribuir seu aplicativo na loja com uma assinatura no modo Debug. Vamos abordar a assinatura no modo Debug.

Como estamos utilizando o Eclipse ADT, essa tarefa será simples e indolor! 
Selecione seu projeto e na barra de menu clique em File e selecione Export...:


Na tela que se segue selecione o projeto ao qual você quer gerar a assinatura e clique em Next. Marque a opção "Create a new keystore", escolha onde quer salvar sua chave e forneça a senha de utilização da mesma:


Ao clicar em Next será aberta uma tela no qual você deverá fornecer várias informações. Alias é apenas o nome pelo qual você identificará essa assinatura nas demais vezes que a for utilizar; Password e Confirm você coloca a mesma senha criada no passo anterior; Validity (years) será a validade dessa assinatura em anos; First and Last Name coloca-se o nome e ultimo nome do desenvolvedor(a). As outras informações são opcionais:


Clique em Next e na próxima tela escolha onde você quer salvar o arquivo ".apk" de seu aplicativo. Ao selecionar o local aparecerá a informação "Certificate expires in XX years." Clique em Finish e sua assinatura estará gerada! Simples, não?

6) A chave da API Google Maps é baseada em uma informação do certificado digital de sua aplicação chamada SHA-1 fingerprint que pelo fato de ser um texto único o Google Maps o utiliza para identificar sua aplicação. 

Para recuperar o valor do SHA-1 fingerprint de nossa assinatura digital (criada no model debug) localize o arquivo "debug.keystore" que está localizado No Linux em "~/.android/" e no Windows em "C:/Users/seu_nome_de_usuario/.android/":


Abra o terminal de comandos e execute o seguinte comando:

 keytool -list -v -keystore "caminho_do_arquivo" -alias androiddebugkey -storepass android -keypass android  

Você verá algo parecido com isso:


O certificado SHA-1 fingerprint é o valor da linha iniciada com SHA1. Guarde-a pois iremos utilizá-la mais adiante.

7) Voltando ao Console de APIs do Google, clique em "Create Project", dê um nome ao projeto (sugiro o mesmo nome da aplicação) e clique em "Create":


Espere a tela de Overview de seu projeto carregar e no menu da esquerda expanda a opção APIs & auth e selecione APIs. Procure na lista de APIs no centro da tela a opção Google Maps Android API v2 e no lado direito clique em OFF para que fique ON ativando assim a Google Maps API v2 para este projeto:

 
Neste momento, ela deverá ser mostrada no topo da lista na sessão de APIs ativadas.
Agora, na mesma aba APIs & auth, selecione o menu Credentials e clique em "Create new Key":


Na tela que se segue, digite o valor da chave SHA-1 fingerprint que recuperamos no passo 6) seguido de ";" (ponto-e-vírgula) e o pacote do seu aplicativo (recupere do atributo package do arquivo "AndroidManifest.xml" de sua aplicação:





Feito isso clique em Create. Na tela que se segue, o Console de APIs da Google mostrará como resposta alguns dados, dentre eles a API Key da sua aplicação:


Agora, para incluir tal chave dinamicamente em nossa aplicação, apenas inclua o trecho de código abaixo logo antes de fechar a tag "</application>" em seu arquivo "AndroidManifest.xml" referente à sua aplicação:



 <meta-data  
             android:name="com.google.android.maps.v2.API_KEY"  
             android:value="SUA_API_KEY_AQUI"/>  

8) Para o perfeito funcionamento do Google Maps em nossa aplicação, precisamos deixar explicito em nosso aquivo "AndroidManifest.xml" algumas permissões que o usuário deverá ceder para utilizar o Google Maps através de seu aplicativo. Adicione o seguinte trecho de código no arquivo "AndroidManifest.xml" referente à sua aplicação, logo acima da tag de abertura "<application>" (acima, não dentro):



 <!-- Usada pela API para baixar os mapas do servidor da API Google Maps -->  
 <uses-permission android:name="android.permission.INTERNET"/>  
 <!-- Para permitir à API verificar a conexão antes de tentar baixar os mapas -->  
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>  
 <!-- Para permitir à API guardar cache de mapas no armazenamento externo -->  
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
 <!-- Permite à API usar o wifi ou 3G ou ambos para determinar a localização -->  
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>  
 <!-- Permite à API utilizar o GPS para determinar a localização -->  
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 

 <!-- Indica a utilização da versão 2 do OpenGL para renderização dos mapas -->
 <uses-feature android:glEsVersion="0x00020000" android:required="true"/> 

9) Pronto! Agora toda a configuração está pronto e nós podemos adicionar mapas em nossa aplicação!


Para adicionar um mapa em sua aplicação, no arquivo XML de layout onde você quer adicionar um mapa, adicione o seguinte componente:


 10) Feito isso, compile e execute sua aplicação. Onde você colocou o trecho acima deverá aparecer um mapa! Para aprender a manipular o mapa, definir rotas, fazer marcações e etc, dê uma olhada no próximo tutorial com o título "Android - Trabalhando com Google Maps".



É isso aí!! Se tudo ocorreu bem e você seguiu todo o passo-a-passo, certamente terá em mãos um Aplicativo que utiliza mapas da API do Google Maps!!