diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java new file mode 100644 index 000000000..ccac33d5a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java @@ -0,0 +1,74 @@ +package com.mapbox.mapboxsdk.telemetry; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.location.Location; +import android.location.LocationManager; +import android.media.AudioManager; +import android.media.ToneGenerator; +import android.os.Handler; +import android.util.Log; + +public class TelemetryLocationReceiver extends BroadcastReceiver { + + private static final String TAG = "TelemLocationReceiver"; + + /** + * Default Constructor + */ + public TelemetryLocationReceiver() { + super(); + } + + /** + * This method is called when the BroadcastReceiver is receiving an Intent + * broadcast. During this time you can use the other methods on + * BroadcastReceiver to view/modify the current result values. This method + * is always called within the main thread of its process, unless you + * explicitly asked for it to be scheduled on a different thread using + * {@link Context#registerReceiver(BroadcastReceiver, + * IntentFilter, String, Handler)}. When it runs on the main + * thread you should + * never perform long-running operations in it (there is a timeout of + * 10 seconds that the system allows before considering the receiver to + * be blocked and a candidate to be killed). You cannot launch a popup dialog + * in your implementation of onReceive(). + *
+ *If this BroadcastReceiver was launched through a <receiver> tag, + * then the object is no longer alive after returning from this + * function. This means you should not perform any operations that + * return a result to you asynchronously -- in particular, for interacting + * with services, you should use + * {@link Context#startService(Intent)} instead of + * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish + * to interact with a service that is already running, you can use + * {@link #peekService}. + *
+ *The Intent filters used in {@link Context#registerReceiver} + * and in application manifests are not guaranteed to be exclusive. They + * are hints to the operating system about how to find suitable recipients. It is + * possible for senders to force delivery to specific recipients, bypassing filter + * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()} + * implementations should respond only to known actions, ignoring any unexpected + * Intents that they may receive. + * + * @param context The Context in which the receiver is running. + * @param intent The Intent being received. + */ + @Override + public void onReceive(Context context, Intent intent) { + + Location location = (Location)intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED); + if (location != null) { + Log.i(TAG, "location received = " + location); + final ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100); + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + tg.stopTone(); + tg.release(); + } else { + Log.i(TAG, "location NOT received"); + } + + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java new file mode 100644 index 000000000..c49eb1aa3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java @@ -0,0 +1,162 @@ +package com.mapbox.mapboxsdk.telemetry; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ServiceInfo; +import android.os.AsyncTask; +import android.os.IBinder; +import android.os.PowerManager; +import android.support.annotation.Nullable; +import android.util.Log; + +public class TelemetryService extends Service { + + private static final String TAG = "TelemetryService"; + + private TelemetryLocationReceiver telemetryLocationReceiver = null; + private PendingIntent locationPendingIntent = null; + private PowerManager.WakeLock telemetryWakeLock; + + /** + * Return the communication channel to the service. May return null if + * clients can not bind to the service. The returned + * {@link IBinder} is usually for a complex interface + * that has been described using + * aidl. + *
+ *Note that unlike other application components, calls on to the + * IBinder interface returned here may not happen on the main thread + * of the process. More information about the main thread can be found in + * Processes and + * Threads.
+ * + * @param intent The Intent that was used to bind to this service, + * as given to {@link Context#bindService + * Context.bindService}. Note that any extras that were included with + * the Intent at that point will not be seen here. + * @return Return an IBinder through which clients can call on to the + * service. + */ + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + + /** + * Called by the system when the service is first created. Do not call this method directly. + */ + @Override + public void onCreate() { + super.onCreate(); + + // Enable Location Listening for lifecycle of app + IntentFilter filter = new IntentFilter("com.mapbox.mapboxsdk.telemetry.TelemetryLocationReceiver"); + telemetryLocationReceiver = new TelemetryLocationReceiver(); + registerReceiver(telemetryLocationReceiver, filter); + } + + /** + * Called by the system to notify a Service that it is no longer used and is being removed. The + * service should clean up any resources it holds (threads, registered + * receivers, etc) at this point. Upon return, there will be no more calls + * in to this Service object and it is effectively dead. Do not call this method directly. + */ + @Override + public void onDestroy() { + shutdownTelemetry(); + super.onDestroy(); + } + + /** + * This is called if the service is currently running and the user has + * removed a task that comes from the service's application. If you have + * set {@link ServiceInfo#FLAG_STOP_WITH_TASK ServiceInfo.FLAG_STOP_WITH_TASK} + * then you will not receive this callback; instead, the service will simply + * be stopped. + * + * @param rootIntent The original root Intent that was used to launch + * the task that is being removed. + */ + @Override + public void onTaskRemoved(Intent rootIntent) { + shutdownTelemetry(); + super.onTaskRemoved(rootIntent); + } + + /** + * Called by the system every time a client explicitly starts the service by calling + * {@link Context#startService}, providing the arguments it supplied and a + * unique integer token representing the start request. Do not call this method directly. + * + *For backwards compatibility, the default implementation calls + * {@link #onStart} and returns either {@link #START_STICKY} + * or {@link #START_STICKY_COMPATIBILITY}. + *
+ *If you need your application to run on platform versions prior to API
+ * level 5, you can use the following model to handle the older {@link #onStart}
+ * callback in that case. The handleCommand method is implemented by
+ * you as appropriate:
+ *
Note that the system calls this on your + * service's main thread. A service's main thread is the same + * thread where UI operations take place for Activities running in the + * same process. You should always avoid stalling the main + * thread's event loop. When doing long-running operations, + * network calls, or heavy disk I/O, you should kick off a new + * thread, or use {@link AsyncTask}.
+ * + * @param intent The Intent supplied to {@link Context#startService}, + * as given. This may be null if the service is being restarted after + * its process has gone away, and it had previously returned anything + * except {@link #START_STICKY_COMPATIBILITY}. + * @param flags Additional data about this start request. Currently either + * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}. + * @param startId A unique integer representing this specific request to + * start. Use with {@link #stopSelfResult(int)}. + * @return The return value indicates what semantics the system should + * use for the service's current started state. It may be one of the + * constants associated with the {@link #START_CONTINUATION_MASK} bits. + * @see #stopSelfResult(int) + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + + // Start WakeLock to keep Location Data working when device sleeps + PowerManager mgr = (PowerManager)getSystemService(Context.POWER_SERVICE); + telemetryWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TelemetryWakeLock"); + telemetryWakeLock.acquire(); + + // Start Ambient Location Listening + // TODO + + Intent locIntent = new Intent("com.mapbox.hermes.LocationReceiver"); + locationPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 14872, locIntent, PendingIntent.FLAG_CANCEL_CURRENT); + try { + // Request Location Updates to be delivered to PendingIntent + // Currently Not supported in LOST + // TODO +// locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 3.0f, mLocationPendingIntent); + } catch (SecurityException e) { + Log.e(TAG, "Permissions Not Authorized For Location Updates: " + e); + } + + + return START_NOT_STICKY; + } + + private void shutdownTelemetry() { + // TODO +// locationManager.removeUpdates(mLocationPendingIntent); + locationPendingIntent.cancel(); + unregisterReceiver(telemetryLocationReceiver); + telemetryWakeLock.release(); + } +}