[android] Extract telemetry into MAS
* [android] add the mapbox-android-telemetry module dependency. * update javadoc
This commit is contained in:
@@ -8,8 +8,11 @@ import android.text.TextUtils;
|
||||
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
|
||||
import com.mapbox.mapboxsdk.location.LocationSource;
|
||||
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
|
||||
import com.mapbox.mapboxsdk.telemetry.MapboxEventManager;
|
||||
import com.mapbox.services.android.telemetry.MapboxTelemetry;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEngine;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
|
||||
|
||||
public final class Mapbox {
|
||||
|
||||
@@ -32,7 +35,9 @@ public final class Mapbox {
|
||||
if (INSTANCE == null) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
INSTANCE = new Mapbox(appContext, accessToken);
|
||||
MapboxEventManager.getMapboxEventManager().initialize(appContext, accessToken);
|
||||
LocationEngine locationEngine = new LocationSource(appContext);
|
||||
locationEngine.setPriority(LocationEnginePriority.NO_POWER);
|
||||
MapboxTelemetry.getInstance().initialize(appContext, accessToken, locationEngine);
|
||||
ConnectivityReceiver.instance(appContext);
|
||||
}
|
||||
return INSTANCE;
|
||||
|
||||
@@ -7,9 +7,7 @@ import android.os.Parcelable;
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.utils.MathUtils;
|
||||
|
||||
import static com.mapbox.mapboxsdk.utils.MathUtils.convertNativeBearing;
|
||||
import com.mapbox.services.android.telemetry.utils.MathUtils;
|
||||
|
||||
/**
|
||||
* Resembles the position, angle, zoom and tilt of the user's viewpoint.
|
||||
@@ -245,7 +243,7 @@ public final class CameraPosition implements Parcelable {
|
||||
super();
|
||||
if (nativeCameraValues != null && nativeCameraValues.length == 5) {
|
||||
target(new LatLng(nativeCameraValues[0], nativeCameraValues[1]));
|
||||
bearing(convertNativeBearing(nativeCameraValues[2]));
|
||||
bearing(MathUtils.convertNativeBearing(nativeCameraValues[2]));
|
||||
tilt(nativeCameraValues[3]);
|
||||
zoom(nativeCameraValues[4]);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
import com.mapbox.mapboxsdk.maps.UiSettings;
|
||||
import com.mapbox.mapboxsdk.utils.MathUtils;
|
||||
import com.mapbox.services.android.telemetry.utils.MathUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
/**
|
||||
* GeoConstants exposes constants for doing locational calculations on Earth
|
||||
*/
|
||||
public class GeoConstants {
|
||||
|
||||
/**
|
||||
* The <a href='http://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius'>equatorial radius</a>
|
||||
* value in meters
|
||||
*/
|
||||
public static final int RADIUS_EARTH_METERS = 6378137;
|
||||
|
||||
/**
|
||||
* The minimum latitude on Earth. This is the minimum latitude representable
|
||||
* by Mapbox GL's Mercator projection, because the projection distorts latitude
|
||||
* near the poles towards infinity.
|
||||
*/
|
||||
public static final double MIN_LATITUDE = -85.05112878;
|
||||
|
||||
/**
|
||||
* The maximum latitude on Earth. This is the maximum latitude representable
|
||||
* by Mapbox GL's Mercator projection, because the projection distorts latitude
|
||||
* near the poles towards infinity.
|
||||
*/
|
||||
public static final double MAX_LATITUDE = 85.05112878;
|
||||
|
||||
/**
|
||||
* The minimum longitude on Earth
|
||||
*/
|
||||
public static final double MIN_LONGITUDE = -180;
|
||||
|
||||
/**
|
||||
* The maximum longitude on Earth
|
||||
*/
|
||||
public static final double MAX_LONGITUDE = 180;
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
|
||||
/**
|
||||
* A {@code TelemetryServiceNotConfiguredException} is thrown by {@link MapView} when it checks and finds that
|
||||
* TelemetryService has not been configured in the app's AndroidManifest.xml {@link MapView#onCreate(Bundle)}
|
||||
*
|
||||
* @see MapView#onCreate(Bundle)
|
||||
*/
|
||||
public class TelemetryServiceNotConfiguredException extends RuntimeException {
|
||||
|
||||
public TelemetryServiceNotConfiguredException() {
|
||||
super("\nTelemetryService is not configured in your applications AndroidManifest.xml. "
|
||||
+ "\nPlease add \"com.mapbox.mapboxsdk.telemetry.TelemetryService\" service in your applications "
|
||||
+ "AndroidManifest.xml"
|
||||
+ "\nFor an example visit http://goo.gl/cET0Jn. For more information visit https://www.mapbox.com/android-sdk/.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import android.location.Location;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.mapbox.mapboxsdk.constants.GeoConstants;
|
||||
import com.mapbox.mapboxsdk.utils.MathUtils;
|
||||
import com.mapbox.services.android.telemetry.constants.GeoConstants;
|
||||
import com.mapbox.services.android.telemetry.utils.MathUtils;
|
||||
|
||||
/**
|
||||
* A geographical location which contains a single latitude, longitude pair, with
|
||||
|
||||
@@ -183,7 +183,7 @@ class HTTPRequest implements Callback {
|
||||
return USER_AGENT_STRING = Util.toHumanReadableAscii(
|
||||
String.format("%s %s (%s) Android/%s (%s)",
|
||||
getApplicationIdentifier(),
|
||||
BuildConfig.MAPBOX_VERSION_STRING,
|
||||
com.mapbox.services.android.telemetry.BuildConfig.MAPBOX_VERSION_STRING,
|
||||
BuildConfig.GIT_REVISION_SHORT,
|
||||
Build.VERSION.SDK_INT,
|
||||
Build.CPU_ABI)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
/**
|
||||
* Callback interface for when a location change occurs.
|
||||
*/
|
||||
public interface LocationListener {
|
||||
|
||||
/**
|
||||
* Callback method for receiving location updates from LocationServices.
|
||||
*
|
||||
* @param location The new Location data
|
||||
*/
|
||||
void onLocationChanged(Location location);
|
||||
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import com.mapbox.mapboxsdk.telemetry.TelemetryLocationReceiver;
|
||||
import com.mapzen.android.lost.api.LocationRequest;
|
||||
import com.mapzen.android.lost.api.LostApiClient;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static com.mapzen.android.lost.api.LocationServices.FusedLocationApi;
|
||||
|
||||
/**
|
||||
* Manages locational updates. Contains methods to register and unregister location listeners.
|
||||
* <ul>
|
||||
* <li>You can register a {@link LocationListener} with {@link #addLocationListener(LocationListener)} to receive
|
||||
* location updates.</li>
|
||||
* <li> You can unregister a {@link LocationListener} with {@link #removeLocationListener(LocationListener)}.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Note: If registering a listener in your Activity.onStart() implementation, you should unregister it in
|
||||
* Activity.onStop(). (You won't receive location updates when paused, and this will cut down on unnecessary system
|
||||
* overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back
|
||||
* in the history stack.
|
||||
* </p>
|
||||
*/
|
||||
public class LocationServices implements LostApiClient.ConnectionCallbacks,
|
||||
com.mapzen.android.lost.api.LocationListener {
|
||||
|
||||
private static LocationServices instance;
|
||||
|
||||
private Context context;
|
||||
private LostApiClient locationClient;
|
||||
private Location lastLocation;
|
||||
|
||||
private CopyOnWriteArrayList<LocationListener> locationListeners;
|
||||
|
||||
private boolean isGpsEnabled;
|
||||
|
||||
/**
|
||||
* Private constructor for singleton LocationServices
|
||||
*/
|
||||
private LocationServices(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
// Setup location services
|
||||
locationClient = new LostApiClient.Builder(context).addConnectionCallbacks(this).build();
|
||||
locationListeners = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary (singleton) access method for LocationServices
|
||||
*
|
||||
* @param context Context
|
||||
* @return LocationServices
|
||||
*/
|
||||
public static LocationServices getLocationServices(@NonNull final Context context) {
|
||||
if (instance == null) {
|
||||
instance = new LocationServices(context.getApplicationContext());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enabled / Disable GPS focused location tracking
|
||||
*
|
||||
* @param enableGPS true if GPS is to be enabled, false if GPS is to be disabled
|
||||
*/
|
||||
public void toggleGPS(boolean enableGPS) {
|
||||
if (!areLocationPermissionsGranted()) {
|
||||
Timber.w("Location Permissions Not Granted Yet. Try again after requesting.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Disconnect
|
||||
if (locationClient.isConnected()) {
|
||||
// Disconnect first to ensure that the new requests are GPS
|
||||
FusedLocationApi.removeLocationUpdates(locationClient, this);
|
||||
locationClient.disconnect();
|
||||
}
|
||||
isGpsEnabled = enableGPS;
|
||||
|
||||
// Setup Fresh
|
||||
locationClient.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
// noinspection MissingPermission
|
||||
Location lastLocation = FusedLocationApi.getLastLocation(locationClient);
|
||||
if (lastLocation != null) {
|
||||
this.lastLocation = lastLocation;
|
||||
}
|
||||
|
||||
LocationRequest locationRequest;
|
||||
|
||||
if (isGpsEnabled) {
|
||||
// LocationRequest Tuned for GPS
|
||||
locationRequest = LocationRequest.create()
|
||||
.setFastestInterval(1000)
|
||||
.setSmallestDisplacement(3.0f)
|
||||
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
|
||||
// noinspection MissingPermission
|
||||
FusedLocationApi.requestLocationUpdates(locationClient, locationRequest, this);
|
||||
} else {
|
||||
// LocationRequest Tuned for PASSIVE
|
||||
locationRequest = LocationRequest.create()
|
||||
.setFastestInterval(1000)
|
||||
.setSmallestDisplacement(3.0f)
|
||||
.setPriority(LocationRequest.PRIORITY_NO_POWER);
|
||||
// noinspection MissingPermission
|
||||
FusedLocationApi.requestLocationUpdates(locationClient, locationRequest, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the GPS sensor is currently enabled
|
||||
*
|
||||
* @return active state of the GPS
|
||||
*/
|
||||
public boolean isGpsEnabled() {
|
||||
return isGpsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the location has changed.
|
||||
*
|
||||
* @param location The updated location
|
||||
*/
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
// Timber.d("onLocationChanged()..." + location);
|
||||
this.lastLocation = location;
|
||||
|
||||
// Update Listeners
|
||||
for (LocationListener listener : this.locationListeners) {
|
||||
listener.onLocationChanged(location);
|
||||
}
|
||||
|
||||
// Update the Telemetry Receiver
|
||||
Intent locIntent = new Intent(TelemetryLocationReceiver.INTENT_STRING);
|
||||
locIntent.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
|
||||
LocalBroadcastManager.getInstance(context.getApplicationContext()).sendBroadcast(locIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Last known location
|
||||
*
|
||||
* @return Last known location data
|
||||
*/
|
||||
public Location getLastLocation() {
|
||||
return lastLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a LocationListener to receive location updates
|
||||
*
|
||||
* @param locationListener LocationListener
|
||||
*/
|
||||
public void addLocationListener(@NonNull LocationListener locationListener) {
|
||||
if (!this.locationListeners.contains(locationListener)) {
|
||||
this.locationListeners.add(locationListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a LocationListener to stop receiving location updates
|
||||
*
|
||||
* @param locationListener LocationListener to remove
|
||||
* @return True if LocationListener was found and removed, False if it was not
|
||||
*/
|
||||
public boolean removeLocationListener(@NonNull LocationListener locationListener) {
|
||||
return this.locationListeners.remove(locationListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check status of Location Permissions
|
||||
*
|
||||
* @return True if granted to the app, False if not
|
||||
*/
|
||||
public boolean areLocationPermissionsGranted() {
|
||||
if ((ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
!= PackageManager.PERMISSION_GRANTED)
|
||||
&& (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
!= PackageManager.PERMISSION_GRANTED)) {
|
||||
Timber.w("Location Permissions Not Granted Yet. Try again after requesting.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import com.mapbox.services.android.telemetry.location.LocationEngine;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEngineListener;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
|
||||
import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
|
||||
import com.mapzen.android.lost.api.LocationListener;
|
||||
import com.mapzen.android.lost.api.LocationRequest;
|
||||
import com.mapzen.android.lost.api.LocationServices;
|
||||
import com.mapzen.android.lost.api.LostApiClient;
|
||||
|
||||
/**
|
||||
* Manages locational updates. Contains methods to register and unregister location listeners.
|
||||
* <ul>
|
||||
* <li>You can register a {@link LocationEngineListener} with
|
||||
* {@link #addLocationEngineListener(LocationEngineListener)} to receive
|
||||
* location updates.</li>
|
||||
* <li> You can unregister a {@link LocationEngineListener} with
|
||||
* {@link #removeLocationEngineListener(LocationEngineListener)} to stop receiving location updates.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Note: If registering a listener in your Activity.onStart() implementation, you should unregister it in
|
||||
* Activity.onStop(). (You won't receive location updates when paused, and this will cut down on unnecessary system
|
||||
* overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back
|
||||
* in the history stack.
|
||||
* </p>
|
||||
*/
|
||||
public class LocationSource extends LocationEngine implements
|
||||
LostApiClient.ConnectionCallbacks, LocationListener {
|
||||
|
||||
private static final String LOG_TAG = LocationSource.class.getSimpleName();
|
||||
|
||||
private static LocationEngine instance;
|
||||
|
||||
private Context context;
|
||||
private LostApiClient lostApiClient;
|
||||
|
||||
public LocationSource(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
lostApiClient = new LostApiClient.Builder(context)
|
||||
.addConnectionCallbacks(this)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static synchronized LocationEngine getLocationEngine(Context context) {
|
||||
if (instance == null) {
|
||||
instance = new LocationSource(context.getApplicationContext());
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
if (lostApiClient != null && !lostApiClient.isConnected()) {
|
||||
lostApiClient.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
if (lostApiClient != null && lostApiClient.isConnected()) {
|
||||
lostApiClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return lostApiClient.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
for (LocationEngineListener listener : locationListeners) {
|
||||
listener.onConnected();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended() {
|
||||
Log.d(LOG_TAG, "Connection suspended.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLastLocation() {
|
||||
if (lostApiClient.isConnected() && PermissionsManager.areLocationPermissionsGranted(context)) {
|
||||
//noinspection MissingPermission
|
||||
return LocationServices.FusedLocationApi.getLastLocation(lostApiClient);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestLocationUpdates() {
|
||||
// Common params
|
||||
LocationRequest request = LocationRequest.create()
|
||||
.setFastestInterval(1000)
|
||||
.setSmallestDisplacement(3.0f);
|
||||
|
||||
// Priority matching is straightforward
|
||||
if (priority == LocationEnginePriority.NO_POWER) {
|
||||
request.setPriority(LocationRequest.PRIORITY_NO_POWER);
|
||||
} else if (priority == LocationEnginePriority.LOW_POWER) {
|
||||
request.setPriority(LocationRequest.PRIORITY_LOW_POWER);
|
||||
} else if (priority == LocationEnginePriority.BALANCED_POWER_ACCURACY) {
|
||||
request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
|
||||
} else if (priority == LocationEnginePriority.HIGH_ACCURACY) {
|
||||
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
|
||||
}
|
||||
|
||||
if (lostApiClient.isConnected() && PermissionsManager.areLocationPermissionsGranted(context)) {
|
||||
//noinspection MissingPermission
|
||||
LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocationUpdates() {
|
||||
LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
for (LocationEngineListener listener : locationListeners) {
|
||||
listener.onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
Log.d(LOG_TAG, "Provider disabled: " + provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
Log.d(LOG_TAG, "Provider enabled: " + provider);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.maps;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.location.Location;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.view.GestureDetectorCompat;
|
||||
import android.support.v4.view.ScaleGestureDetectorCompat;
|
||||
@@ -15,8 +16,11 @@ import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector;
|
||||
import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector;
|
||||
import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.telemetry.MapboxEvent;
|
||||
import com.mapbox.mapboxsdk.utils.MathUtils;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.services.android.telemetry.MapboxEvent;
|
||||
import com.mapbox.services.android.telemetry.MapboxTelemetry;
|
||||
import com.mapbox.services.android.telemetry.utils.MathUtils;
|
||||
import com.mapbox.services.android.telemetry.utils.TelemetryUtils;
|
||||
|
||||
/**
|
||||
* Manages gestures events on a MapView.
|
||||
@@ -88,6 +92,18 @@ final class MapGestureDetector {
|
||||
this.focalPoint = focalPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given coordinates from a gesture, use the current projection to translate it into
|
||||
* a Location object.
|
||||
*
|
||||
* @param x coordinate
|
||||
* @param y coordinate
|
||||
* @return location
|
||||
*/
|
||||
private Location getLocationFromGesture(float x, float y) {
|
||||
LatLng latLng = projection.fromScreenLocation(new PointF(x, y));
|
||||
return TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when user touches the screen, all positions are absolute.
|
||||
@@ -123,8 +139,9 @@ final class MapGestureDetector {
|
||||
&& uiSettings.isZoomGesturesEnabled();
|
||||
if (twoTap) {
|
||||
// Confirmed 2nd Finger Down
|
||||
MapboxEvent.trackGestureEvent(projection,
|
||||
MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, event.getX(), event.getY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent(
|
||||
getLocationFromGesture(event.getX(), event.getY()),
|
||||
MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform.getZoom()));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -153,7 +170,8 @@ final class MapGestureDetector {
|
||||
|
||||
// Scroll / Pan Has Stopped
|
||||
if (scrollInProgress) {
|
||||
MapboxEvent.trackGestureDragEndEvent(projection, event.getX(), event.getY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapDragEndEvent(
|
||||
getLocationFromGesture(event.getX(), event.getY()), transform.getZoom()));
|
||||
scrollInProgress = false;
|
||||
}
|
||||
|
||||
@@ -252,7 +270,9 @@ final class MapGestureDetector {
|
||||
break;
|
||||
}
|
||||
|
||||
MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_DOUBLETAP, e.getX(), e.getY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent(
|
||||
getLocationFromGesture(e.getX(), e.getY()),
|
||||
MapboxEvent.GESTURE_DOUBLETAP, transform.getZoom()));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -281,8 +301,10 @@ final class MapGestureDetector {
|
||||
}
|
||||
}
|
||||
|
||||
MapboxEvent.trackGestureEvent(projection,
|
||||
MapboxEvent.GESTURE_SINGLETAP, motionEvent.getX(), motionEvent.getY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent(
|
||||
getLocationFromGesture(motionEvent.getX(), motionEvent.getY()),
|
||||
MapboxEvent.GESTURE_SINGLETAP, transform.getZoom()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -341,8 +363,9 @@ final class MapGestureDetector {
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
if (!scrollInProgress) {
|
||||
scrollInProgress = true;
|
||||
MapboxEvent.trackGestureEvent(projection,
|
||||
MapboxEvent.GESTURE_PAN_START, e1.getX(), e1.getY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent(
|
||||
getLocationFromGesture(e1.getX(), e1.getY()),
|
||||
MapboxEvent.GESTURE_PAN_START, transform.getZoom()));
|
||||
}
|
||||
if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
|
||||
return false;
|
||||
@@ -384,8 +407,9 @@ final class MapGestureDetector {
|
||||
|
||||
scaleGestureOccurred = true;
|
||||
beginTime = detector.getEventTime();
|
||||
MapboxEvent.trackGestureEvent(projection,
|
||||
MapboxEvent.GESTURE_PINCH_START, detector.getFocusX(), detector.getFocusY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent(
|
||||
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
|
||||
MapboxEvent.GESTURE_PINCH_START, transform.getZoom()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -475,8 +499,9 @@ final class MapGestureDetector {
|
||||
}
|
||||
|
||||
beginTime = detector.getEventTime();
|
||||
MapboxEvent.trackGestureEvent(projection,
|
||||
MapboxEvent.GESTURE_ROTATION_START, detector.getFocusX(), detector.getFocusY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent(
|
||||
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
|
||||
MapboxEvent.GESTURE_ROTATION_START, transform.getZoom()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -555,8 +580,9 @@ final class MapGestureDetector {
|
||||
}
|
||||
|
||||
beginTime = detector.getEventTime();
|
||||
MapboxEvent.trackGestureEvent(projection,
|
||||
MapboxEvent.GESTURE_PITCH_START, detector.getFocusX(), detector.getFocusY(), transform.getZoom());
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent(
|
||||
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
|
||||
MapboxEvent.GESTURE_PITCH_START, transform.getZoom()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ import com.mapbox.mapboxsdk.constants.Style;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
|
||||
import com.mapbox.mapboxsdk.telemetry.MapboxEvent;
|
||||
import com.mapbox.mapboxsdk.telemetry.MapboxEventManager;
|
||||
import com.mapbox.services.android.telemetry.MapboxEvent;
|
||||
import com.mapbox.services.android.telemetry.MapboxTelemetry;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -205,7 +205,7 @@ public class MapView extends FrameLayout {
|
||||
nativeMapView.setAccessToken(Mapbox.getAccessToken());
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
MapboxEvent.trackMapLoadEvent();
|
||||
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapLoadEvent());
|
||||
} else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
|
||||
mapboxMap.onRestoreInstanceState(savedInstanceState);
|
||||
}
|
||||
@@ -666,7 +666,7 @@ public class MapView extends FrameLayout {
|
||||
builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
MapboxEventManager.getMapboxEventManager().setTelemetryEnabled(true);
|
||||
MapboxTelemetry.getInstance().setTelemetryEnabled(true);
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
@@ -683,7 +683,7 @@ public class MapView extends FrameLayout {
|
||||
builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
MapboxEventManager.getMapboxEventManager().setTelemetryEnabled(false);
|
||||
MapboxTelemetry.getInstance().setTelemetryEnabled(false);
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
package com.mapbox.mapboxsdk.maps;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
|
||||
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
|
||||
import com.mapbox.mapboxsdk.location.LocationListener;
|
||||
import com.mapbox.mapboxsdk.location.LocationServices;
|
||||
import com.mapbox.mapboxsdk.location.LocationSource;
|
||||
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEngineListener;
|
||||
import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -28,7 +26,7 @@ public final class TrackingSettings {
|
||||
private final UiSettings uiSettings;
|
||||
private final FocalPointChangeListener focalPointChangedListener;
|
||||
private final CameraZoomInvalidator zoomInvalidator;
|
||||
private LocationListener myLocationListener;
|
||||
private LocationEngineListener myLocationListener;
|
||||
|
||||
private boolean myLocationEnabled;
|
||||
private boolean dismissLocationTrackingOnGesture = true;
|
||||
@@ -317,7 +315,12 @@ public final class TrackingSettings {
|
||||
|
||||
void setOnMyLocationChangeListener(@Nullable final MapboxMap.OnMyLocationChangeListener listener) {
|
||||
if (listener != null) {
|
||||
myLocationListener = new LocationListener() {
|
||||
myLocationListener = new LocationEngineListener() {
|
||||
@Override
|
||||
public void onConnected() {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
if (listener != null) {
|
||||
@@ -325,20 +328,13 @@ public final class TrackingSettings {
|
||||
}
|
||||
}
|
||||
};
|
||||
LocationServices.getLocationServices(myLocationView.getContext()).addLocationListener(myLocationListener);
|
||||
LocationSource.getLocationEngine(myLocationView.getContext()).addLocationEngineListener(myLocationListener);
|
||||
} else {
|
||||
LocationServices.getLocationServices(myLocationView.getContext()).removeLocationListener(myLocationListener);
|
||||
LocationSource.getLocationEngine(myLocationView.getContext()).removeLocationEngineListener(myLocationListener);
|
||||
myLocationListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isPermissionsAccepted() {
|
||||
return (ContextCompat.checkSelfPermission(myLocationView.getContext(),
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
|
||||
|| ContextCompat.checkSelfPermission(myLocationView.getContext(),
|
||||
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
void setOnMyLocationTrackingModeChangeListener(MapboxMap.OnMyLocationTrackingModeChangeListener listener) {
|
||||
this.onMyLocationTrackingModeChangeListener = listener;
|
||||
}
|
||||
@@ -357,7 +353,7 @@ public final class TrackingSettings {
|
||||
}
|
||||
|
||||
void setMyLocationEnabled(boolean locationEnabled) {
|
||||
if (!isPermissionsAccepted()) {
|
||||
if (!PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) {
|
||||
Timber.e("Could not activate user location tracking: "
|
||||
+ "user did not accept the permission or permissions were not requested.");
|
||||
return;
|
||||
|
||||
@@ -31,10 +31,12 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
|
||||
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
|
||||
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.location.LocationListener;
|
||||
import com.mapbox.mapboxsdk.location.LocationServices;
|
||||
import com.mapbox.mapboxsdk.location.LocationSource;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEngine;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEngineListener;
|
||||
import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@@ -367,8 +369,7 @@ public class MyLocationView extends View {
|
||||
}
|
||||
|
||||
if (userLocationListener != null) {
|
||||
LocationServices services = LocationServices.getLocationServices(getContext());
|
||||
services.removeLocationListener(userLocationListener);
|
||||
LocationSource.getLocationEngine(getContext()).removeLocationEngineListener(userLocationListener);
|
||||
userLocationListener = null;
|
||||
}
|
||||
}
|
||||
@@ -418,10 +419,10 @@ public class MyLocationView extends View {
|
||||
* @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
|
||||
*/
|
||||
private void toggleGps(boolean enableGps) {
|
||||
LocationServices locationServices = LocationServices.getLocationServices(getContext());
|
||||
LocationEngine locationEngine = LocationSource.getLocationEngine(getContext());
|
||||
if (enableGps) {
|
||||
// Set an initial location if one available
|
||||
Location lastLocation = locationServices.getLastLocation();
|
||||
Location lastLocation = locationEngine.getLastLocation();
|
||||
|
||||
if (lastLocation != null) {
|
||||
setLocation(lastLocation);
|
||||
@@ -431,14 +432,14 @@ public class MyLocationView extends View {
|
||||
userLocationListener = new GpsLocationListener(this);
|
||||
}
|
||||
|
||||
locationServices.addLocationListener(userLocationListener);
|
||||
locationEngine.addLocationEngineListener(userLocationListener);
|
||||
} else {
|
||||
// Disable location and user dot
|
||||
location = null;
|
||||
locationServices.removeLocationListener(userLocationListener);
|
||||
locationEngine.removeLocationEngineListener(userLocationListener);
|
||||
}
|
||||
|
||||
locationServices.toggleGPS(enableGps);
|
||||
locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
@@ -557,7 +558,7 @@ public class MyLocationView extends View {
|
||||
contentPaddingY = (padding[1] - padding[3]) / 2;
|
||||
}
|
||||
|
||||
private static class GpsLocationListener implements LocationListener {
|
||||
private static class GpsLocationListener implements LocationEngineListener {
|
||||
|
||||
private WeakReference<MyLocationView> userLocationView;
|
||||
|
||||
@@ -565,6 +566,15 @@ public class MyLocationView extends View {
|
||||
userLocationView = new WeakReference<>(myLocationView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
MyLocationView locationView = userLocationView.get();
|
||||
if (locationView != null) {
|
||||
Location location = LocationSource.getLocationEngine(locationView.getContext()).getLastLocation();
|
||||
locationView.setLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for receiving location updates from LocationServices.
|
||||
*
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.telemetry;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSink;
|
||||
import okio.GzipSink;
|
||||
import okio.Okio;
|
||||
|
||||
/**
|
||||
* OkHttp Interceptor for Gzipping Telemetry Data requests to the server.
|
||||
* Based on: https://github.com/square/okhttp/wiki/Interceptors
|
||||
*/
|
||||
public final class GzipRequestInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
public Response intercept(Interceptor.Chain chain) throws IOException {
|
||||
Request originalRequest = chain.request();
|
||||
if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
|
||||
Timber.d("Not compressing");
|
||||
return chain.proceed(originalRequest);
|
||||
}
|
||||
|
||||
Timber.d("Compressing");
|
||||
Request compressedRequest = originalRequest.newBuilder()
|
||||
.header("Content-Encoding", "gzip")
|
||||
.method(originalRequest.method(), gzip(originalRequest.body()))
|
||||
.build();
|
||||
return chain.proceed(compressedRequest);
|
||||
}
|
||||
|
||||
private RequestBody gzip(final RequestBody body) {
|
||||
return new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return body.contentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return -1; // We don't know the compressed length in advance!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
|
||||
body.writeTo(gzipSink);
|
||||
gzipSink.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.telemetry;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Constants for Telemetry Metadata
|
||||
*/
|
||||
public class MapboxEvent implements Serializable {
|
||||
public static final int VERSION_NUMBER = 2;
|
||||
public static final String MAPBOX_EVENTS_BASE_URL = "https://events.mapbox.com";
|
||||
public static final String SOURCE_MAPBOX = "mapbox";
|
||||
|
||||
// Event Types
|
||||
public static final String TYPE_TURNSTILE = "appUserTurnstile";
|
||||
public static final String TYPE_MAP_LOAD = "map.load";
|
||||
public static final String TYPE_MAP_CLICK = "map.click";
|
||||
public static final String TYPE_MAP_DRAGEND = "map.dragend";
|
||||
public static final String TYPE_LOCATION = "location";
|
||||
public static final String TYPE_VISIT = "visit";
|
||||
|
||||
// Event Keys
|
||||
public static final String KEY_LATITUDE = "lat";
|
||||
public static final String KEY_LONGITUDE = "lng";
|
||||
public static final String KEY_SPEED = "speed";
|
||||
public static final String KEY_COURSE = "course";
|
||||
public static final String KEY_ALTITUDE = "altitude";
|
||||
public static final String KEY_HORIZONTAL_ACCURACY = "horizontalAccuracy";
|
||||
public static final String KEY_ZOOM = "zoom";
|
||||
|
||||
public static final String KEY_PUSH_ENABLED = "enabled.push";
|
||||
public static final String KEY_EMAIL_ENABLED = "enabled.email";
|
||||
public static final String KEY_GESTURE_ID = "gesture";
|
||||
public static final String KEY_ARRIVAL_DATE = "arrivalDate";
|
||||
public static final String KEY_DEPARTURE_DATE = "departureDate";
|
||||
|
||||
public static final String GESTURE_SINGLETAP = "SingleTap";
|
||||
public static final String GESTURE_DOUBLETAP = "DoubleTap";
|
||||
public static final String GESTURE_TWO_FINGER_SINGLETAP = "TwoFingerTap";
|
||||
public static final String GESTURE_QUICK_ZOOM = "QuickZoom";
|
||||
public static final String GESTURE_PAN_START = "Pan";
|
||||
public static final String GESTURE_PINCH_START = "Pinch";
|
||||
public static final String GESTURE_ROTATION_START = "Rotation";
|
||||
public static final String GESTURE_PITCH_START = "Pitch";
|
||||
|
||||
// Event Attributes
|
||||
public static final String ATTRIBUTE_EVENT = "event";
|
||||
public static final String ATTRIBUTE_USERID = "userId";
|
||||
public static final String ATTRIBUTE_SOURCE = "source";
|
||||
public static final String ATTRIBUTE_ENABLED_TELEMETRY = "enabled.telemetry";
|
||||
public static final String ATTRIBUTE_SESSION_ID = "sessionId";
|
||||
public static final String ATTRIBUTE_VERSION = "version";
|
||||
public static final String ATTRIBUTE_CREATED = "created";
|
||||
public static final String ATTRIBUTE_VENDOR_ID = "vendorId";
|
||||
public static final String ATTRIBUTE_APP_BUNDLE_ID = "appBundleId";
|
||||
public static final String ATTRIBUTE_MODEL = "model";
|
||||
public static final String ATTRIBUTE_OPERATING_SYSTEM = "operatingSystem";
|
||||
public static final String ATTRIBUTE_ORIENTATION = "orientation";
|
||||
public static final String ATTRIBUTE_BATTERY_LEVEL = "batteryLevel";
|
||||
public static final String ATTRIBUTE_PLUGGED_IN = "pluggedIn";
|
||||
public static final String ATTRIBUTE_APPLICATION_STATE = "applicationState";
|
||||
public static final String ATTRIBUTE_RESOLUTION = "resolution";
|
||||
public static final String ATTRIBUTE_ACCESSIBILITY_FONT_SCALE = "accessibilityFontScale";
|
||||
public static final String ATTRIBUTE_CARRIER = "carrier";
|
||||
public static final String ATTRIBUTE_CELLULAR_NETWORK_TYPE = "cellularNetworkType";
|
||||
public static final String ATTRIBUTE_WIFI = "wifi";
|
||||
|
||||
/**
|
||||
* Helper method for tracking gesture events
|
||||
*
|
||||
* @param projection Projection of the Map object
|
||||
* @param gestureId Type of Gesture See {@see MapboxEvent#GESTURE_SINGLETAP
|
||||
* MapboxEvent#GESTURE_DOUBLETAP
|
||||
* MapboxEvent#GESTURE_TWO_FINGER_SINGLETAP
|
||||
* MapboxEvent#GESTURE_QUICK_ZOOM
|
||||
* MapboxEvent#GESTURE_PAN_START
|
||||
* MapboxEvent#GESTURE_PINCH_START
|
||||
* MapboxEvent#GESTURE_ROTATION_START
|
||||
* MapboxEvent#GESTURE_PITCH_START}
|
||||
* @param xCoordinate Original x screen coordinate at start of gesture
|
||||
* @param yCoordinate Original y screen cooridnate at start of gesture
|
||||
* @param zoom Zoom level to be registered
|
||||
*/
|
||||
public static void trackGestureEvent(@NonNull Projection projection, @NonNull String gestureId, float xCoordinate,
|
||||
float yCoordinate, double zoom) {
|
||||
LatLng tapLatLng = projection.fromScreenLocation(new PointF(xCoordinate, yCoordinate));
|
||||
|
||||
// NaN and Infinite checks to prevent JSON errors at send to server time
|
||||
if (Double.isNaN(tapLatLng.getLatitude()) || Double.isNaN(tapLatLng.getLongitude())) {
|
||||
Timber.d("trackGestureEvent() has a NaN lat or lon. Returning.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Double.isInfinite(tapLatLng.getLatitude()) || Double.isInfinite(tapLatLng.getLongitude())) {
|
||||
Timber.d("trackGestureEvent() has an Infinite lat or lon. Returning.");
|
||||
return;
|
||||
}
|
||||
|
||||
Hashtable<String, Object> evt = new Hashtable<>();
|
||||
evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_CLICK);
|
||||
evt.put(MapboxEvent.ATTRIBUTE_CREATED, MapboxEventManager.generateCreateDate());
|
||||
evt.put(MapboxEvent.KEY_GESTURE_ID, gestureId);
|
||||
evt.put(MapboxEvent.KEY_LATITUDE, tapLatLng.getLatitude());
|
||||
evt.put(MapboxEvent.KEY_LONGITUDE, tapLatLng.getLongitude());
|
||||
evt.put(MapboxEvent.KEY_ZOOM, zoom);
|
||||
|
||||
MapboxEventManager.getMapboxEventManager().pushEvent(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for tracking DragEnd gesture event
|
||||
* See {@see MapboxEvent#TYPE_MAP_DRAGEND}
|
||||
*
|
||||
* @param projection projection of the Map object.
|
||||
* @param xCoordinate Original x screen coordinate at end of drag
|
||||
* @param yCoordinate Orginal y screen coordinate at end of drag
|
||||
* @param zoom Zoom level to be registered
|
||||
*/
|
||||
public static void trackGestureDragEndEvent(@NonNull Projection projection, float xCoordinate, float yCoordinate,
|
||||
double zoom) {
|
||||
LatLng tapLatLng = projection.fromScreenLocation(new PointF(xCoordinate, yCoordinate));
|
||||
|
||||
// NaN and Infinite checks to prevent JSON errors at send to server time
|
||||
if (Double.isNaN(tapLatLng.getLatitude()) || Double.isNaN(tapLatLng.getLongitude())) {
|
||||
Timber.d("trackGestureDragEndEvent() has a NaN lat or lon. Returning.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Double.isInfinite(tapLatLng.getLatitude()) || Double.isInfinite(tapLatLng.getLongitude())) {
|
||||
Timber.d("trackGestureDragEndEvent() has an Infinite lat or lon. Returning.");
|
||||
return;
|
||||
}
|
||||
|
||||
Hashtable<String, Object> evt = new Hashtable<>();
|
||||
evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_DRAGEND);
|
||||
evt.put(MapboxEvent.ATTRIBUTE_CREATED, MapboxEventManager.generateCreateDate());
|
||||
evt.put(MapboxEvent.KEY_LATITUDE, tapLatLng.getLatitude());
|
||||
evt.put(MapboxEvent.KEY_LONGITUDE, tapLatLng.getLongitude());
|
||||
evt.put(MapboxEvent.KEY_ZOOM, zoom);
|
||||
|
||||
MapboxEventManager.getMapboxEventManager().pushEvent(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for tracking map load event
|
||||
*/
|
||||
public static void trackMapLoadEvent() {
|
||||
Hashtable<String, Object> evt = new Hashtable<>();
|
||||
evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_LOAD);
|
||||
evt.put(MapboxEvent.ATTRIBUTE_CREATED, MapboxEventManager.generateCreateDate());
|
||||
MapboxEventManager.getMapboxEventManager().pushEvent(evt);
|
||||
}
|
||||
}
|
||||
@@ -1,828 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.telemetry;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.location.Location;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.mapbox.mapboxsdk.BuildConfig;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.constants.GeoConstants;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.exceptions.TelemetryServiceNotConfiguredException;
|
||||
import com.mapbox.mapboxsdk.location.LocationServices;
|
||||
import com.mapbox.mapboxsdk.utils.MathUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.Vector;
|
||||
|
||||
import okhttp3.CertificatePinner;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.internal.Util;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Singleton control center for managing Telemetry Data.
|
||||
* Primary access is via MapboxEventManager.getMapboxEventManager()
|
||||
*/
|
||||
public class MapboxEventManager {
|
||||
|
||||
private static MapboxEventManager mapboxEventManager = null;
|
||||
|
||||
private boolean initialized = false;
|
||||
private boolean stagingEnv;
|
||||
private boolean telemetryEnabled;
|
||||
|
||||
private final Vector<Hashtable<String, Object>> events = new Vector<>();
|
||||
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ",
|
||||
MapboxConstants.MAPBOX_LOCALE);
|
||||
|
||||
private Context context = null;
|
||||
private String accessToken = null;
|
||||
private String eventsURL = MapboxEvent.MAPBOX_EVENTS_BASE_URL;
|
||||
|
||||
private String userAgent = BuildConfig.MAPBOX_EVENTS_USER_AGENT_BASE;
|
||||
|
||||
private Intent batteryStatus = null;
|
||||
private final String operatingSystem = "Android - " + Build.VERSION.RELEASE;
|
||||
|
||||
private DisplayMetrics displayMetrics = null;
|
||||
|
||||
private String mapboxVendorId = null;
|
||||
|
||||
private String mapboxSessionId = null;
|
||||
private long mapboxSessionIdLastSet = 0;
|
||||
private static long hourInMillis = 1000 * 60 * 60;
|
||||
private static long flushDelayInitialInMillis = 1000 * 10; // 10 Seconds
|
||||
private static long flushDelayInMillis = 1000 * 60 * 3; // 3 Minutes
|
||||
private static final int SESSION_ID_ROTATION_HOURS = 24;
|
||||
|
||||
private static final int FLUSH_EVENTS_CAP = 1000;
|
||||
|
||||
private static MessageDigest messageDigest = null;
|
||||
|
||||
private static final double locationEventAccuracy = 10000000;
|
||||
|
||||
private Timer timer = null;
|
||||
|
||||
/**
|
||||
* Private Constructor for configuring the single instance per app.
|
||||
*/
|
||||
private MapboxEventManager() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal setup of MapboxEventsManager. It needs to be called once before @link
|
||||
* MapboxEventManager#getMapboxEventManager
|
||||
* <p>
|
||||
* This allows for a cleaner getMapboxEventManager() that doesn't require context and accessToken
|
||||
*
|
||||
* @param context The context associated with MapView
|
||||
* @param accessToken The accessToken to load MapView
|
||||
*/
|
||||
public void initialize(@NonNull Context context, @NonNull String accessToken) {
|
||||
|
||||
// Timber.i("Telemetry initialize() called...");
|
||||
|
||||
if (initialized) {
|
||||
// Timber.i("Mapbox Telemetry has already been initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.context = context.getApplicationContext();
|
||||
this.accessToken = accessToken;
|
||||
|
||||
validateTelemetryServiceConfigured();
|
||||
|
||||
// Setup Message Digest
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance("SHA-1");
|
||||
} catch (NoSuchAlgorithmException exception) {
|
||||
Timber.w("Error getting Encryption Algorithm: ", exception);
|
||||
}
|
||||
|
||||
// Create Initial Session Id
|
||||
rotateSessionId();
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES_FILE,
|
||||
Context.MODE_PRIVATE);
|
||||
|
||||
// Determine if Telemetry Should Be Enabled
|
||||
/// Timber.i("Right before Telemetry set enabled in initialized()");
|
||||
setTelemetryEnabled(prefs.getBoolean(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_ENABLED, true));
|
||||
|
||||
// Load / Create Vendor Id
|
||||
if (prefs.contains(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_VENDORID)) {
|
||||
mapboxVendorId = prefs.getString(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_VENDORID, "");
|
||||
}
|
||||
if (TextUtils.isEmpty(mapboxVendorId)) {
|
||||
String vendorId = UUID.randomUUID().toString();
|
||||
mapboxVendorId = encodeString(vendorId);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putString(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_VENDORID, mapboxVendorId);
|
||||
editor.apply();
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
// Get DisplayMetrics Setup
|
||||
displayMetrics = new DisplayMetrics();
|
||||
((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(displayMetrics);
|
||||
|
||||
// Check for Staging Server Information
|
||||
try {
|
||||
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(),
|
||||
PackageManager.GET_META_DATA);
|
||||
String stagingURL = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_STAGING_SERVER);
|
||||
String stagingAccessToken = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_STAGING_ACCESS_TOKEN);
|
||||
|
||||
if (TextUtils.isEmpty(stagingURL) || TextUtils.isEmpty(stagingAccessToken)) {
|
||||
// Timber.d("Looking in SharedPreferences for Staging Credentials");
|
||||
stagingURL = prefs.getString(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_URL, null);
|
||||
stagingAccessToken = prefs.getString(
|
||||
MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_ACCESS_TOKEN, null);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(stagingURL) && !TextUtils.isEmpty(stagingAccessToken)) {
|
||||
eventsURL = stagingURL;
|
||||
this.accessToken = accessToken;
|
||||
stagingEnv = true;
|
||||
}
|
||||
|
||||
// Build User Agent
|
||||
String appIdentifier = getApplicationIdentifier();
|
||||
if (TextUtils.equals(userAgent, BuildConfig.MAPBOX_EVENTS_USER_AGENT_BASE) && !TextUtils.isEmpty(appIdentifier)) {
|
||||
userAgent = Util
|
||||
.toHumanReadableAscii(String.format(MapboxConstants.MAPBOX_LOCALE, "%s %s", appIdentifier, userAgent));
|
||||
}
|
||||
|
||||
} catch (Exception exception) {
|
||||
// Timber.e("Error Trying to load Staging Credentials: ", exception);
|
||||
}
|
||||
|
||||
// Register for battery updates
|
||||
IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
batteryStatus = context.registerReceiver(null, iFilter);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary Access method using Singleton pattern
|
||||
*
|
||||
* @return MapboxEventManager
|
||||
*/
|
||||
public static MapboxEventManager getMapboxEventManager() {
|
||||
if (mapboxEventManager == null) {
|
||||
mapboxEventManager = new MapboxEventManager();
|
||||
}
|
||||
return mapboxEventManager;
|
||||
}
|
||||
|
||||
// Checks that TelemetryService has been configured by developer
|
||||
private void validateTelemetryServiceConfigured() {
|
||||
try {
|
||||
// Check Implementing app's AndroidManifest.xml
|
||||
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
|
||||
PackageManager.GET_SERVICES);
|
||||
if (packageInfo.services != null) {
|
||||
for (ServiceInfo service : packageInfo.services) {
|
||||
if (TextUtils.equals("com.mapbox.mapboxsdk.telemetry.TelemetryService", service.name)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
// Timber.w("Error checking for Telemetry Service Config: ", exception);
|
||||
}
|
||||
throw new TelemetryServiceNotConfiguredException();
|
||||
}
|
||||
|
||||
public static String generateCreateDate() {
|
||||
return dateFormat.format(new Date());
|
||||
}
|
||||
|
||||
public boolean isTelemetryEnabled() {
|
||||
return telemetryEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables / Disables Telemetry
|
||||
*
|
||||
* @param telemetryEnabled True to start telemetry, false to stop it
|
||||
*/
|
||||
public void setTelemetryEnabled(boolean telemetryEnabled) {
|
||||
// Timber.i("this.telemetryEnabled = " + this.telemetryEnabled + "; telemetryEnabled = " + telemetryEnabled);
|
||||
if (this.telemetryEnabled == telemetryEnabled) {
|
||||
// Timber.d("No need to start / stop telemetry as it's already in that state.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (telemetryEnabled) {
|
||||
// Timber.d("Starting Telemetry Up!");
|
||||
// Start It Up
|
||||
context.startService(new Intent(context, TelemetryService.class));
|
||||
|
||||
// Make sure Ambient Mode is started at a minimum
|
||||
if (LocationServices.getLocationServices(context).areLocationPermissionsGranted()) {
|
||||
// Timber.i("Permissions are good, see if GPS is enabled and if not then setup Ambient.");
|
||||
if (LocationServices.getLocationServices(context).isGpsEnabled()) {
|
||||
LocationServices.getLocationServices(context).toggleGPS(false);
|
||||
}
|
||||
} else {
|
||||
// Start timer that checks for Permissions
|
||||
// Timber.i("Permissions are not good. Need to do some looping to check on stuff.");
|
||||
|
||||
final Handler permsHandler = new Handler();
|
||||
Runnable runnable = new Runnable() {
|
||||
|
||||
private final ExponentialBackoffCounter exponentialBackoffCounter = new ExponentialBackoffCounter();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (LocationServices.getLocationServices(context).areLocationPermissionsGranted()) {
|
||||
// Timber.i("Permissions finally granted, so starting Ambient if GPS isn't already enabled");
|
||||
// Start Ambient
|
||||
if (LocationServices.getLocationServices(context).isGpsEnabled()) {
|
||||
LocationServices.getLocationServices(context).toggleGPS(false);
|
||||
}
|
||||
} else {
|
||||
// Restart Handler
|
||||
long nextWaitTime = exponentialBackoffCounter.getNextCount();
|
||||
// Timber.i("Permissions not granted yet... let's try again in " + nextWaitTime/1000 + " seconds");
|
||||
permsHandler.postDelayed(this, nextWaitTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
permsHandler.postDelayed(runnable, 1000 * 10);
|
||||
}
|
||||
|
||||
// Manage Timer Flush
|
||||
timer = new Timer();
|
||||
timer.schedule(new FlushEventsTimerTask(), flushDelayInitialInMillis, flushDelayInMillis);
|
||||
} else {
|
||||
// Timber.d("Shutting Telemetry Down");
|
||||
// Shut It Down
|
||||
events.removeAllElements();
|
||||
context.stopService(new Intent(context, TelemetryService.class));
|
||||
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Persist
|
||||
this.telemetryEnabled = telemetryEnabled;
|
||||
SharedPreferences prefs = context.getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES_FILE,
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_ENABLED, telemetryEnabled);
|
||||
editor.apply();
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately attempt to send all events data in the queue to the server.
|
||||
* <p>
|
||||
* NOTE: Permission set to package private to enable only telemetry code to use this.
|
||||
*/
|
||||
void flushEventsQueueImmediately() {
|
||||
// Timber.i("flushEventsQueueImmediately() called...");
|
||||
new FlushTheEventsTask().execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Centralized method for adding populated event to the queue allowing for cap size checking
|
||||
*
|
||||
* @param event Event to add to the Events Queue
|
||||
*/
|
||||
private void putEventOnQueue(@NonNull Hashtable<String, Object> event) {
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
events.add(event);
|
||||
if (events.size() == FLUSH_EVENTS_CAP) {
|
||||
// Timber.d("eventsSize == flushCap so send data.");
|
||||
flushEventsQueueImmediately();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Location Event to the system for processing
|
||||
*
|
||||
* @param location Location event
|
||||
*/
|
||||
public void addLocationEvent(Location location) {
|
||||
|
||||
// NaN and Infinite checks to prevent JSON errors at send to server time
|
||||
if (Double.isNaN(location.getLatitude()) || Double.isNaN(location.getLongitude())
|
||||
|| Double.isNaN(location.getAltitude()) || Float.isNaN(location.getAccuracy())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Double.isInfinite(location.getLatitude()) || Double.isInfinite(location.getLongitude())
|
||||
|| Double.isInfinite(location.getAltitude()) || Float.isInfinite(location.getAccuracy())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add Location even to queue
|
||||
Hashtable<String, Object> event = new Hashtable<>();
|
||||
event.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_LOCATION);
|
||||
event.put(MapboxEvent.ATTRIBUTE_CREATED, generateCreateDate());
|
||||
event.put(MapboxEvent.ATTRIBUTE_SOURCE, MapboxEvent.SOURCE_MAPBOX);
|
||||
event.put(MapboxEvent.ATTRIBUTE_SESSION_ID, encodeString(mapboxSessionId));
|
||||
event.put(MapboxEvent.KEY_LATITUDE,
|
||||
Math.floor(location.getLatitude() * locationEventAccuracy) / locationEventAccuracy);
|
||||
event.put(MapboxEvent.KEY_LONGITUDE,
|
||||
Math.floor(location.getLongitude() * locationEventAccuracy) / locationEventAccuracy);
|
||||
event.put(MapboxEvent.KEY_ALTITUDE, location.getAltitude());
|
||||
event.put(MapboxEvent.KEY_HORIZONTAL_ACCURACY, Math.round(location.getAccuracy()));
|
||||
event.put(MapboxEvent.ATTRIBUTE_OPERATING_SYSTEM, operatingSystem);
|
||||
event.put(MapboxEvent.ATTRIBUTE_APPLICATION_STATE, getApplicationState());
|
||||
|
||||
putEventOnQueue(event);
|
||||
|
||||
rotateSessionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push Interactive Events to the system for processing
|
||||
*
|
||||
* @param eventWithAttributes Event with attributes
|
||||
*/
|
||||
public void pushEvent(Hashtable<String, Object> eventWithAttributes) {
|
||||
if (context == null || accessToken == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventWithAttributes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String eventType = (String) eventWithAttributes.get(MapboxEvent.ATTRIBUTE_EVENT);
|
||||
if (TextUtils.isEmpty(eventType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType.equalsIgnoreCase(MapboxEvent.TYPE_MAP_LOAD)) {
|
||||
// Map Load Data Model
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_USERID, mapboxVendorId);
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_MODEL, Build.MODEL);
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_OPERATING_SYSTEM, operatingSystem);
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_RESOLUTION, displayMetrics.density);
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_ACCESSIBILITY_FONT_SCALE, getAccesibilityFontScaleSize());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_ORIENTATION, getOrientation());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_BATTERY_LEVEL, getBatteryLevel());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_PLUGGED_IN, isPluggedIn());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_CARRIER, getCellularCarrier());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE, getCellularNetworkType());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_WIFI, getConnectedToWifi());
|
||||
|
||||
// Put Map Load on events before Turnstile clears it
|
||||
putEventOnQueue(eventWithAttributes);
|
||||
|
||||
// Turnstile
|
||||
pushTurnstileEvent();
|
||||
|
||||
// Return immediately to avoid double adding of event
|
||||
return;
|
||||
|
||||
} else if (eventType.equalsIgnoreCase(MapboxEvent.TYPE_MAP_CLICK)) {
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_ORIENTATION, getOrientation());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_BATTERY_LEVEL, getBatteryLevel());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_PLUGGED_IN, isPluggedIn());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_CARRIER, getCellularCarrier());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE, getCellularNetworkType());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_WIFI, getConnectedToWifi());
|
||||
} else if (eventType.equalsIgnoreCase(MapboxEvent.TYPE_MAP_DRAGEND)) {
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_ORIENTATION, getOrientation());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_BATTERY_LEVEL, getBatteryLevel());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_PLUGGED_IN, isPluggedIn());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_CARRIER, getCellularCarrier());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE, getCellularNetworkType());
|
||||
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_WIFI, getConnectedToWifi());
|
||||
} else {
|
||||
// Timber.w("This is not an event type in the Events Data Model.");
|
||||
return;
|
||||
}
|
||||
|
||||
putEventOnQueue(eventWithAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes turnstile event for internal billing purposes
|
||||
*/
|
||||
private void pushTurnstileEvent() {
|
||||
|
||||
Hashtable<String, Object> event = new Hashtable<>();
|
||||
event.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_TURNSTILE);
|
||||
event.put(MapboxEvent.ATTRIBUTE_CREATED, generateCreateDate());
|
||||
event.put(MapboxEvent.ATTRIBUTE_USERID, mapboxVendorId);
|
||||
event.put(MapboxEvent.ATTRIBUTE_ENABLED_TELEMETRY, telemetryEnabled);
|
||||
|
||||
events.add(event);
|
||||
|
||||
// Send to Server Immediately
|
||||
flushEventsQueueImmediately();
|
||||
// Timber.d("turnstile event pushed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA-1 Encoding for strings
|
||||
*
|
||||
* @param string String to encode
|
||||
* @return String encoded if no error, original string if error
|
||||
*/
|
||||
private String encodeString(String string) {
|
||||
try {
|
||||
if (messageDigest != null) {
|
||||
messageDigest.reset();
|
||||
messageDigest.update(string.getBytes("UTF-8"));
|
||||
byte[] bytes = messageDigest.digest();
|
||||
|
||||
// Get the Hex version of the digest
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02X", b));
|
||||
}
|
||||
String hex = sb.toString();
|
||||
|
||||
return hex;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
// Timber.w("Error encoding string, will return in original form.", exception);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes Session Id based on time boundary
|
||||
*/
|
||||
private void rotateSessionId() {
|
||||
long now = System.currentTimeMillis();
|
||||
if ((TextUtils.isEmpty(mapboxSessionId))
|
||||
|| (now - mapboxSessionIdLastSet > (SESSION_ID_ROTATION_HOURS * hourInMillis))) {
|
||||
mapboxSessionId = UUID.randomUUID().toString();
|
||||
mapboxSessionIdLastSet = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
private String getOrientation() {
|
||||
switch (context.getResources().getConfiguration().orientation) {
|
||||
case Configuration.ORIENTATION_LANDSCAPE:
|
||||
return "Landscape";
|
||||
case Configuration.ORIENTATION_PORTRAIT:
|
||||
return "Portrait";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private int getBatteryLevel() {
|
||||
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
|
||||
|
||||
return Math.round((level / (float) scale) * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if device is plugged in to power via USB or AC or not.
|
||||
* <p>
|
||||
* http://developer.android.com/reference/android/os/BatteryManager.html#EXTRA_PLUGGED
|
||||
*
|
||||
* @return true if plugged in, false if not
|
||||
*/
|
||||
private boolean isPluggedIn() {
|
||||
|
||||
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
|
||||
if (chargePlug == BatteryManager.BATTERY_PLUGGED_USB || chargePlug == BatteryManager.BATTERY_PLUGGED_AC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getApplicationState() {
|
||||
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
|
||||
if (appProcesses == null) {
|
||||
return "";
|
||||
}
|
||||
final String packageName = context.getPackageName();
|
||||
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
|
||||
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||
&& appProcess.processName.equals(packageName)) {
|
||||
return "Foreground";
|
||||
}
|
||||
}
|
||||
return "Background";
|
||||
}
|
||||
|
||||
private float getAccesibilityFontScaleSize() {
|
||||
// Values
|
||||
// Small = 0.85
|
||||
// Normal = 1.0
|
||||
// Large = 1.15
|
||||
// Huge = 1.3
|
||||
|
||||
return context.getResources().getConfiguration().fontScale;
|
||||
}
|
||||
|
||||
private String getCellularCarrier() {
|
||||
TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
String carrierName = manager.getNetworkOperatorName();
|
||||
if (TextUtils.isEmpty(carrierName)) {
|
||||
carrierName = "";
|
||||
}
|
||||
return carrierName;
|
||||
}
|
||||
|
||||
private String getCellularNetworkType() {
|
||||
TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
switch (manager.getNetworkType()) {
|
||||
case TelephonyManager.NETWORK_TYPE_1xRTT:
|
||||
return "1xRTT";
|
||||
case TelephonyManager.NETWORK_TYPE_CDMA:
|
||||
return "CDMA";
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
return "EDGE";
|
||||
case TelephonyManager.NETWORK_TYPE_EHRPD:
|
||||
return "EHRPD";
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_0:
|
||||
return "EVDO_0";
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_A:
|
||||
return "EVDO_A";
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_B:
|
||||
return "EVDO_B";
|
||||
case TelephonyManager.NETWORK_TYPE_GPRS:
|
||||
return "GPRS";
|
||||
case TelephonyManager.NETWORK_TYPE_HSDPA:
|
||||
return "HSDPA";
|
||||
case TelephonyManager.NETWORK_TYPE_HSPA:
|
||||
return "HSPA";
|
||||
case TelephonyManager.NETWORK_TYPE_HSPAP:
|
||||
return "HSPAP";
|
||||
case TelephonyManager.NETWORK_TYPE_HSUPA:
|
||||
return "HSUPA";
|
||||
case TelephonyManager.NETWORK_TYPE_IDEN:
|
||||
return "IDEN";
|
||||
case TelephonyManager.NETWORK_TYPE_LTE:
|
||||
return "LTE";
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS:
|
||||
return "UMTS";
|
||||
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
|
||||
return "Unknown";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean getConnectedToWifi() {
|
||||
|
||||
Boolean status = false;
|
||||
WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
if (wifiMgr.isWifiEnabled()) {
|
||||
try {
|
||||
WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
|
||||
if (wifiInfo.getNetworkId() != -1) {
|
||||
status = true;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
// Timber.w("Error getting Wifi Connection Status: ", exception);
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Task responsible for converting stored events and sending them to the server
|
||||
*/
|
||||
private class FlushTheEventsTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
|
||||
if (events.isEmpty()) {
|
||||
// Timber.d("No events in the queue to send so returning.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check for NetworkConnectivity
|
||||
if (!Mapbox.isConnected()) {
|
||||
// Timber.w("Not connected to network, so empty events cache and return without attempting to send events");
|
||||
// Make sure that events don't pile up when Offline
|
||||
// and thus impact available memory over time.
|
||||
events.removeAllElements();
|
||||
return null;
|
||||
}
|
||||
|
||||
Response response = null;
|
||||
|
||||
try {
|
||||
// Send data
|
||||
// =========
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
|
||||
Vector<Hashtable<String, Object>> eventsClone = (Vector<Hashtable<String, Object>>) events.clone();
|
||||
|
||||
for (Hashtable<String, Object> evt : eventsClone) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
|
||||
// Build the JSON but only if there's a value for it in the evt
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_EVENT, evt.get(MapboxEvent.ATTRIBUTE_EVENT));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_CREATED, evt.get(MapboxEvent.ATTRIBUTE_CREATED));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_USERID, evt.get(MapboxEvent.ATTRIBUTE_USERID));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_ENABLED_TELEMETRY, evt.get(MapboxEvent.ATTRIBUTE_ENABLED_TELEMETRY));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_SOURCE, evt.get(MapboxEvent.ATTRIBUTE_SOURCE));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_SESSION_ID, evt.get(MapboxEvent.ATTRIBUTE_SESSION_ID));
|
||||
jsonObject.putOpt(MapboxEvent.KEY_LATITUDE, evt.get(MapboxEvent.KEY_LATITUDE));
|
||||
|
||||
// Make sure Longitude Is Wrapped
|
||||
if (evt.containsKey(MapboxEvent.KEY_LONGITUDE)) {
|
||||
double lon = (double) evt.get(MapboxEvent.KEY_LONGITUDE);
|
||||
if ((lon < GeoConstants.MIN_LONGITUDE) || (lon > GeoConstants.MAX_LONGITUDE)) {
|
||||
lon = MathUtils.wrap(lon, GeoConstants.MIN_LONGITUDE, GeoConstants.MAX_LONGITUDE);
|
||||
}
|
||||
jsonObject.put(MapboxEvent.KEY_LONGITUDE, lon);
|
||||
}
|
||||
|
||||
jsonObject.putOpt(MapboxEvent.KEY_ALTITUDE, evt.get(MapboxEvent.KEY_ALTITUDE));
|
||||
jsonObject.putOpt(MapboxEvent.KEY_ZOOM, evt.get(MapboxEvent.KEY_ZOOM));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_OPERATING_SYSTEM, evt.get(MapboxEvent.ATTRIBUTE_OPERATING_SYSTEM));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_USERID, evt.get(MapboxEvent.ATTRIBUTE_USERID));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_MODEL, evt.get(MapboxEvent.ATTRIBUTE_MODEL));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_RESOLUTION, evt.get(MapboxEvent.ATTRIBUTE_RESOLUTION));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_ACCESSIBILITY_FONT_SCALE,
|
||||
evt.get(MapboxEvent.ATTRIBUTE_ACCESSIBILITY_FONT_SCALE));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_BATTERY_LEVEL, evt.get(MapboxEvent.ATTRIBUTE_BATTERY_LEVEL));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_PLUGGED_IN, evt.get(MapboxEvent.ATTRIBUTE_PLUGGED_IN));
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_WIFI, evt.get(MapboxEvent.ATTRIBUTE_WIFI));
|
||||
|
||||
// Special Cases where empty string is denoting null and therefore should not be sent at all
|
||||
// This arises as thread safe Hashtable does not accept null values (nor keys)
|
||||
if (evt.containsKey(MapboxEvent.ATTRIBUTE_ORIENTATION)) {
|
||||
String orientation = (String) evt.get(MapboxEvent.ATTRIBUTE_ORIENTATION);
|
||||
if (!TextUtils.isEmpty(orientation)) {
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_ORIENTATION, orientation);
|
||||
}
|
||||
}
|
||||
if (evt.containsKey(MapboxEvent.ATTRIBUTE_CARRIER)) {
|
||||
String carrier = (String) evt.get(MapboxEvent.ATTRIBUTE_CARRIER);
|
||||
if (!TextUtils.isEmpty(carrier)) {
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_CARRIER, carrier);
|
||||
}
|
||||
}
|
||||
if (evt.containsKey(MapboxEvent.ATTRIBUTE_APPLICATION_STATE)) {
|
||||
String appState = (String) evt.get(MapboxEvent.ATTRIBUTE_APPLICATION_STATE);
|
||||
if (!TextUtils.isEmpty(appState)) {
|
||||
jsonObject.putOpt(MapboxEvent.ATTRIBUTE_APPLICATION_STATE,
|
||||
evt.get(MapboxEvent.ATTRIBUTE_APPLICATION_STATE));
|
||||
}
|
||||
}
|
||||
|
||||
// Special Cases where null has to be passed if no value exists
|
||||
// Requires using put() instead of putOpt()
|
||||
String eventType = (String) evt.get(MapboxEvent.ATTRIBUTE_EVENT);
|
||||
if (!TextUtils.isEmpty(eventType) && eventType.equalsIgnoreCase(MapboxEvent.TYPE_MAP_CLICK)) {
|
||||
jsonObject.put(MapboxEvent.KEY_GESTURE_ID, evt.get(MapboxEvent.KEY_GESTURE_ID));
|
||||
}
|
||||
if (evt.containsKey(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE)) {
|
||||
String cellularNetworkType = (String) evt.get(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE);
|
||||
if (TextUtils.isEmpty(cellularNetworkType)) {
|
||||
jsonObject.put(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE, null);
|
||||
} else {
|
||||
jsonObject.put(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE,
|
||||
evt.get(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE));
|
||||
}
|
||||
}
|
||||
|
||||
jsonArray.put(jsonObject);
|
||||
}
|
||||
|
||||
// Based on http://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html
|
||||
CertificatePinner.Builder certificatePinnerBuilder = new CertificatePinner.Builder();
|
||||
if (stagingEnv) {
|
||||
// Staging - Geotrust
|
||||
certificatePinnerBuilder
|
||||
.add("cloudfront-staging.tilestream.net", "sha256/3euxrJOrEZI15R4104UsiAkDqe007EPyZ6eTL/XxdAY=")
|
||||
.add("cloudfront-staging.tilestream.net", "sha256/5kJvNEMw0KjrCAu7eXY5HZdvyCS13BbA0VJG1RSP91w=")
|
||||
.add("cloudfront-staging.tilestream.net", "sha256/r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=");
|
||||
} else {
|
||||
certificatePinnerBuilder
|
||||
// Prod - Geotrust
|
||||
.add("events.mapbox.com", "sha256/BhynraKizavqoC5U26qgYuxLZst6pCu9J5stfL6RSYY=")
|
||||
.add("events.mapbox.com", "sha256/owrR9U9FWDWtrFF+myoRIu75JwU4sJwzvhCNLZoY37g=")
|
||||
.add("events.mapbox.com", "sha256/SQVGZiOrQXi+kqxcvWWE96HhfydlLVqFr4lQTqI5qqo=")
|
||||
// Prod - DigiCert
|
||||
.add("events.mapbox.com", "sha256/Tb0uHZ/KQjWh8N9+CZFLc4zx36LONQ55l6laDi1qtT4=")
|
||||
.add("events.mapbox.com", "sha256/RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho=")
|
||||
.add("events.mapbox.com", "sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=");
|
||||
}
|
||||
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.certificatePinner(certificatePinnerBuilder.build())
|
||||
.addInterceptor(new GzipRequestInterceptor())
|
||||
.build();
|
||||
RequestBody body = RequestBody.create(JSON, jsonArray.toString());
|
||||
|
||||
String url = eventsURL + "/events/v2?access_token=" + accessToken;
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.header("User-Agent", userAgent)
|
||||
.post(body)
|
||||
.build();
|
||||
response = client.newCall(request).execute();
|
||||
// Timber.d("response code = " + response.code() + " for events " + events.size());
|
||||
|
||||
} catch (Exception exception) {
|
||||
Timber.e("FlushTheEventsTask borked: ", exception);
|
||||
} finally {
|
||||
if (response != null && response.body() != null) {
|
||||
response.body().close();
|
||||
}
|
||||
// Reset Events
|
||||
// ============
|
||||
events.removeAllElements();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TimerTask responsible for sending event data to server
|
||||
*/
|
||||
private class FlushEventsTimerTask extends TimerTask {
|
||||
/**
|
||||
* The task to run should be specified in the implementation of the {@code run()}
|
||||
* method.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
new FlushTheEventsTask().execute();
|
||||
}
|
||||
}
|
||||
|
||||
private String getApplicationIdentifier() {
|
||||
try {
|
||||
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
return String.format(MapboxConstants.MAPBOX_LOCALE, "%s/%s/%s", context.getPackageName(),
|
||||
packageInfo.versionName, packageInfo.versionCode);
|
||||
} catch (Exception exception) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static class ExponentialBackoffCounter {
|
||||
|
||||
private static final long BASE_TIME = 30000 /*30 seconds*/;
|
||||
private int attempt;
|
||||
|
||||
long getNextCount() {
|
||||
attempt++;
|
||||
return attempt * BASE_TIME;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Listener for Location updates generated by implementing app.
|
||||
*/
|
||||
public class TelemetryLocationReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final String INTENT_STRING = "com.mapbox.mapboxsdk.telemetry.TelemetryLocationReceiver";
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* android.content.IntentFilter, String, android.os.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().
|
||||
* <p>
|
||||
* <p><b>If this BroadcastReceiver was launched through a <receiver> tag,
|
||||
* then the object is no longer alive after returning from this
|
||||
* function.</b> 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, android.content.ServiceConnection, int)}. If you wish
|
||||
* to interact with a service that is already running, you can use
|
||||
* {@link #peekService}.
|
||||
* <p>
|
||||
* <p>The Intent filters used in {@link Context#registerReceiver}
|
||||
* and in application manifests are <em>not</em> 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) {
|
||||
if (intent == null || intent.getExtras() == null) {
|
||||
// see https://github.com/mapbox/mapbox-gl-native/issues/6934
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = (Location) intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED);
|
||||
if (location != null) {
|
||||
MapboxEventManager.getMapboxEventManager().addLocationEvent(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.telemetry;
|
||||
|
||||
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.support.annotation.Nullable;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Manages Startup and Shutdown of Telemetry resources
|
||||
*/
|
||||
public class TelemetryService extends Service {
|
||||
|
||||
private TelemetryLocationReceiver telemetryLocationReceiver = null;
|
||||
|
||||
/**
|
||||
* 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 <a href="{@docRoot}guide/components/aidl.html">described using
|
||||
* aidl</a>.
|
||||
* <p>
|
||||
* <p><em>Note that unlike other application components, calls on to the
|
||||
* IBinder interface returned here may not happen on the main thread
|
||||
* of the process</em>. More information about the main thread can be found in
|
||||
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
|
||||
* Threads</a>.</p>
|
||||
*
|
||||
* @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 <em>not</em> 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();
|
||||
|
||||
Timber.i("onCreate() called");
|
||||
|
||||
// Enable Location Listening for lifecycle of app
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(
|
||||
new TelemetryLocationReceiver(),
|
||||
new IntentFilter(TelemetryLocationReceiver.INTENT_STRING)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* <p>For backwards compatibility, the default implementation calls
|
||||
* {@link #onStart} and returns either {@link #START_STICKY}
|
||||
* or {@link #START_STICKY_COMPATIBILITY}.
|
||||
* </p>
|
||||
* <p>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 <code>handleCommand</code> method is implemented by
|
||||
* you as appropriate:
|
||||
* </p>
|
||||
* <p>
|
||||
* <p class="caution">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}.</p>
|
||||
*
|
||||
* @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) {
|
||||
|
||||
Timber.i("onStartCommand() called");
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
private void shutdownTelemetry() {
|
||||
|
||||
// Send Any Remaining events to the server
|
||||
MapboxEventManager.getMapboxEventManager().flushEventsQueueImmediately();
|
||||
|
||||
// Undesired, but needed trick to keep app alive long enough for data to get to server
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (Exception exception) {
|
||||
Timber.e("Error while trying to sleep for 1 second: " + exception);
|
||||
}
|
||||
|
||||
try {
|
||||
unregisterReceiver(telemetryLocationReceiver);
|
||||
} catch (IllegalArgumentException illegalArgumentException) {
|
||||
Timber.e("Error when unregisterReceiver: " + illegalArgumentException);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains the Mapbox Maps Android Telemetry API classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.telemetry;
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.utils;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
/**
|
||||
* Test a value in specified range, returning minimum if it's below, and maximum if it's above
|
||||
*
|
||||
* @param value Value to test
|
||||
* @param min Minimum value of range
|
||||
* @param max Maximum value of range
|
||||
* @return value if it's between min and max, min if it's below, max if it's above
|
||||
*/
|
||||
public static double clamp(double value, double min, double max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a value in specified range, returning minimum if it's below, and maximum if it's above
|
||||
*
|
||||
* @param value Value to test
|
||||
* @param min Minimum value of range
|
||||
* @param max Maximum value of range
|
||||
* @return value if it's between min and max, min if it's below, max if it's above
|
||||
*/
|
||||
public static float clamp(float value, float min, float max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrains value to the given range (including min, excluding max) via modular arithmetic.
|
||||
* <p>
|
||||
* Same formula as used in Core GL (wrap.hpp)
|
||||
* std::fmod((std::fmod((value - min), d) + d), d) + min;
|
||||
*
|
||||
* @param value Value to wrap
|
||||
* @param min Minimum value
|
||||
* @param max Maximum value
|
||||
* @return Wrapped value
|
||||
*/
|
||||
public static double wrap(double value, double min, double max) {
|
||||
double delta = max - min;
|
||||
|
||||
double firstMod = (value - min) % delta;
|
||||
double secondMod = (firstMod + delta) % delta;
|
||||
|
||||
return secondMod + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bearing from core to match Android SDK value.
|
||||
*
|
||||
* @param nativeBearing bearing value coming from core
|
||||
* @return bearing in degrees starting from 0 rotating clockwise
|
||||
*/
|
||||
public static double convertNativeBearing(double nativeBearing) {
|
||||
double direction = -nativeBearing;
|
||||
|
||||
while (direction > 360) {
|
||||
direction -= 360;
|
||||
}
|
||||
while (direction < 0) {
|
||||
direction += 360;
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user