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); } } }