diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 44d2738d..c5fbcc1a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -25,6 +25,8 @@
android:authorities="com.example.android.shushme"
android:exported="false"/>
+
+
diff --git a/app/src/main/java/com/example/android/shushme/GeofenceBroadcastReceiver.java b/app/src/main/java/com/example/android/shushme/GeofenceBroadcastReceiver.java
new file mode 100644
index 00000000..c02d01dc
--- /dev/null
+++ b/app/src/main/java/com/example/android/shushme/GeofenceBroadcastReceiver.java
@@ -0,0 +1,40 @@
+package com.example.android.shushme;
+
+/*
+* Copyright (C) 2017 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.
+*/
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class GeofenceBroadcastReceiver extends BroadcastReceiver {
+
+ public static final String TAG = GeofenceBroadcastReceiver.class.getSimpleName();
+
+ /***
+ * Handles the Broadcast message sent when the Geofence Transition is triggered
+ * Careful here though, this is running on the main thread so make sure you start an AsyncTask for
+ * anything that takes longer than say 10 second to run
+ *
+ * @param context
+ * @param intent
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "onReceive called");
+ }
+}
diff --git a/app/src/main/java/com/example/android/shushme/Geofencing.java b/app/src/main/java/com/example/android/shushme/Geofencing.java
new file mode 100644
index 00000000..bd8a2fa1
--- /dev/null
+++ b/app/src/main/java/com/example/android/shushme/Geofencing.java
@@ -0,0 +1,168 @@
+package com.example.android.shushme;
+
+/*
+* Copyright (C) 2017 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.
+*/
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.Result;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingRequest;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.location.places.Place;
+import com.google.android.gms.location.places.PlaceBuffer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Geofencing implements ResultCallback {
+
+ // Constants
+ public static final String TAG = Geofencing.class.getSimpleName();
+ private static final float GEOFENCE_RADIUS = 50; // 50 meters
+ private static final long GEOFENCE_TIMEOUT = 24 * 60 * 60 * 1000; // 24 hours
+
+ private List mGeofenceList;
+ private PendingIntent mGeofencePendingIntent;
+ private GoogleApiClient mGoogleApiClient;
+ private Context mContext;
+
+ public Geofencing(Context context, GoogleApiClient client) {
+ mContext = context;
+ mGoogleApiClient = client;
+ mGeofencePendingIntent = null;
+ mGeofenceList = new ArrayList<>();
+ }
+
+ /***
+ * Registers the list of Geofences specified in mGeofenceList with Google Place Services
+ * Uses {@code #mGoogleApiClient} to connect to Google Place Services
+ * Uses {@link #getGeofencingRequest} to get the list of Geofences to be registered
+ * Uses {@link #getGeofencePendingIntent} to get the pending intent to launch the IntentService
+ * when the Geofence is triggered
+ * Triggers {@link #onResult} when the geofences have been registered successfully
+ */
+ public void registerAllGeofences() {
+ // Check that the API client is connected and that the list has Geofences in it
+ if (mGoogleApiClient == null || !mGoogleApiClient.isConnected() ||
+ mGeofenceList == null || mGeofenceList.size() == 0) {
+ return;
+ }
+ try {
+ LocationServices.GeofencingApi.addGeofences(
+ mGoogleApiClient,
+ getGeofencingRequest(),
+ getGeofencePendingIntent()
+ ).setResultCallback(this);
+ } catch (SecurityException securityException) {
+ // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
+ Log.e(TAG, securityException.getMessage());
+ }
+ }
+
+ /***
+ * Unregisters all the Geofences created by this app from Google Place Services
+ * Uses {@code #mGoogleApiClient} to connect to Google Place Services
+ * Uses {@link #getGeofencePendingIntent} to get the pending intent passed when
+ * registering the Geofences in the first place
+ * Triggers {@link #onResult} when the geofences have been unregistered successfully
+ */
+ public void unRegisterAllGeofences() {
+ if (mGoogleApiClient == null || !mGoogleApiClient.isConnected()) {
+ return;
+ }
+ try {
+ LocationServices.GeofencingApi.removeGeofences(
+ mGoogleApiClient,
+ // This is the same pending intent that was used in registerGeofences
+ getGeofencePendingIntent()
+ ).setResultCallback(this);
+ } catch (SecurityException securityException) {
+ // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
+ Log.e(TAG, securityException.getMessage());
+ }
+ }
+
+
+ /***
+ * Updates the local ArrayList of Geofences using data from the passed in list
+ * Uses the Place ID defined by the API as the Geofence object Id
+ *
+ * @param places the PlaceBuffer result of the getPlaceById call
+ */
+ public void updateGeofencesList(PlaceBuffer places) {
+ mGeofenceList = new ArrayList<>();
+ if (places == null || places.getCount() == 0) return;
+ for (Place place : places) {
+ // Read the place information from the DB cursor
+ String placeUID = place.getId();
+ double placeLat = place.getLatLng().latitude;
+ double placeLng = place.getLatLng().longitude;
+ // Build a Geofence object
+ Geofence geofence = new Geofence.Builder()
+ .setRequestId(placeUID)
+ .setExpirationDuration(GEOFENCE_TIMEOUT)
+ .setCircularRegion(placeLat, placeLng, GEOFENCE_RADIUS)
+ .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
+ .build();
+ // Add it to the list
+ mGeofenceList.add(geofence);
+ }
+ }
+
+ /***
+ * Creates a GeofencingRequest object using the mGeofenceList ArrayList of Geofences
+ * Used by {@code #registerGeofences}
+ *
+ * @return the GeofencingRequest object
+ */
+ private GeofencingRequest getGeofencingRequest() {
+ GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
+ builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
+ builder.addGeofences(mGeofenceList);
+ return builder.build();
+ }
+
+ /***
+ * Creates a PendingIntent object using the GeofenceTransitionsIntentService class
+ * Used by {@code #registerGeofences}
+ *
+ * @return the PendingIntent object
+ */
+ private PendingIntent getGeofencePendingIntent() {
+ // Reuse the PendingIntent if we already have it.
+ if (mGeofencePendingIntent != null) {
+ return mGeofencePendingIntent;
+ }
+ Intent intent = new Intent(mContext, GeofenceBroadcastReceiver.class);
+ mGeofencePendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.
+ FLAG_UPDATE_CURRENT);
+ return mGeofencePendingIntent;
+ }
+
+ @Override
+ public void onResult(@NonNull Result result) {
+ Log.e(TAG, String.format("Error adding/removing geofence : %s",
+ result.getStatus().toString()));
+ }
+
+}
diff --git a/app/src/main/java/com/example/android/shushme/MainActivity.java b/app/src/main/java/com/example/android/shushme/MainActivity.java
index b3ccdefd..768eb799 100644
--- a/app/src/main/java/com/example/android/shushme/MainActivity.java
+++ b/app/src/main/java/com/example/android/shushme/MainActivity.java
@@ -18,6 +18,7 @@
import android.content.ContentValues;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
@@ -31,6 +32,8 @@
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.Switch;
import android.widget.Toast;
import com.example.android.shushme.provider.PlaceContract;
@@ -63,7 +66,9 @@ public class MainActivity extends AppCompatActivity implements
// Member variables
private PlaceListAdapter mAdapter;
private RecyclerView mRecyclerView;
+ private boolean mIsEnabled;
private GoogleApiClient mClient;
+ private Geofencing mGeofencing;
/**
* Called when the activity is starting
@@ -81,11 +86,22 @@ protected void onCreate(Bundle savedInstanceState) {
mAdapter = new PlaceListAdapter(this, null);
mRecyclerView.setAdapter(mAdapter);
- // TODO (9) Create a boolean SharedPreference to store the state of the "Enable Geofences" switch
- // and initialize the switch based on the value of that SharedPreference
+ // Initialize the switch state and Handle enable/disable switch change
+ Switch onOffSwitch = (Switch) findViewById(R.id.enable_switch);
+ mIsEnabled = getPreferences(MODE_PRIVATE).getBoolean(getString(R.string.setting_enabled), false);
+ onOffSwitch.setChecked(mIsEnabled);
+ onOffSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit();
+ editor.putBoolean(getString(R.string.setting_enabled), isChecked);
+ mIsEnabled = isChecked;
+ editor.commit();
+ if (isChecked) mGeofencing.registerAllGeofences();
+ else mGeofencing.unRegisterAllGeofences();
+ }
- // TODO (10) Handle the switch's change event and Register/Unregister geofences based on the value of isChecked
- // as well as set a private boolean mIsEnabled to the current switch's state
+ });
// Build up the LocationServices API client
// Uses the addApi method to request the LocationServices API
@@ -98,31 +114,7 @@ protected void onCreate(Bundle savedInstanceState) {
.enableAutoManage(this, this)
.build();
- // TODO (1) Create a Geofencing class with a Context and GoogleApiClient constructor that
- // initializes a private member ArrayList of Geofences called mGeofenceList
-
- // TODO (2) Inside Geofencing, implement a public method called updateGeofencesList that
- // given a PlaceBuffer will create a Geofence object for each Place using Geofence.Builder
- // and add that Geofence to mGeofenceList
-
- // TODO (3) Inside Geofencing, implement a private helper method called getGeofencingRequest that
- // uses GeofencingRequest.Builder to return a GeofencingRequest object from the Geofence list
-
- // TODO (4) Create a GeofenceBroadcastReceiver class that extends BroadcastReceiver and override
- // onReceive() to simply log a message when called. Don't forget to add a receiver tag in the Manifest
-
- // TODO (5) Inside Geofencing, implement a private helper method called getGeofencePendingIntent that
- // returns a PendingIntent for the GeofenceBroadcastReceiver class
-
- // TODO (6) Inside Geofencing, implement a public method called registerAllGeofences that
- // registers the GeofencingRequest by calling LocationServices.GeofencingApi.addGeofences
- // using the helper functions getGeofencingRequest() and getGeofencePendingIntent()
-
- // TODO (7) Inside Geofencing, implement a public method called unRegisterAllGeofences that
- // unregisters all geofences by calling LocationServices.GeofencingApi.removeGeofences
- // using the helper function getGeofencePendingIntent()
-
- // TODO (8) Create a new instance of Geofencing using "this" as the context and mClient as the client
+ mGeofencing = new Geofencing(this, mClient);
}
@@ -177,7 +169,8 @@ public void refreshPlacesData() {
@Override
public void onResult(@NonNull PlaceBuffer places) {
mAdapter.swapPlaces(places);
- // TODO (11) Call updateGeofenceList and registerAllGeofences if mIsEnabled is true
+ mGeofencing.updateGeofencesList(places);
+ if (mIsEnabled) mGeofencing.registerAllGeofences();
}
});
}