Tutorial Android – Criando e Monitorando Geofences

On 1 de setembro de 2014 by Jeferson

Nesse tutorial vamos falar de Geofences.
Mas o que é uma Geofence?
Geofences representa uma área geográfica demarcada virtualmente através de coordenadas de GPS. Utilizadas em dispositivos móveis geralmente para monitorar perímetros e notificar usuários que entram ou saem da área da Geofence demarcada.

geofences-maps
Ou Seja, uma vez que definir uma geofence a partir de uma coordenada e um raio, você pode notificar o usuário toda vez que ele entrar e/ou sair da área delimitada. Essa técnica pode te abrir um leque imenso de possibilidades ao criar um aplicativo android. Como por exemplo, em um aplicativo de recomendação, você pode avisar que o usuário está próximo de um bar ou restaurante que talvez o interesse; em um aplicativo de música você pode fazer com que uma musica se inicie de acordo com um determinado lugar e etc. A partir desse tutorial fica dependendo só da sua criatividade e um pouco de técnica para elaborar algo muito interessante.
Para esse tutorial, vamos criar um aplicativo simples, ele vai monitorar algumas coordenadas pré-definidas e mandar uma notificação toda vez que o usuário entrar em uma dessas áreas. Ao clicar na notificação, uma nova tela vai abrir com o nome do lugar.
Por se tratar de um tutorial de nível intermediário, alguns detalhes do código não serão explicados pois se tratam de temas comuns no android. Caso alguma dúvida seja levantada, entre em contato que logo responderei. Tudo isso, para não perdermos o foco do tutorial, ok?
Vamos começar, usando a plataforma de desenvolvimento Adt-bundle crie um novo projeto android.
definindo projeto
Vamos começar pela MainActivity.java criada automaticamente, mas para isso vamos definir um layout simples para a Activity contendo dois TextView, abra a o layout activity_main.xml :

[sourcecode language=”java”]
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginTop="28dp"
        android:text="@string/geofences"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/places"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="40dp"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>
[/sourcecode]

 

Agora na MainActivity.java:

[sourcecode language=”java”]

package com.exemplo.geofences;

import java.util.ArrayList;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationStatusCodes;

/*
 * Criado por Jeferson Rodrigues
 * Agosto de 2014
 *
 */

public class MainActivity extends Activity implements Runnable, GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener, LocationClient.OnAddGeofencesResultListener,
LocationClient.OnRemoveGeofencesResultListener
{

    private final int RADIUS = 200;
    private LocationClient locationClient;
    private Intent mIntent;
    private PendingIntent mPendingIntent;
    private ArrayList<Geofence> geofences;
    private Geofence.Builder builder;
    private TextView textPlaces;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        textPlaces = (TextView) findViewById(R.id.places);        
        textPlaces.setText("1 – cine vila rica\n2 – Casa dos contos\n3 – Iceb Ufop\n4 – Escola de minas Ufop ");
        
        verifyPlayServices();
        builder = new Geofence.Builder();
        geofences = new ArrayList<Geofence>();
        GooglePlayServicesClient.ConnectionCallbacks connectionCallbacks = new GooglePlayServicesClient.ConnectionCallbacks()
        {
            @Override
            public void onConnected(Bundle connectionHint)
            {
                LocationRequest locationrequest = LocationRequest.create();
                locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
                locationrequest.setInterval(1000 * 60);

                locationClient.requestLocationUpdates(locationrequest, new LocationListener()
                {
                    @Override
                    public void onLocationChanged(Location location){}
                });

                startGeofence();
            }

            @Override
            public void onDisconnected() {}
        };
        GooglePlayServicesClient.OnConnectionFailedListener onConnectionFailedListener = new GooglePlayServicesClient.OnConnectionFailedListener()
        {
            @Override
            public void onConnectionFailed(ConnectionResult result)
            {
            }
        };

        locationClient = new LocationClient(getBaseContext(), connectionCallbacks, onConnectionFailedListener);
        locationClient.connect();

        mIntent = new Intent( this, GeofencingService.class );
        mPendingIntent = PendingIntent.getService( this, 0, mIntent, PendingIntent.FLAG_UPDATE_CURRENT );

    }
    private void startGeofence()
    {
        geofences.add(addListGeofence(-20.3849, -43.5059, "Lugar – Cine Vila rica") ); //cine vila rica
        geofences.add(addListGeofence(-20.3835,  -43.5031, "Lugar – Casa dos contos") ); //casa dos contos
        geofences.add(addListGeofence(-20.396100, -43.510455,"Lugar – Casa") );//casa
        geofences.add(addListGeofence(-20.39824509,-43.5078705, "Lugar – Escola de minas Ufop") );//escola de minas

        if( !geofences.isEmpty()){
            try{
                locationClient.addGeofences( geofences, mPendingIntent, this);
            }catch (Exception e) {
                e.getMessage();
            }

        }

    }
    private Geofence addListGeofence( double latitude, double longitude, String id)
    {
        Geofence mGeofence = builder.setRequestId(id)
                .setCircularRegion(latitude,longitude , RADIUS)
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                .build();

        return mGeofence;
    }

    private void verifyPlayServices()
    {
        switch(GooglePlayServicesUtil.isGooglePlayServicesAvailable(this))
        {
        case ConnectionResult.SUCCESS:
        {
            break;
        }
        case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
        {
            finish();
        }
        default:
        {
            finish();
        }
        }
    }

    @Override
    public void onRemoveGeofencesByPendingIntentResult(int status, PendingIntent arg1)
    {
        if( status == LocationStatusCodes.SUCCESS )
        {
            stopService( mIntent );
        }
    }

    @Override
    public void onRemoveGeofencesByRequestIdsResult(int status, String[] arg1)
    {
        if( status == LocationStatusCodes.SUCCESS )
        {
            stopService( mIntent );
        }
    }

    @Override
    public void onAddGeofencesResult(int status, String[] arg1)
    {
        if(status == LocationStatusCodes.SUCCESS)
        {
            Intent intent = new Intent(mIntent);
            startService(intent);
        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult arg0) {}

    @Override
    public void onConnected(Bundle arg0) {}

    @Override
    public void onDisconnected() {}

    @Override
    public void run() {}

    @Override
    protected void onResume()
    {
        super.onResume();

        if(!locationClient.isConnected() && !locationClient.isConnecting())
        {
            locationClient.connect();
        }

    }

    @Override
    protected void onPause()
    {
        super.onPause();
        locationClient.disconnect();
    }
}

 [/sourcecode]

