Android How to - Create region monitor service








The following code shows how to Create region monitor service.

Code revised from
Android Recipes:A Problem-Solution Approach
http://www.apress.com/9781430234135
ISBN13: 978-1-4302-3413-5

Example

Register service in manifest xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.androidrecipes.regionmonitor"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />
    
    <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.androidrecipes.regionmonitor.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.androidrecipes.regionmonitor.RegionMonitorService" />
    </application>

</manifest>

Main xml file

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/status"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <SeekBar
        android:id="@+id/radius"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="1000"/>
    <TextView
        android:id="@+id/radius_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Set Geofence at My Location"
        android:onClick="onSetGeofenceClick" />

    <View
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
    
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Monitoring"
        android:onClick="onStartMonitorClick" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop Monitoring"
        android:onClick="onStopMonitorClick" />
</LinearLayout>

Main activity Java code

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.LocationStatusCodes;
/* www  .j  a v  a2  s .  c o m*/
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends Activity implements
        OnSeekBarChangeListener,
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener,
        LocationClient.OnAddGeofencesResultListener,
        LocationClient.OnRemoveGeofencesResultListener {
    private static final String TAG = "RegionMonitorActivity";

    private static final String FENCE_ID = "com.androidrecipes.FENCE";
    
    private LocationClient mLocationClient;
    private SeekBar mRadiusSlider;
    private TextView mStatusText, mRadiusText;
    
    private Geofence mCurrentFence;
    private Intent mServiceIntent;
    private PendingIntent mCallbackIntent;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mStatusText = (TextView) findViewById(R.id.status);
        mRadiusText = (TextView) findViewById(R.id.radius_text);
        mRadiusSlider = (SeekBar) findViewById(R.id.radius);
        mRadiusSlider.setOnSeekBarChangeListener(this);
        updateRadiusDisplay();
        
        switch (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this)) {
            case ConnectionResult.SUCCESS:
                //Do nothing, move on
                break;
            case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
                Toast.makeText(this,
                        "Geofencing service requires an update, please open Google Play.",
                        Toast.LENGTH_SHORT).show();
                finish();
                return;
            default:
                Toast.makeText(this,
                        "Geofencing service is not available on this device.",
                        Toast.LENGTH_SHORT).show();
                finish();
                return;
        }
        //Create a client for Google Services
        mLocationClient = new LocationClient(this, this, this);
        //Create an Intent to trigger our service
        mServiceIntent = new Intent(this, RegionMonitorService.class);
        //Create a PendingIntent for Google Services to use with callbacks
        mCallbackIntent = PendingIntent.getService(this, 0, mServiceIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //Connect to all services
        if (!mLocationClient.isConnected()
                && !mLocationClient.isConnecting()) {
            mLocationClient.connect();
        }
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        //Disconnect when not in the foreground
        mLocationClient.disconnect();
    }
    
    public void onSetGeofenceClick(View v) {
        //Obtain the last location from services and radius
        // from the UI
        Location current = mLocationClient.getLastLocation();
        int radius = mRadiusSlider.getProgress();
        
        //Create a new Geofence using the Builder
        Geofence.Builder builder = new Geofence.Builder();
        mCurrentFence = builder
            //Unique to this geofence
            .setRequestId(FENCE_ID)
            //Size and location
            .setCircularRegion(
                current.getLatitude(),
                current.getLongitude(),
                radius)
            //Events both in and out of the fence
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER
                    | Geofence.GEOFENCE_TRANSITION_EXIT)
            //Keep alive
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .build();
        
        mStatusText.setText(String.format("Geofence set at %.3f, %.3f",
                current.getLatitude(), current.getLongitude()) );
    }
    
    public void onStartMonitorClick(View v) {
        if (mCurrentFence == null) {
            Toast.makeText(this, "Geofence Not Yet Set",
                    Toast.LENGTH_SHORT).show();
            return;
        }
        
        //Add the fence to start tracking, the PendingIntent will
        // be triggered with new updates
        ArrayList<Geofence> geofences = new ArrayList<Geofence>();
        geofences.add(mCurrentFence);
        mLocationClient.addGeofences(geofences, mCallbackIntent, this);
    }
    
    public void onStopMonitorClick(View v) {
        //Remove to stop tracking
        mLocationClient.removeGeofences(mCallbackIntent, this);
    }
    
    /** SeekBar Callbacks */
    
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {
        updateRadiusDisplay();
    }
    
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) { }
    
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) { }
    
    private void updateRadiusDisplay() {
        mRadiusText.setText(mRadiusSlider.getProgress() + " meters");
    }
    
    /** Google Services Connection Callbacks */
    
    @Override
    public void onConnected(Bundle connectionHint) {
        Log.v(TAG, "Google Services Connected");
    }
    
    @Override
    public void onDisconnected() {
        Log.w(TAG, "Google Services Disconnected");
    }
    
    @Override
    public void onConnectionFailed(ConnectionResult result) {
        Log.w(TAG, "Google Services Connection Failure");
    }
    
    /** LocationClient Callbacks */
    
    /*
     * Called when the asynchronous geofence add is complete.
     * When this happens we start our monitoring service.
     */
    @Override
    public void onAddGeofencesResult(int statusCode,
            String[] geofenceRequestIds) {
        if (statusCode == LocationStatusCodes.SUCCESS) {
            Toast.makeText(this, "Geofence Added Successfully", Toast.LENGTH_SHORT).show();
        }
        
        Intent startIntent = new Intent(mServiceIntent);
        startIntent.setAction(RegionMonitorService.ACTION_INIT);
        startService(mServiceIntent);
    }

    /*
     * Called when the asynchronous geofence remove is complete.
     * The version called depends on whether you requested the
     * removal via PendingIntent or request Id.
     * When this happens we stop our monitoring service.
     */
    @Override
    public void onRemoveGeofencesByPendingIntentResult(
            int statusCode, PendingIntent pendingIntent) {
        if (statusCode == LocationStatusCodes.SUCCESS) {
            Toast.makeText(this, "Geofence Removed Successfully",
                    Toast.LENGTH_SHORT).show();
        }
        
        stopService(mServiceIntent);
    }
    
    @Override
    public void onRemoveGeofencesByRequestIdsResult(
            int statusCode, String[] geofenceRequestIds) {
        if (statusCode == LocationStatusCodes.SUCCESS) {
            Toast.makeText(this, "Geofence Removed Successfully",
                    Toast.LENGTH_SHORT).show();
        }
        
        stopService(mServiceIntent);
    }
}

