package com.hrupin.samples.androidgeofencessample;
import android.Manifest;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
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.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.hrupin.samples.androidgeofencessample.db.GeofenceContract;
import com.hrupin.samples.androidgeofencessample.db.GeofenceStorage;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements
ConnectionCallbacks, OnConnectionFailedListener, OnMapReadyCallback, GoogleMap.OnMapClickListener, GoogleMap.OnMarkerClickListener, GoogleMap.OnInfoWindowClickListener {
private static final String TAG = "MainActivity";
public static final String SHARED_PREFERENCES_NAME = BuildConfig.APPLICATION_ID + ".SHARED_PREFERENCES_NAME";
public static final String NEW_GEOFENCE_NUMBER = BuildConfig.APPLICATION_ID + ".NEW_GEOFENCE_NUMBER";
public static final long GEOFENCE_EXPIRATION_IN_HOURS = 12;
public static final long GEOFENCE_EXPIRATION_IN_MILLISECONDS = GEOFENCE_EXPIRATION_IN_HOURS * 60 * 60 * 1000;
public static final float GEOFENCE_RADIUS_IN_METERS = 100; // 100 m
private static final int PERMISSIONS_REQUEST = 105;
protected GoogleApiClient mGoogleApiClient;
protected ArrayList<Geofence> mGeofenceList;
private PendingIntent mGeofencePendingIntent;
private SharedPreferences mSharedPreferences;
private GoogleMap googleMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MapFragment mapFragment = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
mGeofenceList = new ArrayList<>();
mGeofencePendingIntent = null;
mSharedPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE);
buildGoogleApiClient();
}
/**
* Builds a GoogleApiClient. Uses the {@code #addApi} method to request the LocationServices API.
*/
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
@Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
@Override
protected void onStop() {
super.onStop();
mGoogleApiClient.disconnect();
}
/**
* Runs when a GoogleApiClient object successfully connects.
*/
@Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "Connected to GoogleApiClient");
}
@Override
public void onConnectionFailed(ConnectionResult result) {
// Refer to the javadoc for ConnectionResult to see what error codes might be returned in
// onConnectionFailed.
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
}
@Override
public void onConnectionSuspended(int cause) {
// The connection to Google Play services was lost for some reason.
Log.i(TAG, "Connection suspended");
// onConnected() will be called again automatically when the service reconnects
}
private GeofencingRequest getGeofencingRequest(Geofence geofence) {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
builder.addGeofence(geofence);
return builder.build();
}
private void logSecurityException(SecurityException securityException) {
Log.e(TAG, "Invalid location permission. " +
"You need to use ACCESS_FINE_LOCATION with geofences", securityException);
}
private PendingIntent getGeofencePendingIntent() {
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@Override
public void onMapReady(GoogleMap map) {
googleMap = map;
initMap(googleMap);
}
private void initMap(GoogleMap map) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST);
return;
}
map.setMyLocationEnabled(true);
map.setOnMapClickListener(this);
map.setOnMarkerClickListener(this);
map.setOnInfoWindowClickListener(this);
reloadMapMarkers();
}
private void reloadMapMarkers() {
googleMap.clear();
try (Cursor cursor = GeofenceStorage.getCursor()) {
while (cursor.moveToNext()) {
long expires = Long.parseLong(cursor.getString(cursor.getColumnIndex(GeofenceContract.GeofenceEntry.COLUMN_NAME_EXPIRES)));
if(System.currentTimeMillis() < expires) {
String key = cursor.getString(cursor.getColumnIndex(GeofenceContract.GeofenceEntry.COLUMN_NAME_KEY));
double lat = Double.parseDouble(cursor.getString(cursor.getColumnIndex(GeofenceContract.GeofenceEntry.COLUMN_NAME_LAT)));
double lng = Double.parseDouble(cursor.getString(cursor.getColumnIndex(GeofenceContract.GeofenceEntry.COLUMN_NAME_LNG)));
addMarker(key, new LatLng(lat, lng));
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initMap(googleMap);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST);
}
return;
}
}
}
@Override
public void onMapClick(final LatLng latLng) {
if (!mGoogleApiClient.isConnected()) {
Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show();
return;
}
final String key = getNewGeofenceNumber() + "";
final long expTime = System.currentTimeMillis() + GEOFENCE_EXPIRATION_IN_MILLISECONDS;
addMarker(key, latLng);
Geofence geofence = new Geofence.Builder()
.setRequestId(key)
.setCircularRegion(
latLng.latitude,
latLng.longitude,
GEOFENCE_RADIUS_IN_METERS
)
.setExpirationDuration(GEOFENCE_EXPIRATION_IN_MILLISECONDS)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
Geofence.GEOFENCE_TRANSITION_EXIT)
.build();
try {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
getGeofencingRequest(geofence),
getGeofencePendingIntent()
).setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
GeofenceStorage.saveToDb(key, latLng, expTime);
Toast.makeText(MainActivity.this, getString(R.string.geofences_added), Toast.LENGTH_SHORT).show();
} else {
String errorMessage = GeofenceTransitionsIntentService.getErrorString(MainActivity.this, status.getStatusCode());
Log.e(TAG, errorMessage);
}
}
});
} catch (SecurityException securityException) {
logSecurityException(securityException);
}
}
private void addMarker(String key, LatLng latLng) {
googleMap.addMarker(new MarkerOptions()
.title("G:" + key)
.snippet("Click here if you want delete this geofence")
.position(latLng));
googleMap.addCircle(new CircleOptions()
.center(latLng)
.radius(GEOFENCE_RADIUS_IN_METERS)
.strokeColor(Color.RED)
.fillColor(Color.parseColor("#80ff0000")));
}
private int getNewGeofenceNumber(){
int number = mSharedPreferences.getInt(NEW_GEOFENCE_NUMBER, 0);
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putInt(NEW_GEOFENCE_NUMBER, number + 1);
editor.commit();
return number;
}
@Override
public boolean onMarkerClick(Marker marker) {
return false;
}
@Override
public void onInfoWindowClick(Marker marker) {
final String requestId = marker.getTitle().split(":")[1];
if (!mGoogleApiClient.isConnected()) {
Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show();
return;
}
try {
List<String> idList = new ArrayList<>();
idList.add(requestId);
LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient, idList).setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
GeofenceStorage.removeGeofence(requestId);
Toast.makeText(MainActivity.this, getString(R.string.geofences_removed), Toast.LENGTH_SHORT).show();
reloadMapMarkers();
} else {
// Get the status code for the error and log it using a user-friendly message.
String errorMessage = GeofenceTransitionsIntentService.getErrorString(MainActivity.this,
status.getStatusCode());
Log.e(TAG, errorMessage);
}
}
});
} catch (SecurityException securityException) {
logSecurityException(securityException);
}
}
}