[android] Extract telemetry into MAS

* [android] add the mapbox-android-telemetry module dependency.

* update javadoc
This commit is contained in:
Antonio Zugaldia
2017-02-03 15:12:13 -05:00
committed by GitHub
parent e5c9db4793
commit c97ec3be03
29 changed files with 283 additions and 1736 deletions

View File

@@ -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;

View File

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

View File

@@ -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;

View File

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

View File

@@ -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/.");
}
}

View File

@@ -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

View File

@@ -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)

View File

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

View File

@@ -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) {
}
}

View File

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

View File

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

View File

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

View File

@@ -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;

View File

@@ -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.
*

View File

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

View File

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

View File

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

View File

@@ -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 &lt;receiver&gt; 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);
}
}
}

View File

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

View File

@@ -1,4 +0,0 @@
/**
* Contains the Mapbox Maps Android Telemetry API classes.
*/
package com.mapbox.mapboxsdk.telemetry;

View File

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