Service Java code

package com.java2s.myapplication3.app;
//from  ww  w . j  ava 2 s  .  co m
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.LocationClient;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
    private static final String TAG = "RegionMonitorService";

    private static final int NOTE_ID = 100;
    //Unique action to identify start requests vs. events
    public static final String ACTION_INIT =
            "com.androidrecipes.regionmonitor.ACTION_INIT";

    private NotificationManager mNoteManager;

    @Override
    public void onCreate() {
        super.onCreate();
        mNoteManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //Post a system notification when the service starts
        NotificationCompat.Builder builder =
                new NotificationCompat.Builder(this);
        builder.setSmallIcon(R.drawable.ic_launcher);
        builder.setContentTitle("Geofence Service");
        builder.setContentText("Waiting for transition...");
        builder.setOngoing(true);

        Notification note = builder.build();
        mNoteManager.notify(NOTE_ID, note);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //Nothing to do yet, just starting the service
        if (ACTION_INIT.equals(intent.getAction())) {
            //We don't care if this service dies unexpectedly
            return START_NOT_STICKY;
        }

        if (LocationClient.hasError(intent)) {
            //Log any errors
            Log.w(TAG, "Error monitoring region: "
                    + LocationClient.getErrorCode(intent));
        } else {
            //Update the ongoing notification from the new event
            NotificationCompat.Builder builder =
                    new NotificationCompat.Builder(this);
            builder.setSmallIcon(R.drawable.ic_launcher);
            builder.setDefaults(Notification.DEFAULT_SOUND
                    | Notification.DEFAULT_LIGHTS);
            builder.setOngoing(true);

            int transitionType = LocationClient.getGeofenceTransition(intent);
            //Check whether we entered or exited the region
            if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER) {
                builder.setContentTitle("Geofence Transition");
                builder.setContentText("Entered your Geofence");
            } else if (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
                builder.setContentTitle("Geofence Transition");
                builder.setContentText("Exited your Geofence");
            }

            Notification note = builder.build();
            mNoteManager.notify(NOTE_ID, note);
        }

        //We don't care if this service dies unexpectedly
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //When the service dies, cancel our ongoing notification
        mNoteManager.cancel(NOTE_ID);
    }

    /* We are not binding to this service */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}