/* Copyright 2015 Esri * * 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. * * A copy of the license is available in the repository's * https://github.com/Esri/arcgis-runtime-demos-android/blob/master/license.txt * * For information about licensing your deployed app, see * https://developers.arcgis.com/android/guide/license-your-app.htm * */ package com.esri.runtime.android.localgeofence; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.esri.core.geodatabase.Geodatabase; import com.esri.core.geodatabase.GeodatabaseFeatureTable; import com.esri.core.geometry.Polygon; import com.esri.core.map.Feature; import com.esri.core.table.TableException; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; import com.google.android.gms.location.LocationServices; import java.io.File; import java.io.FileNotFoundException; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); // Latest Google Play Services must be installed and available on device. //http://www.zionsoft.net/2014/02/google-play-services-set-up/ private GoogleApiClient mGoogleClient; // Geodatabase and table containing fence features to choose from. public static Geodatabase mGdb = null; public static GeodatabaseFeatureTable mGdbFeatureTable = null; // Define two sets of update intervals, for normal updates, and for fast updates // when device is near the fence. GeofenceService will only be assigned power // blame for the interval set by xx_UPDATE_INTERVAL, but can still receive // locations triggered by other applications at a rate up to // xx_FASTEST_UPDATE_INTERVAL. Note that these are set to suit demo purposes, // but should be tailored to the speciifc usage. public static final int NORMAL_UPDATE_INTERVAL = 1800000; // 30 minutes public static final int NORMAL_FASTEST_UPDATE_INTERVAL = 25000; // 25 seconds public static final int NORMAL_MIN_DISPLACEMENT = 150; // Meters public static final int FAST_UPDATE_INTERVAL = 15000; // 15 seconds public static final int FAST_FASTEST_UPDATE_INTERVAL = 5000; // 5 seconds public static final int FAST_MIN_DISPLACEMENT = 25; // Meters public static final String ACTION_CHECK_LOCATION = "com.esri.arcgis.android.samples.localgeofence.action.CHECK_LOCATION"; public static final String ACTION_START_NORMAL_UPDATES = "com.esri.arcgis.android.samples.localgeofence.action.NORMAL_UPDATES"; public static final String ACTION_START_FAST_UPDATES = "com.esri.arcgis.android.samples.localgeofence.action.FAST_UPDATES"; // Intent Extra IDs public static final String GEOFENCE_FEATURE_OBJECTID_EXTRA_ID = "com.esri.arcgis.android.samples.geometryobjectid"; // Geodatabase of geofences. Set this to any geodatabase, ensuring that the // approproate Layer ID and display field names are also set below. public static final String GEODATABASE_FILEPATH = "/ArcGIS/California/Cali2.geodatabase"; public static final int FENCE_LAYER_ID = 1; public static final String FENCE_NAME_FIELD = "NAME"; public static final String FENCE_OBJECTID_FIELD = "OBJECTID"; // Keys for shared preferences. //public static final String KEY_FENCE_SUBTITLE = "fenceSubtitle"; //public static final String KEY_FENCE_GEOMETRY_OID = "fenceGeometryOid"; EditText mFenceSubtitleEditText; TextView mIntroTextView; TextView mFenceNameTextView; Button mChooseFenceButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mIntroTextView = (TextView)findViewById(R.id.textViewIntro); mFenceNameTextView = (TextView)findViewById(R.id.textViewFenceName); mFenceSubtitleEditText = (EditText)findViewById(R.id.editTextFenceSubtitle); mChooseFenceButton = (Button) findViewById(R.id.buttonChooseName); // Connect to geodatabase boolean haveGdb = setupGeodatabase(); if (! haveGdb) { mChooseFenceButton.setEnabled(false); Toast.makeText(MainActivity.this, "Geofence geodatabase not found", Toast.LENGTH_SHORT).show(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.start_service_menu_item) { startGeofenceService(); return true; } else if (id == R.id.stop_service_menu_item) { stopServices(); return true; } else if (id == R.id.choose_location_map_menu_item) { selectOnMap(); return true; } else if (id == R.id.show_location_menu_item) { showCurrentLocation(); return true; } return super.onOptionsItemSelected(item); } public void onClick_SelectFromList(View view) { selectFromList(); } @Override protected void onDestroy() { super.onDestroy(); if (mGdb != null) { mGdb.dispose(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // Receive results from either map or list geofence selection - both give // exactly the same information. if (data == null) return; if (resultCode == RESULT_OK) { long fenceOid = data.getLongExtra(GEOFENCE_FEATURE_OBJECTID_EXTRA_ID, -1); Feature fenceFeature = getFeatureFromGeodatabase(fenceOid); if (fenceFeature != null) { Polygon fencePolygon = (Polygon)fenceFeature.getGeometry(); String fenceName = fenceFeature.getAttributeValue(FENCE_NAME_FIELD).toString(); LocalGeofence.setFence(fencePolygon, mGdbFeatureTable.getSpatialReference()); LocalGeofence.setFeatureName(fenceName); LocalGeofence.setFeatureOid(fenceOid); mFenceNameTextView.setText(fenceName); } } } /** * Send an intent to start the geofence service that listens to location * updates at a normal rate of frequency. */ private void startGeofenceService() { LocalGeofence.setSubtitle(mFenceSubtitleEditText.getText().toString()); Intent serviceIntent = new Intent(MainActivity.ACTION_START_NORMAL_UPDATES, null, this, GeofenceServiceNormal.class); PendingIntent pendingIntent = PendingIntent.getService(this, 0, serviceIntent, PendingIntent.FLAG_UPDATE_CURRENT); try { pendingIntent.send(); } catch (PendingIntent.CanceledException e) { Toast.makeText(MainActivity.this, "Problem starting geofence service", Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } /** * Unsubscribe both of the geofence services from location updates. */ private void stopServices() { mGoogleClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { @Override public void onConnected(Bundle bundle) { Log.i(TAG, "MainActivity.stopServiceByConnectAndRemove.onConnected"); // Create a PendingIntent for this GeofenceServiceNormal class // and use it to unregister from location updates. Intent fastIntent = new Intent(MainActivity.ACTION_CHECK_LOCATION, null, MainActivity.this, GeofenceServiceFast.class); PendingIntent fastPendingIntent = PendingIntent.getService( MainActivity.this, 0, fastIntent, 0); final PendingResult<Status> cancelFastResult = LocationServices.FusedLocationApi.removeLocationUpdates( mGoogleClient, fastPendingIntent); cancelFastResult.setResultCallback(new ResultCallback<Status>() { @Override public void onResult(Status status) { Log.i(TAG, " removeLocationUpdates PendingResult Status:" + status); Toast.makeText(MainActivity.this, "Stopped Fast Location Updates", Toast.LENGTH_SHORT).show(); } }); Intent normalIntent = new Intent(MainActivity.ACTION_CHECK_LOCATION, null, MainActivity.this, GeofenceServiceNormal.class); PendingIntent normalPendingIntent = PendingIntent.getService( MainActivity.this, 0, normalIntent, 0); final PendingResult<Status> cancelNormalResult = LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleClient, normalPendingIntent); cancelNormalResult.setResultCallback(new ResultCallback<Status>() { @Override public void onResult(Status status) { Log.i(TAG, " removeLocationUpdates PendingResult Status:" + status); Toast.makeText(MainActivity.this, "Stopped Normal Location Updates", Toast.LENGTH_SHORT).show(); } }); // Disconnect the client again, no longer required unless user // wants to start updates again. mGoogleClient.disconnect(); mGoogleClient = null; } @Override public void onConnectionSuspended(int i) { } }) .build(); mGoogleClient.connect(); } /** * Start an activity that allows the user to select a geofence feature from a list. */ private void selectFromList() { Intent geofenceListIntent = new Intent(MainActivity.this, GeofenceListActivity.class); // If working against multiple layers, use Extras to send layer IDs. startActivityForResult(geofenceListIntent, FENCE_LAYER_ID); } /** * Start an activity that allows the user to select a geofence feature on a map. */ private void selectOnMap() { Intent geofenceMapIntent = new Intent(MainActivity.this, GeofenceMapActivity.class); // If working against multiple layers, use Extras to send layer IDs. startActivityForResult(geofenceMapIntent, FENCE_LAYER_ID); } /** * Start an activity that shows the users current location, so they can track * progress. */ private void showCurrentLocation() { Intent currentLocationIntent = new Intent(MainActivity.this, CurrentLocationActivity.class); // No result is required as we dont need any data from this activity. startActivity(currentLocationIntent); } /** * Get a feature from the geodatabase by its ObjectID.s */ private Feature getFeatureFromGeodatabase(long featureOid) { if (featureOid < 0) { return null; } Feature feature = null; try { feature = mGdbFeatureTable.getFeature(featureOid); } catch (TableException e) { e.printStackTrace(); } return feature; } /** * Open the geodatabase of geofence features. * @return true if geodatabase was opened, otherwise false. */ private boolean setupGeodatabase() { File rootFile = Environment.getExternalStorageDirectory(); File dataFile = new File(rootFile, GEODATABASE_FILEPATH); if (! dataFile.exists()) return false; String filePath = dataFile.getAbsolutePath(); try { mGdb = new Geodatabase(filePath); } catch (FileNotFoundException e) { e.printStackTrace(); return false; } if (mGdb == null) return false; mGdbFeatureTable = mGdb.getGeodatabaseFeatureTableByLayerId(FENCE_LAYER_ID); return (mGdbFeatureTable != null); } }