Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
android:authorities="com.example.android.shushme"
android:exported="false"/>

<receiver android:name=".GeofenceBroadcastReceiver" />

</application>

<uses-permission android:name="android.permission.INTERNET" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}
}
168 changes: 168 additions & 0 deletions app/src/main/java/com/example/android/shushme/Geofencing.java
Original file line number Diff line number Diff line change
@@ -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<Geofence> 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()));
}

}
53 changes: 23 additions & 30 deletions app/src/main/java/com/example/android/shushme/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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);

}

Expand Down Expand Up @@ -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();
}
});
}
Expand Down