Vamos entender o que foi feito, nessa classe vamos definir o que  vai aparecer estaticamente na  primeira tela, métodos para colher sua localização e os métodos da Geofences que vão ser mantidos funcionando a partir de um service rodando em background.

A posição GPS do usuário é captado pelo GooglePlayService por isso, é necessário ter o SDK instalado e configurado e não se esqueça de implementar os listners:

[sourcecode language=”java”]
public class MainActivity extends Activity implements Runnable, GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener, LocationClient.OnAddGeofencesResultListener,
LocationClient.OnRemoveGeofencesResultListener
[/sourcecode]

[sourcecode language=”java”]
GooglePlayServicesClient.ConnectionCallbacks connectionCallbacks = new GooglePlayServicesClient.ConnectionCallbacks()
        {
            @Override
            public void onConnected(Bundle connectionHint)
            {
                LocationRequest locationrequest = LocationRequest.create();
                locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
                locationrequest.setInterval(1000 * 60);

                locationClient.requestLocationUpdates(locationrequest, new LocationListener()
                {
                    @Override
                    public void onLocationChanged(Location location){}
                });

                startGeofence();
            }

            @Override
            public void onDisconnected() {}
        };
        GooglePlayServicesClient.OnConnectionFailedListener onConnectionFailedListener = new GooglePlayServicesClient.OnConnectionFailedListener()
        {
            @Override
            public void onConnectionFailed(ConnectionResult result)
            {
            }
        };

        locationClient = new LocationClient(getBaseContext(), connectionCallbacks, onConnectionFailedListener);
        locationClient.connect();
 [/sourcecode]

Não vou entrar em detalhes sobre o assunto, mas definimos um tempo de atualização
e a acurácia na qual as requisições GPS são feitas.

Após colher a posição do usuário é necessário então criar uma intent para o Service
e um pending para a notificação.

[sourcecode language=”java”]
mIntent = new Intent( this, GeofencingService.class );
mPendingIntent = PendingIntent.getService( this, 0, mIntent, PendingIntent.FLAG_UPDATE_CURRENT );
[/sourcecode]

Vamos definir então agora a nossa lista de coordenadas, no método onConnected do
GooglePlayServicesClient temos a chamada de startGeofence();
Neste método, preenchemos a lista previamente criada de Geofences, uma geofence
é criada a partir dométodo addListGeofence que neste caso recebe a latitude,
longitude e a String com o nome do lugar.

[sourcecode language=”java”]

private void startGeofence()
    {

        geofences.add(addListGeofence(-20.3849, -43.5059, "Lugar – Cine Vila rica") ); //cine vila rica
        geofences.add(addListGeofence(-20.3835,  -43.5031, "Lugar – Casa dos contos") ); //casa dos contos
        geofences.add(addListGeofence(-20.396100, -43.510455,"Lugar – Casa") );//casa
        geofences.add(addListGeofence(-20.39824509,-43.5078705, "Lugar – Escola de minas Ufop") );//escola de minas
        if( !geofences.isEmpty()){
            try{
                locationClient.addGeofences( geofences, mPendingIntent, this);
            }catch (Exception e) {
                e.getMessage();
            }

        }

    }
    private Geofence addListGeofence( double latitude, double longitude, String id)
    {
        Geofence mGeofence = builder.setRequestId(id)
                .setCircularRegion(latitude,longitude , RADIUS)  // Radius define o tamanho do raio de alcance da geofence
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT) //Pegamos a transição de entrada e saída
                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                .build();

        return mGeofence;
    }
