Tutorial Android – Criando e Monitorando Geofences
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.
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.
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:
que valor é este ?
android:value=”@integer/google_play_services_version”
É o identificador do ID da biblioteca do Play Services do Google. necessário para ter acesso a funcionalidade.
Como fazes import do:
com.google.android.gms.location.Geofence
não encontro o jar em lado nenhum ..
Voce deve incluir em seu projeto a biblioteca do Google, a Google API. Ele estara lá.
esta aplicação não contém quando o utilizador sai da geofence, pois não ?
Não, ela so contabiliza quando entra, nao quando sai
A aplicação só tem em conta quando o utilizador entra numa area. não contempla qd ele sai, certo ?
Sim
Simples e objetivo. Parabéns.
Teria como disponibilizar o código fonte?