I am trying to build an app that sends an SMS to another phone when a geofence is crossed. I borrowed code from the google sample geofence. I wanted to know what method is executed when the geofence is called. I tried putting in my code in the getGeofencePendingIntent method (as seen commented). However, this led to an SMS being sent whenever either the Add Geofence or Remove Geofence button was clicked. The notification (from GeofenceTransitionsIntentService class) also did not get executed.
I tried to put in my code in the GeofenceTransitionsIntentService class itself because I figured whatever code exists there would be executed when the geofence was crossed. However, this led to the notifications as well as the SMS not executing.
Here is the code for sending the SMS:
String phoneNum = editText2.getText().toString();
if (phoneNum.length()>9 && phoneNum.length()<11) {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNum, null, "This is part of an SMS text message!", null, null);
}
else
{
// display message if text fields are empty
Toast.makeText(getBaseContext(),"Please add a correct phone number.",Toast.LENGTH_SHORT).show();
}
and this is my code in the SetGeofence (Main Activity) class:
package com.indianstudentroom.senioralert;
import android.Manifest;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import java.util.ArrayList;
import java.util.Map;
public class SetGeofence extends AppCompatActivity implements OnCompleteListener<Void> {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set_geofence);
// Get the UI widgets.
mAddGeofencesButton = (Button) findViewById(R.id.add_geofences_button);
mRemoveGeofencesButton = (Button) findViewById(R.id.remove_geofences_button);
openMonitoringButton = (Button) findViewById(R.id.button);
editText2 = (EditText) findViewById(R.id.edit_text);
// Empty list for storing geofences.
mGeofenceList = new ArrayList<>();
// Initially set the PendingIntent used in addGeofences() and removeGeofences() to null.
mGeofencePendingIntent = null;
setButtonsEnabledState();
// Get the geofences used. Geofence data is hard coded in this sample.
populateGeofenceList();
mGeofencingClient = LocationServices.getGeofencingClient(this);
}
private static final String TAG = MainActivity.class.getSimpleName();
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
/**
* Tracks whether the user requested to add or remove geofences, or to do neither.
*/
private enum PendingGeofenceTask {
ADD, REMOVE, NONE
}
/**
* Provides access to the Geofencing API.
*/
private GeofencingClient mGeofencingClient;
/**
* The list of geofences used in this sample.
*/
private ArrayList<Geofence> mGeofenceList;
/**
* Used when requesting to add or remove geofences.
*/
private PendingIntent mGeofencePendingIntent;
// Buttons for kicking off the process of adding or removing geofences.
private Button mAddGeofencesButton;
private Button mRemoveGeofencesButton;
public EditText editText2;
private Button openMonitoringButton;
private PendingGeofenceTask mPendingGeofenceTask = PendingGeofenceTask.NONE;
@Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
performPendingGeofenceTask();
}
}
/**
* Builds and returns a GeofencingRequest. Specifies the list of geofences to be monitored.
* Also specifies how the geofence notifications are initially triggered.
*/
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
// The INITIAL_TRIGGER_ENTER flag indicates that geofencing service should trigger a
// GEOFENCE_TRANSITION_ENTER notification when the geofence is added and if the device
// is already inside that geofence.
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
// Add the geofences to be monitored by geofencing service.
builder.addGeofences(mGeofenceList);
// Return a GeofencingRequest.
return builder.build();
}
/**
* Adds geofences, which sets alerts to be notified when the device enters or exits one of the
* specified geofences. Handles the success or failure results returned by addGeofences().
*/
public void addGeofencesButtonHandler(View view) {
if (!checkPermissions()) {
mPendingGeofenceTask = PendingGeofenceTask.ADD;
requestPermissions();
return;
}
addGeofences();
}
/**
* Adds geofences. This method should be called after the user has granted the location
* permission.
*/
@SuppressWarnings("MissingPermission")
private void addGeofences() {
if (!checkPermissions()) {
showSnackbar(getString(R.string.insufficient_permissions));
return;
}
mGeofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
.addOnCompleteListener(this);
}
/**
* Removes geofences, which stops further notifications when the device enters or exits
* previously registered geofences.
*/
public void removeGeofencesButtonHandler(View view) {
if (!checkPermissions()) {
mPendingGeofenceTask = PendingGeofenceTask.REMOVE;
requestPermissions();
return;
}
removeGeofences();
}
/**
* Removes geofences. This method should be called after the user has granted the location
* permission.
*/
@SuppressWarnings("MissingPermission")
private void removeGeofences() {
if (!checkPermissions()) {
showSnackbar(getString(R.string.insufficient_permissions));
return;
}
mGeofencingClient.removeGeofences(getGeofencePendingIntent()).addOnCompleteListener(this);
}
public void openMonitoring() {
Intent intent = new Intent(SetGeofence.this, Monitoring.class);
startActivity(intent);
}
/**
* Runs when the result of calling {@link #addGeofences()} and/or {@link #removeGeofences()}
* is available.
*
* @param task the resulting Task, containing either a result or error.
*/
@Override
public void onComplete(@NonNull Task<Void> task) {
mPendingGeofenceTask = PendingGeofenceTask.NONE;
if (task.isSuccessful()) {
updateGeofencesAdded(!getGeofencesAdded());
setButtonsEnabledState();
int messageId = getGeofencesAdded() ? R.string.geofences_added :
R.string.geofences_removed;
Toast.makeText(this, getString(messageId), Toast.LENGTH_SHORT).show();
if (getString(messageId).equals(R.string.geofences_added)) {
openMonitoring();
}
} else {
// Get the status code for the error and log it using a user-friendly message.
String errorMessage = GeofenceErrorMessages.getErrorString(this, task.getException());
Log.w(TAG, errorMessage);
}
}
/**
* Gets a PendingIntent to send with the request to add or remove Geofences. Location Services
* issues the Intent inside this PendingIntent whenever a geofence transition occurs for the
* current list of geofences.
*
* @return A PendingIntent for the IntentService that handles geofence transitions.
*/
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
/* String phoneNum = editText2.getText().toString();
if (phoneNum.length()>9 && phoneNum.length()<11) {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNum, null, "The person under your care has left the geofence! Please take the appropriate action and save them!", null, null);
}
else
{
// display message if text fields are empty
Toast.makeText(getBaseContext(),"Please add a correct phone number.",Toast.LENGTH_SHORT).show();
}
*/
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* This sample hard codes geofence data. A real app might dynamically create geofences based on
* the user's location.
*/
private void populateGeofenceList() {
for (Map.Entry<String, LatLng> entry : Constants.BAY_AREA_LANDMARKS.entrySet()) {
mGeofenceList.add(new Geofence.Builder()
// Set the request ID of the geofence. This is a string to identify this
// geofence.
.setRequestId(entry.getKey())
// Set the circular region of this geofence.
.setCircularRegion(
entry.getValue().latitude,
entry.getValue().longitude,
Constants.GEOFENCE_RADIUS_IN_METERS
)
// Set the expiration duration of the geofence. This geofence gets automatically
// removed after this period of time.
.setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
// Set the transition types of interest. Alerts are only generated for these
// transition. We track entry and exit transitions in this sample.
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
Geofence.GEOFENCE_TRANSITION_EXIT)
// Create the geofence.
.build());
}
}
/**
* Ensures that only one button is enabled at any time. The Add Geofences button is enabled
* if the user hasn't yet added geofences. The Remove Geofences button is enabled if the
* user has added geofences.
*/
private void setButtonsEnabledState() {
if (getGeofencesAdded()) {
mAddGeofencesButton.setEnabled(false);
mRemoveGeofencesButton.setEnabled(true);
openMonitoringButton.setEnabled(true);
} else {
mAddGeofencesButton.setEnabled(true);
mRemoveGeofencesButton.setEnabled(false);
openMonitoringButton.setEnabled(false);
}
}
public void openMonitoring(View view) {
Intent i = new Intent(SetGeofence.this, Monitoring.class);
startActivity(i);
}
/**
* Shows a {@link Snackbar} using {@code text}.
*
* @param text The Snackbar text.
*/
private void showSnackbar(final String text) {
View container = findViewById(android.R.id.content);
if (container != null) {
Snackbar.make(container, text, Snackbar.LENGTH_LONG).show();
}
}
/**
* Shows a {@link Snackbar}.
*
* @param mainTextStringId The id for the string resource for the Snackbar text.
* @param actionStringId The text of the action item.
* @param listener The listener associated with the Snackbar action.
*/
private void showSnackbar(final int mainTextStringId, final int actionStringId,
View.OnClickListener listener) {
Snackbar.make(
findViewById(android.R.id.content),
getString(mainTextStringId),
Snackbar.LENGTH_INDEFINITE)
.setAction(getString(actionStringId), listener).show();
}
/**
* Returns true if geofences were added, otherwise false.
*/
private boolean getGeofencesAdded() {
return PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
Constants.GEOFENCES_ADDED_KEY, false);
}
/**
* Stores whether geofences were added ore removed in {SharedPreferences };
*
* @param added Whether geofences were added or removed.
*/
private void updateGeofencesAdded(boolean added) {
PreferenceManager.getDefaultSharedPreferences(this)
.edit()
.putBoolean(Constants.GEOFENCES_ADDED_KEY, added)
.apply();
}
/**
* Performs the geofencing task that was pending until location permission was granted.
*/
private void performPendingGeofenceTask() {
if (mPendingGeofenceTask == PendingGeofenceTask.ADD) {
addGeofences();
} else if (mPendingGeofenceTask == PendingGeofenceTask.REMOVE) {
removeGeofences();
}
}
/**
* Return the current state of the permissions needed.
*/
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
showSnackbar(R.string.permission_rationale, android.R.string.ok,
new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(SetGeofence.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
});
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(SetGeofence.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
/**
* Callback received when a permissions request has been completed.
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "Permission granted.");
performPendingGeofenceTask();
} else {
// Permission denied.
// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.
// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
showSnackbar(R.string.permission_denied_explanation, R.string.settings,
new View.OnClickListener() {
@Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
mPendingGeofenceTask = PendingGeofenceTask.NONE;
}
}
}
}
Thanks so much!
onHandleIntent
on GeofenceTransitionsIntentService
should be executed when your Geofence is entered/exited. Post your code in that method