[/sourcecode]

Agora vamos escrever o código do service, mas antes abra o arquivo manifest.xml e
registre o serviço, aproveite e coloque permissão para acessar a localização do
usuário. Seu Manifest deve ficar assim:

[sourcecode language=”java”]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.exemplo.geofences"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.exemplo.geofences.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name="com.exemplo.geofences.GeofencingService"
            android:exported="false"
            android:label="@string/app_name" >
        </service>

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

        <activity
            android:name="com.exemplo.geofences.SecondActivity"
            android:label="@string/title_activity_second" >
        </activity>
    </application>
</manifest>[/sourcecode]

Vamos ao Service:

[sourcecode language=”java”]
package com.exemplo.geofences;

import java.util.List;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.LocationClient;

/*
 * Criado por Jeferson Rodrigues
 * Agosto de 2014
 *
 */

public class GeofencingService extends IntentService {
    public List<Geofence> list;
    private NotificationManager mNotificationManager;
    public GeofencingService(String name) {
        super(name);
    }
    public GeofencingService() {
        this( "Geofencing Service" );
    }
    @Override
    public IBinder onBind( Intent intent ) {
        return null;
    }
    @Override
    protected void onHandleIntent( Intent intent ) {
        int transitionType = LocationClient.getGeofenceTransition( intent );
        list = LocationClient.getTriggeringGeofences(intent);
        if( transitionType == Geofence.GEOFENCE_TRANSITION_ENTER ) {
            
            NotificationCompat.Builder builder = new NotificationCompat.Builder( this );
            builder.setSmallIcon(R.drawable.ic_launcher );
            builder.setDefaults( Notification.DEFAULT_ALL );
            builder.setAutoCancel(true);
            Intent myIntent = new Intent(getApplicationContext(),  SecondActivity.class);
            myIntent.putExtra("MSG", list.get(0).getRequestId());
            Log.i("Geofenfe", "Estou na geofence " + list.get(0).getRequestId() );
            
            PendingIntent pendingIntent = PendingIntent.getActivity(
                    getApplicationContext(),
                    0,
                    myIntent,
                    Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
            
            builder.setContentTitle("Geofences");
            builder.setContentIntent(pendingIntent);
            builder.setContentText( "Você entrou na Geofence -" + list.get(0).getRequestId());
            mNotificationManager.notify( 1, builder.build() );
            
        }    
        else if( transitionType == Geofence.GEOFENCE_TRANSITION_EXIT ) {
                Log.i("Exit", "Saindo da geofence " + list.get(0).getRequestId());
        }
    }
    @Override
    public void onCreate() {
        super.onCreate();
        mNotificationManager = (NotificationManager) getSystemService( NOTIFICATION_SERVICE );
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onHandleIntent( intent );
        return START_NOT_STICKY;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        mNotificationManager.cancel( 1 );
    }
}[/sourcecode]

Nessa parte não temos muito mistério, o service fica “escutando”, uma vez que o
raio da geofence é alcançado a condição:
if( transitionType == Geofence.GEOFENCE_TRANSITION_ENTER ) é satisfeita e então
uma notificação com o nome do lugar é enviada ao sistema. Ao clicar na notifica-
ção uma segunda activity é aberta apresentando o texto. É possivel monitorar a saída
da geofence, podendo assim repetir os mesmos passos para quando se entra, porém
enviando uma notificação diferente no else if.

E por fim vamos a Activity que recebe o retorno da notificação e imprimi na tela:

[sourcecode language=”java”]
package com.exemplo.geofences;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

/*
 * Criado por Jeferson Rodrigues
 * Agosto de 2014
 *
 */
public class SecondActivity extends Activity {
    private String msg;
    private TextView textMsgReceived;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        textMsgReceived = (TextView) findViewById(R.id.received);        
        
        Intent intent = getIntent();
        msg         = intent.getStringExtra("MSG");
        
        
        textMsgReceived.setText("Mensagem recebida – Estou na Geofence – " + msg );
        
        
        
    }
}

 [/sourcecode]

Seu layout correspondente:

[sourcecode language=”java”]
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin" >

    <TextView
        android:id="@+id/textViewSecond"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginTop="28dp"
        android:text="@string/geofences"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/received"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textViewSecond"
        android:layout_marginTop="40dp"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>
[/sourcecode]

Assim terminamos nosso tutorial, vocês podem notar que o que foi feito aqui é o básico em questão de aplicação final, porém a idéia aqui pode ser expandida e dar margem a inumeras aplicações muito mais complexas.
Veja uns prints da aplicação funcionando:

screen2

11 Responses to “Tutorial Android – Criando e Monitorando Geofences”

Trackbacks & Pings

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *