edu.bsu.android.apps.geofence.Other.java Source code

Java tutorial

Introduction

Here is the source code for edu.bsu.android.apps.geofence.Other.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package edu.bsu.android.apps.geofence;

import android.app.Activity;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import edu.bsu.android.apps.geofence.R;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.LocationClient;

import edu.bsu.android.apps.geofence.content.ContentProviderUtils;
import edu.bsu.android.apps.geofence.objects.SimpleGeofence;
import edu.bsu.android.apps.geofence.utils.FileUtils;
import edu.bsu.android.apps.geofence.utils.GeofenceUtils;
import edu.bsu.android.apps.geofence.utils.GeofenceUtils.REMOVE_TYPE;
import edu.bsu.android.apps.geofence.utils.GeofenceUtils.REQUEST_TYPE;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * UI handler for the Location Services Geofence sample app. Allow input of latitude, longitude, and radius for two geofences. When registering
 * geofences, check input and then send the geofences to Location Services. Also allow removing either one of or both of the geofences. The menu
 * allows you to clear the screen or delete the geofences stored in persistent memory.
 */
public class Other extends FragmentActivity {
    /*
     * Use to set an expiration time for a geofence. After this amount of time Location Services will stop tracking the geofence. Remember to
     * unregister a geofence when you're finished with it. Otherwise, your app will use up battery. To continue monitoring a geofence indefinitely,
     * set the expiration time to Geofence#NEVER_EXPIRE.
     */
    private static final long GEOFENCE_EXPIRATION_IN_HOURS = 12;
    private static final long GEOFENCE_EXPIRATION_IN_MILLISECONDS = GEOFENCE_EXPIRATION_IN_HOURS
            * DateUtils.HOUR_IN_MILLIS;

    private ContentProviderUtils mProvider;

    // Store the current request
    private REQUEST_TYPE mRequestType;

    // Store the current type of removal
    private REMOVE_TYPE mRemoveType;

    // Persistent storage for geofences
    //private SimpleGeofenceStore mPrefs;

    // Store a list of geofences to add
    List<Geofence> mCurrentGeofences;

    // Add geofences handler
    private GeofenceRequester mGeofenceRequester;
    // Remove geofences handler
    private GeofenceRemover mGeofenceRemover;

    // Handle to geofence 1 latitude in the UI
    private EditText mLatitude1;
    // Handle to geofence 1 longitude in the UI
    private EditText mLongitude1;
    // Handle to geofence 1 radius in the UI
    private EditText mRadius1;

    // Handle to geofence 2 latitude in the UI
    private EditText mLatitude2;
    // Handle to geofence 2 longitude in the UI
    private EditText mLongitude2;
    // Handle to geofence 2 radius in the UI
    private EditText mRadius2;

    // Handle to geofence 3 latitude in the UI
    private EditText mLatitude3;
    // Handle to geofence 3 longitude in the UI
    private EditText mLongitude3;
    // Handle to geofence 3 radius in the UI
    private EditText mRadius3;

    // Handle to geofence 4 latitude in the UI
    private EditText mLatitude4;
    // Handle to geofence 4 longitude in the UI
    private EditText mLongitude4;
    // Handle to geofence 4 radius in the UI
    private EditText mRadius4;

    /*
     * Internal lightweight geofence objects for geofence 1 and 2
     */
    private SimpleGeofence mUIGeofence1;
    private SimpleGeofence mUIGeofence2;
    private SimpleGeofence mUIGeofence3;
    private SimpleGeofence mUIGeofence4;

    // decimal formats for latitude, longitude, and radius
    private DecimalFormat mLatLngFormat;
    private DecimalFormat mRadiusFormat;

    /*
     * An instance of an inner class that receives broadcasts from listeners and from the IntentService that receives geofence transition events
     */
    private GeofenceSampleReceiver mBroadcastReceiver;

    // An intent filter for the broadcast receiver
    private IntentFilter mIntentFilter;

    // Store the list of geofences to remove
    private List<String> mGeofenceIdsToRemove;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mProvider = ContentProviderUtils.Factory.get(this);

        // Set the pattern for the latitude and longitude format
        String latLngPattern = getString(R.string.lat_lng_pattern);

        // Set the format for latitude and longitude
        mLatLngFormat = new DecimalFormat(latLngPattern);

        // Localize the format
        mLatLngFormat.applyLocalizedPattern(mLatLngFormat.toLocalizedPattern());

        // Set the pattern for the radius format
        String radiusPattern = getString(R.string.radius_pattern);

        // Set the format for the radius
        mRadiusFormat = new DecimalFormat(radiusPattern);

        // Localize the pattern
        mRadiusFormat.applyLocalizedPattern(mRadiusFormat.toLocalizedPattern());

        // Create a new broadcast receiver to receive updates from the listeners and service
        mBroadcastReceiver = new GeofenceSampleReceiver();

        // Create an intent filter for the broadcast receiver
        mIntentFilter = new IntentFilter();

        // Action for broadcast Intents that report successful addition of geofences
        mIntentFilter.addAction(GeofenceUtils.ACTION_GEOFENCES_ADDED);

        // Action for broadcast Intents that report successful removal of geofences
        mIntentFilter.addAction(GeofenceUtils.ACTION_GEOFENCES_REMOVED);

        // Action for broadcast Intents containing various types of geofencing errors
        mIntentFilter.addAction(GeofenceUtils.ACTION_GEOFENCE_ERROR);

        // All Location Services sample apps use this category
        mIntentFilter.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);

        // Instantiate a new geofence storage area
        //mPrefs = new SimpleGeofenceStore(this);

        // Instantiate the current List of geofences
        mCurrentGeofences = new ArrayList<Geofence>();

        // Instantiate a Geofence requester
        mGeofenceRequester = new GeofenceRequester(this);

        // Instantiate a Geofence remover
        mGeofenceRemover = new GeofenceRemover(this);

        // Attach to the main UI
        setContentView(R.layout.activity_main);

        // Get handles to the Geofence editor fields in the UI
        mLatitude1 = (EditText) findViewById(R.id.value_latitude_1);
        mLongitude1 = (EditText) findViewById(R.id.value_longitude_1);
        mRadius1 = (EditText) findViewById(R.id.value_radius_1);

        mLatitude2 = (EditText) findViewById(R.id.value_latitude_2);
        mLongitude2 = (EditText) findViewById(R.id.value_longitude_2);
        mRadius2 = (EditText) findViewById(R.id.value_radius_2);

        mLatitude3 = (EditText) findViewById(R.id.value_latitude_3);
        mLongitude3 = (EditText) findViewById(R.id.value_longitude_3);
        mRadius3 = (EditText) findViewById(R.id.value_radius_3);

        mLatitude4 = (EditText) findViewById(R.id.value_latitude_4);
        mLongitude4 = (EditText) findViewById(R.id.value_longitude_4);
        mRadius4 = (EditText) findViewById(R.id.value_radius_4);
    }

    /*
     * Handle results returned to this Activity by other Activities started with startActivityForResult(). In particular, the method
     * onConnectionFailed() in GeofenceRemover and GeofenceRequester may call startResolutionForResult() to start an Activity that handles Google Play
     * services problems. The result of this call returns here, to onActivityResult. calls
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // Choose what to do based on the request code
        switch (requestCode) {

        // If the request code matches the code sent in onConnectionFailed
        case GeofenceUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST:

            switch (resultCode) {
            // If Google Play services resolved the problem
            case Activity.RESULT_OK:

                // If the request was to add geofences
                if (GeofenceUtils.REQUEST_TYPE.ADD == mRequestType) {

                    // Toggle the request flag and send a new request
                    mGeofenceRequester.setInProgressFlag(false);

                    // Restart the process of adding the current geofences
                    mGeofenceRequester.addGeofences(mCurrentGeofences);

                    // If the request was to remove geofences
                } else if (GeofenceUtils.REQUEST_TYPE.REMOVE == mRequestType) {

                    // Toggle the removal flag and send a new removal request
                    mGeofenceRemover.setInProgressFlag(false);

                    // If the removal was by Intent
                    if (GeofenceUtils.REMOVE_TYPE.INTENT == mRemoveType) {

                        // Restart the removal of all geofences for the PendingIntent
                        mGeofenceRemover.removeGeofencesByIntent(mGeofenceRequester.getRequestPendingIntent());

                        // If the removal was by a List of geofence IDs
                    } else {

                        // Restart the removal of the geofence list
                        mGeofenceRemover.removeGeofencesById(mGeofenceIdsToRemove);
                    }
                }
                break;

            // If any other result was returned by Google Play services
            default:

                // Report that Google Play services was unable to resolve the problem.
                Log.d(GeofenceUtils.APPTAG, getString(R.string.no_resolution));
            }

            // If any other request code was received
        default:
            // Report that this Activity received an unknown requestCode
            Log.d(GeofenceUtils.APPTAG, getString(R.string.unknown_activity_request_code, requestCode));

            break;
        }
    }

    /*
     * Whenever the Activity resumes, reconnect the client to Location Services and reload the last geofences that were set
     */
    @Override
    protected void onResume() {
        super.onResume();
        // Register the broadcast receiver to receive status updates
        LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, mIntentFilter);
        /*
         * Get existing geofences from the latitude, longitude, and radius values stored in SharedPreferences. If no values exist, null is returned.
         */
        ArrayList<SimpleGeofence> list = mProvider.getList();

        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                if (i == 0) {
                    mUIGeofence1 = list.get(i);
                } else if (i == 1) {
                    mUIGeofence2 = list.get(i);
                } else if (i == 2) {
                    mUIGeofence3 = list.get(i);
                } else if (i == 3) {
                    mUIGeofence4 = list.get(i);
                }
            }
        }
        //mUIGeofence1 = mPrefs.getGeofence("1");
        //mUIGeofence2 = mPrefs.getGeofence("2");
        //mUIGeofence3 = mPrefs.getGeofence("3");
        //mUIGeofence4 = mPrefs.getGeofence("4");
        /*
         * If the returned geofences have values, use them to set values in the UI, using the previously-defined number formats.
         */
        if (mUIGeofence1 != null) {
            mLatitude1.setText(mLatLngFormat.format(mUIGeofence1.getLatitude()));
            mLongitude1.setText(mLatLngFormat.format(mUIGeofence1.getLongitude()));
            mRadius1.setText(mRadiusFormat.format(mUIGeofence1.getRadius()));
        }
        if (mUIGeofence2 != null) {
            mLatitude2.setText(mLatLngFormat.format(mUIGeofence2.getLatitude()));
            mLongitude2.setText(mLatLngFormat.format(mUIGeofence2.getLongitude()));
            mRadius2.setText(mRadiusFormat.format(mUIGeofence2.getRadius()));
        }
        if (mUIGeofence3 != null) {
            mLatitude3.setText(mLatLngFormat.format(mUIGeofence3.getLatitude()));
            mLongitude3.setText(mLatLngFormat.format(mUIGeofence3.getLongitude()));
            mRadius3.setText(mRadiusFormat.format(mUIGeofence3.getRadius()));
        }
        if (mUIGeofence4 != null) {
            mLatitude4.setText(mLatLngFormat.format(mUIGeofence4.getLatitude()));
            mLongitude4.setText(mLatLngFormat.format(mUIGeofence4.getLongitude()));
            mRadius4.setText(mRadiusFormat.format(mUIGeofence4.getRadius()));
        }
    }

    /*
     * Inflate the app menu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;

    }

    /*
     * Respond to menu item selections
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {

        // Request to clear the geofence1 settings in the UI
        /*
        case R.id.menu_item_clear_geofence1:
        mLatitude1.setText(GeofenceUtils.EMPTY_STRING);
        mLongitude1.setText(GeofenceUtils.EMPTY_STRING);
        mRadius1.setText(GeofenceUtils.EMPTY_STRING);
        return true;
            
        // Request to clear the geofence2 settings in the UI
        case R.id.menu_item_clear_geofence2:
        mLatitude2.setText(GeofenceUtils.EMPTY_STRING);
        mLongitude2.setText(GeofenceUtils.EMPTY_STRING);
        mRadius2.setText(GeofenceUtils.EMPTY_STRING);
        return true;
            
        // Request to clear both geofence settings in the UI
        case R.id.menu_item_clear_geofences:
        mLatitude1.setText(GeofenceUtils.EMPTY_STRING);
        mLongitude1.setText(GeofenceUtils.EMPTY_STRING);
        mRadius1.setText(GeofenceUtils.EMPTY_STRING);
            
        mLatitude2.setText(GeofenceUtils.EMPTY_STRING);
        mLongitude2.setText(GeofenceUtils.EMPTY_STRING);
        mRadius2.setText(GeofenceUtils.EMPTY_STRING);
        return true;
            
        // Remove all geofences from storage
        case R.id.menu_item_clear_geofence_history:
        mProvider.deleteSimpleGeofence(mUIGeofence1);
        mProvider.deleteSimpleGeofence(mUIGeofence2);
        mProvider.deleteSimpleGeofence(mUIGeofence3);
        mProvider.deleteSimpleGeofence(mUIGeofence4);
        //mPrefs.clearGeofence("1");
        //mPrefs.clearGeofence("2");
        //mPrefs.clearGeofence("3");
        //mPrefs.clearGeofence("4");
        return true;
            
        // Remove all geofences from storage
        */
        case R.id.map:
            startActivity(newIntent(this, MapActivity.class));
            return true;

        // Pass through any other request
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /*
     * Save the current geofence settings in SharedPreferences.
     */
    @Override
    protected void onPause() {
        super.onPause();

        if (mUIGeofence1 != null) {
            mProvider.updateSimpleGeofence(mUIGeofence1);
            //mPrefs.setGeofence("1", mUIGeofence1);
        }

        if (mUIGeofence2 != null) {
            mProvider.updateSimpleGeofence(mUIGeofence2);
            //mPrefs.setGeofence("2", mUIGeofence2);
        }

        if (mUIGeofence3 != null) {
            mProvider.updateSimpleGeofence(mUIGeofence3);
            //mPrefs.setGeofence("3", mUIGeofence3);
        }

        if (mUIGeofence4 != null) {
            mProvider.updateSimpleGeofence(mUIGeofence4);
            //mPrefs.setGeofence("4", mUIGeofence4);
        }
    }

    /**
     * Verify that Google Play services is available before making a request.
     * 
     * @return true if Google Play services is available, otherwise false
     */
    private boolean servicesConnected() {

        // Check that Google Play services is available
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

        // If Google Play services is available
        if (ConnectionResult.SUCCESS == resultCode) {

            // In debug mode, log the status
            Log.d(GeofenceUtils.APPTAG, getString(R.string.play_services_available));

            // Continue
            return true;

            // Google Play services was not available for some reason
        } else {

            // Display an error dialog
            Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0);
            if (dialog != null) {
                ErrorDialogFragment errorFragment = new ErrorDialogFragment();
                errorFragment.setDialog(dialog);
                errorFragment.show(getSupportFragmentManager(), GeofenceUtils.APPTAG);
            }
            return false;
        }
    }

    /**
     * Called when the user clicks the "Remove geofences" button
     * 
     * @param view The view that triggered this callback
     */
    public void onUnregisterByPendingIntentClicked(View view) {
        /*
         * Remove all geofences set by this app. To do this, get the PendingIntent that was added when the geofences were added and use it as an
         * argument to removeGeofences(). The removal happens asynchronously; Location Services calls onRemoveGeofencesByPendingIntentResult()
         * (implemented in the current Activity) when the removal is done
         */

        /*
         * Record the removal as remove by Intent. If a connection error occurs, the app can automatically restart the removal if Google Play services
         * can fix the error
         */
        // Record the type of removal
        mRemoveType = GeofenceUtils.REMOVE_TYPE.INTENT;

        /*
         * Check for Google Play services. Do this after setting the request type. If connecting to Google Play services fails, onActivityResult is
         * eventually called, and it needs to know what type of request was in progress.
         */
        if (!servicesConnected()) {

            return;
        }

        // Try to make a removal request
        try {
            /*
             * Remove the geofences represented by the currently-active PendingIntent. If the PendingIntent was removed for some reason, re-create it;
             * since it's always created with FLAG_UPDATE_CURRENT, an identical PendingIntent is always created.
             */
            mGeofenceRemover.removeGeofencesByIntent(mGeofenceRequester.getRequestPendingIntent());

        } catch (UnsupportedOperationException e) {
            // Notify user that previous request hasn't finished.
            Toast.makeText(this, R.string.remove_geofences_already_requested_error, Toast.LENGTH_LONG).show();
        }

    }

    /**
     * Called when the user clicks the "Remove geofence 1" button
     * 
     * @param view The view that triggered this callback
     */
    public void onUnregisterGeofence1Clicked(View view) {
        /*
         * Remove the geofence by creating a List of geofences to remove and sending it to Location Services. The List contains the id of geofence 1
         * ("1"). The removal happens asynchronously; Location Services calls onRemoveGeofencesByPendingIntentResult() (implemented in the current
         * Activity) when the removal is done.
         */

        // Create a List of 1 Geofence with the ID "1" and store it in the global list
        mGeofenceIdsToRemove = Collections.singletonList("1");

        /*
         * Record the removal as remove by list. If a connection error occurs, the app can automatically restart the removal if Google Play services
         * can fix the error
         */
        mRemoveType = GeofenceUtils.REMOVE_TYPE.LIST;

        /*
         * Check for Google Play services. Do this after setting the request type. If connecting to Google Play services fails, onActivityResult is
         * eventually called, and it needs to know what type of request was in progress.
         */
        if (!servicesConnected()) {

            return;
        }

        // Try to remove the geofence
        try {
            mGeofenceRemover.removeGeofencesById(mGeofenceIdsToRemove);

            // Catch errors with the provided geofence IDs
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (UnsupportedOperationException e) {
            // Notify user that previous request hasn't finished.
            Toast.makeText(this, R.string.remove_geofences_already_requested_error, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Called when the user clicks the "Remove geofence 2" button
     * 
     * @param view The view that triggered this callback
     */
    public void onUnregisterGeofence2Clicked(View view) {
        /*
         * Remove the geofence by creating a List of geofences to remove and sending it to Location Services. The List contains the id of geofence 2,
         * which is "2". The removal happens asynchronously; Location Services calls onRemoveGeofencesByPendingIntentResult() (implemented in the
         * current Activity) when the removal is done.
         */

        /*
         * Record the removal as remove by list. If a connection error occurs, the app can automatically restart the removal if Google Play services
         * can fix the error
         */
        mRemoveType = GeofenceUtils.REMOVE_TYPE.LIST;

        // Create a List of 1 Geofence with the ID "2" and store it in the global list
        mGeofenceIdsToRemove = Collections.singletonList("2");

        /*
         * Check for Google Play services. Do this after setting the request type. If connecting to Google Play services fails, onActivityResult is
         * eventually called, and it needs to know what type of request was in progress.
         */
        if (!servicesConnected()) {

            return;
        }

        // Try to remove the geofence
        try {
            mGeofenceRemover.removeGeofencesById(mGeofenceIdsToRemove);

            // Catch errors with the provided geofence IDs
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (UnsupportedOperationException e) {
            // Notify user that previous request hasn't finished.
            Toast.makeText(this, R.string.remove_geofences_already_requested_error, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Called when the user clicks the "Register geofences" button. Get the geofence parameters for each geofence and add them to a List. Create the
     * PendingIntent containing an Intent that Location Services sends to this app's broadcast receiver when Location Services detects a geofence
     * transition. Send the List and the PendingIntent to Location Services.
     */
    public void onRegisterClicked(View view) {

        /*
         * Record the request as an ADD. If a connection error occurs, the app can automatically restart the add request if Google Play services can
         * fix the error
         */
        mRequestType = GeofenceUtils.REQUEST_TYPE.ADD;

        /*
         * Check for Google Play services. Do this after setting the request type. If connecting to Google Play services fails, onActivityResult is
         * eventually called, and it needs to know what type of request was in progress.
         */
        if (!servicesConnected()) {

            return;
        }

        /*
         * Check that the input fields have values and that the values are with the permitted range
         */
        if (!checkInputFields()) {
            return;
        }

        /*
         * Create a version of geofence 1 that is "flattened" into individual fields. This allows it to be stored in SharedPreferences.
         */
        mUIGeofence1 = new SimpleGeofence();
        mUIGeofence1.setExpirationDuration(GEOFENCE_EXPIRATION_IN_MILLISECONDS);
        mUIGeofence1.setLatitude(Double.valueOf(mLatitude1.getText().toString()));
        mUIGeofence1.setLongitude(Double.valueOf(mLongitude1.getText().toString()));
        mUIGeofence1.setRadius(Float.valueOf(mRadius1.getText().toString()));
        mUIGeofence1.setTransitionType(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT);

        // Store this flat version in SharedPreferences
        mProvider.insertSimpleGeofence(mUIGeofence1);
        //mPrefs.setGeofence("1", mUIGeofence1);

        /*
         * Create a version of geofence 2 that is "flattened" into individual fields. This allows it to be stored in SharedPreferences.
         */
        mUIGeofence2 = new SimpleGeofence();
        mUIGeofence2.setExpirationDuration(GEOFENCE_EXPIRATION_IN_MILLISECONDS);
        mUIGeofence2.setLatitude(Double.valueOf(mLatitude2.getText().toString()));
        mUIGeofence2.setLongitude(Double.valueOf(mLongitude2.getText().toString()));
        mUIGeofence2.setRadius(Float.valueOf(mRadius2.getText().toString()));
        mUIGeofence2.setTransitionType(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT);

        // Store this flat version in SharedPreferences
        mProvider.insertSimpleGeofence(mUIGeofence2);
        //mPrefs.setGeofence("2", mUIGeofence2);

        /*
         * Add Geofence objects to a List. toGeofence() creates a Location Services Geofence object from a flat object
         */
        mCurrentGeofences.add(mUIGeofence1.toGeofence());
        mCurrentGeofences.add(mUIGeofence2.toGeofence());

        if (!TextUtils.isEmpty(mLatitude3.getText().toString())) {
            /*
             * Create a version of geofence 3 that is "flattened" into individual fields. This allows it to be stored in SharedPreferences.
             */
            mUIGeofence3 = new SimpleGeofence();
            mUIGeofence3.setExpirationDuration(GEOFENCE_EXPIRATION_IN_MILLISECONDS);
            mUIGeofence3.setLatitude(Double.valueOf(mLatitude3.getText().toString()));
            mUIGeofence3.setLongitude(Double.valueOf(mLongitude3.getText().toString()));
            mUIGeofence3.setRadius(Float.valueOf(mRadius3.getText().toString()));
            mUIGeofence3.setTransitionType(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT);

            // Store this flat version in SharedPreferences
            //mPrefs.setGeofence("3", mUIGeofence3);
            mProvider.insertSimpleGeofence(mUIGeofence3);
            mCurrentGeofences.add(mUIGeofence3.toGeofence());
        }

        if (!TextUtils.isEmpty(mLatitude4.getText().toString())) {
            /*
             * Create a version of geofence 4 that is "flattened" into individual fields. This allows it to be stored in SharedPreferences.
             */
            mUIGeofence4 = new SimpleGeofence();
            mUIGeofence4.setExpirationDuration(GEOFENCE_EXPIRATION_IN_MILLISECONDS);
            mUIGeofence4.setLatitude(Double.valueOf(mLatitude4.getText().toString()));
            mUIGeofence4.setLongitude(Double.valueOf(mLongitude4.getText().toString()));
            mUIGeofence4.setRadius(Float.valueOf(mRadius4.getText().toString()));
            mUIGeofence4.setTransitionType(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT);

            // Store this flat version in SharedPreferences
            //mPrefs.setGeofence("4", mUIGeofence4);
            mProvider.insertSimpleGeofence(mUIGeofence4);
            mCurrentGeofences.add(mUIGeofence4.toGeofence());
        }

        // Start the request. Fail if there's already a request in progress
        try {
            // Try to add geofences
            mGeofenceRequester.addGeofences(mCurrentGeofences);
        } catch (UnsupportedOperationException e) {
            // Notify user that previous request hasn't finished.
            Toast.makeText(this, R.string.add_geofences_already_requested_error, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Check all the input values and flag those that are incorrect
     * 
     * @return true if all the widget values are correct; otherwise false
     */
    private boolean checkInputFields() {
        // Start with the input validity flag set to true
        boolean inputOK = true;

        /*
         * Latitude, longitude, and radius values can't be empty. If they are, highlight the input field in red and put a Toast message in the UI.
         * Otherwise set the input field highlight to black, ensuring that a field that was formerly wrong is reset.
         */
        if (TextUtils.isEmpty(mLatitude1.getText())) {
            mLatitude1.setBackgroundColor(Color.RED);
            Toast.makeText(this, R.string.geofence_input_error_missing, Toast.LENGTH_LONG).show();

            // Set the validity to "invalid" (false)
            inputOK = false;
        } else {

            mLatitude1.setBackgroundColor(Color.BLACK);
        }

        if (TextUtils.isEmpty(mLongitude1.getText())) {
            mLongitude1.setBackgroundColor(Color.RED);
            Toast.makeText(this, R.string.geofence_input_error_missing, Toast.LENGTH_LONG).show();

            // Set the validity to "invalid" (false)
            inputOK = false;
        } else {

            mLongitude1.setBackgroundColor(Color.BLACK);
        }
        if (TextUtils.isEmpty(mRadius1.getText())) {
            mRadius1.setBackgroundColor(Color.RED);
            Toast.makeText(this, R.string.geofence_input_error_missing, Toast.LENGTH_LONG).show();

            // Set the validity to "invalid" (false)
            inputOK = false;
        } else {

            mRadius1.setBackgroundColor(Color.BLACK);
        }

        if (TextUtils.isEmpty(mLatitude2.getText())) {
            mLatitude2.setBackgroundColor(Color.RED);
            Toast.makeText(this, R.string.geofence_input_error_missing, Toast.LENGTH_LONG).show();

            // Set the validity to "invalid" (false)
            inputOK = false;
        } else {

            mLatitude2.setBackgroundColor(Color.BLACK);
        }
        if (TextUtils.isEmpty(mLongitude2.getText())) {
            mLongitude2.setBackgroundColor(Color.RED);
            Toast.makeText(this, R.string.geofence_input_error_missing, Toast.LENGTH_LONG).show();

            // Set the validity to "invalid" (false)
            inputOK = false;
        } else {

            mLongitude2.setBackgroundColor(Color.BLACK);
        }
        if (TextUtils.isEmpty(mRadius2.getText())) {
            mRadius2.setBackgroundColor(Color.RED);
            Toast.makeText(this, R.string.geofence_input_error_missing, Toast.LENGTH_LONG).show();

            // Set the validity to "invalid" (false)
            inputOK = false;
        } else {

            mRadius2.setBackgroundColor(Color.BLACK);
        }

        /*
         * If all the input fields have been entered, test to ensure that their values are within the acceptable range. The tests can't be performed
         * until it's confirmed that there are actual values in the fields.
         */
        if (inputOK) {

            /*
             * Get values from the latitude, longitude, and radius fields.
             */
            double lat1 = Double.valueOf(mLatitude1.getText().toString());
            double lng1 = Double.valueOf(mLongitude1.getText().toString());
            double lat2 = Double.valueOf(mLatitude1.getText().toString());
            double lng2 = Double.valueOf(mLongitude1.getText().toString());
            float rd1 = Float.valueOf(mRadius1.getText().toString());
            float rd2 = Float.valueOf(mRadius2.getText().toString());

            /*
             * Test latitude and longitude for minimum and maximum values. Highlight incorrect values and set a Toast in the UI.
             */

            if (lat1 > GeofenceUtils.MAX_LATITUDE || lat1 < GeofenceUtils.MIN_LATITUDE) {
                mLatitude1.setBackgroundColor(Color.RED);
                Toast.makeText(this, R.string.geofence_input_error_latitude_invalid, Toast.LENGTH_LONG).show();

                // Set the validity to "invalid" (false)
                inputOK = false;
            } else {

                mLatitude1.setBackgroundColor(Color.BLACK);
            }

            if ((lng1 > GeofenceUtils.MAX_LONGITUDE) || (lng1 < GeofenceUtils.MIN_LONGITUDE)) {
                mLongitude1.setBackgroundColor(Color.RED);
                Toast.makeText(this, R.string.geofence_input_error_longitude_invalid, Toast.LENGTH_LONG).show();

                // Set the validity to "invalid" (false)
                inputOK = false;
            } else {

                mLongitude1.setBackgroundColor(Color.BLACK);
            }

            if (lat2 > GeofenceUtils.MAX_LATITUDE || lat2 < GeofenceUtils.MIN_LATITUDE) {
                mLatitude2.setBackgroundColor(Color.RED);
                Toast.makeText(this, R.string.geofence_input_error_latitude_invalid, Toast.LENGTH_LONG).show();

                // Set the validity to "invalid" (false)
                inputOK = false;
            } else {

                mLatitude2.setBackgroundColor(Color.BLACK);
            }

            if ((lng2 > GeofenceUtils.MAX_LONGITUDE) || (lng2 < GeofenceUtils.MIN_LONGITUDE)) {
                mLongitude2.setBackgroundColor(Color.RED);
                Toast.makeText(this, R.string.geofence_input_error_longitude_invalid, Toast.LENGTH_LONG).show();

                // Set the validity to "invalid" (false)
                inputOK = false;
            } else {

                mLongitude2.setBackgroundColor(Color.BLACK);
            }
            if (rd1 < GeofenceUtils.MIN_RADIUS) {
                mRadius1.setBackgroundColor(Color.RED);
                Toast.makeText(this, R.string.geofence_input_error_radius_invalid, Toast.LENGTH_LONG).show();

                // Set the validity to "invalid" (false)
                inputOK = false;
            } else {

                mRadius1.setBackgroundColor(Color.BLACK);
            }
            if (rd2 < GeofenceUtils.MIN_RADIUS) {
                mRadius2.setBackgroundColor(Color.RED);
                Toast.makeText(this, R.string.geofence_input_error_radius_invalid, Toast.LENGTH_LONG).show();

                // Set the validity to "invalid" (false)
                inputOK = false;
            } else {

                mRadius2.setBackgroundColor(Color.BLACK);
            }
        }

        // If everything passes, the validity flag will still be true, otherwise it will be false.
        return inputOK;
    }

    /**
     * Define a Broadcast receiver that receives updates from connection listeners and the geofence transition service.
     */
    public class GeofenceSampleReceiver extends BroadcastReceiver {
        /*
         * Define the required method for broadcast receivers This method is invoked when a broadcast Intent triggers the receiver
         */
        @Override
        public void onReceive(Context context, Intent intent) {

            // Check the action code and determine what to do
            String action = intent.getAction();
            //Toast.makeText(context, "onReceive action: " + action, Toast.LENGTH_LONG).show();

            // Intent contains information about errors in adding or removing geofences
            if (TextUtils.equals(action, GeofenceUtils.ACTION_GEOFENCE_ERROR)) {

                handleGeofenceError(context, intent);

                // Intent contains information about successful addition or removal of geofences
            } else if (TextUtils.equals(action, GeofenceUtils.ACTION_GEOFENCES_ADDED)
                    || TextUtils.equals(action, GeofenceUtils.ACTION_GEOFENCES_REMOVED)) {

                handleGeofenceStatus(context, intent);

                // Intent contains information about a geofence transition
            } else if (TextUtils.equals(action, GeofenceUtils.ACTION_GEOFENCE_TRANSITION)) {

                handleGeofenceTransition(context, intent);

                // The Intent contained an invalid action
            } else {
                Log.e(GeofenceUtils.APPTAG, getString(R.string.invalid_action_detail, action));
                Toast.makeText(context, R.string.invalid_action, Toast.LENGTH_LONG).show();
            }
        }

        /**
         * If you want to display a UI message about adding or removing geofences, put it here.
         * 
         * @param context A Context for this component
         * @param intent The received broadcast Intent
         */
        private void handleGeofenceStatus(Context context, Intent intent) {
            Toast.makeText(context, "geofence status", Toast.LENGTH_LONG).show();

        }

        /**
         * Report geofence transitions to the UI
         * 
         * @param context A Context for this component
         * @param intent The Intent containing the transition
         */
        private void handleGeofenceTransition(Context context, Intent intent) {
            /*
             * If you want to change the UI when a transition occurs, put the code here. The current design of the app uses a notification to inform
             * the user that a transition has occurred.
             */
            int transition = LocationClient.getGeofenceTransition(intent);
            List<Geofence> crossed = LocationClient.getTriggeringGeofences(intent);

            Toast.makeText(context, "geofence transition: " + transition + "|" + crossed.get(transition),
                    Toast.LENGTH_LONG).show();
            FileUtils.writeFile("transition.txt", transition + "|" + crossed.get(transition));
        }

        /**
         * Report addition or removal errors to the UI, using a Toast
         * 
         * @param intent A broadcast Intent sent by ReceiveTransitionsIntentService
         */
        private void handleGeofenceError(Context context, Intent intent) {
            String msg = intent.getStringExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS);
            Log.e(GeofenceUtils.APPTAG, msg);
            Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Define a DialogFragment to display the error dialog generated in showErrorDialog.
     */
    public static class ErrorDialogFragment extends DialogFragment {

        // Global field to contain the error dialog
        private Dialog mDialog;

        /**
         * Default constructor. Sets the dialog field to null
         */
        public ErrorDialogFragment() {
            super();
            mDialog = null;
        }

        /**
         * Set the dialog to display
         * 
         * @param dialog An error dialog
         */
        public void setDialog(Dialog dialog) {
            mDialog = dialog;
        }

        /*
         * This method must return a Dialog to the DialogFragment.
         */
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return mDialog;
        }
    }

    /**
     * Creates an intent with {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} and {@link Intent#FLAG_ACTIVITY_NEW_TASK}.
     * 
     * @param context the context
     * @param cls the class
     */
    public static final Intent newIntent(Context context, Class<?> cls) {
        try {
            return new Intent(context, cls)
                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        } catch (Exception ex) {
            return new Intent();
        }
    }
}