diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index 5c7ccda12c..d4fcf98018 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -568,6 +568,20 @@
android:name="com.pushwoosh.location.GeoLocationService"/>
+
+
+
+
+
+
+
+
diff --git a/android/src/com/mapswithme/maps/GeofenceReceiver.java b/android/src/com/mapswithme/maps/GeofenceReceiver.java
new file mode 100644
index 0000000000..ce23ea2212
--- /dev/null
+++ b/android/src/com/mapswithme/maps/GeofenceReceiver.java
@@ -0,0 +1,162 @@
+package com.mapswithme.maps;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
+import android.support.v4.content.LocalBroadcastManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.google.android.gms.location.Geofence;
+import com.mapswithme.maps.scheduling.GeofenceTransitionsIntentService;
+
+import java.util.List;
+
+public class GeofenceReceiver extends BroadcastReceiver
+{
+ Context context;
+
+ Intent broadcastIntent = new Intent();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ GeofenceTransitionsIntentService.enqueueWork(context, intent);
+
+ this.context = context;
+
+/* broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);
+
+ if (LocationClient.hasError(intent)) {
+ handleError(intent);
+ } else {
+ handleEnterExit(intent);
+ }*/
+ }
+/*
+
+ private void handleError(Intent intent){
+ // Get the error code
+ int errorCode = LocationClient.getErrorCode(intent);
+
+ // Get the error message
+ String errorMessage = LocationServiceErrorMessages.getErrorString(
+ context, errorCode);
+
+ // Log the error
+ Log.e(GeofenceUtils.APPTAG,
+ context.getString(R.string.geofence_transition_error_detail,
+ errorMessage));
+
+ // Set the action and error message for the broadcast intent
+ broadcastIntent
+ .setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)
+ .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage);
+
+ // Broadcast the error *locally* to other components in this app
+ LocalBroadcastManager.getInstance(context).sendBroadcast(
+ broadcastIntent);
+ }
+
+
+ private void handleEnterExit(Intent intent) {
+ // Get the type of transition (entry or exit)
+ int transition = LocationClient.getGeofenceTransition(intent);
+
+ // Test that a valid transition was reported
+ if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)
+ || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {
+
+ // Post a notification
+ List geofences = LocationClient
+ .getTriggeringGeofences(intent);
+ String[] geofenceIds = new String[geofences.size()];
+ String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER,
+ geofenceIds);
+ String transitionType = GeofenceUtils
+ .getTransitionString(transition);
+
+ for (int index = 0; index < geofences.size(); index++) {
+ Geofence geofence = geofences.get(index);
+ ...do something with the geofence entry or exit. I'm saving them to a local sqlite db
+
+ }
+ // Create an Intent to broadcast to the app
+ broadcastIntent
+ .setAction(GeofenceUtils.ACTION_GEOFENCE_TRANSITION)
+ .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
+ .putExtra(GeofenceUtils.EXTRA_GEOFENCE_ID, geofenceIds)
+ .putExtra(GeofenceUtils.EXTRA_GEOFENCE_TRANSITION_TYPE,
+ transitionType);
+
+ LocalBroadcastManager.getInstance(MyApplication.getContext())
+ .sendBroadcast(broadcastIntent);
+
+ // Log the transition type and a message
+ Log.d(GeofenceUtils.APPTAG, transitionType + ": " + ids);
+ Log.d(GeofenceUtils.APPTAG,
+ context.getString(R.string.geofence_transition_notification_text));
+
+ // In debug mode, log the result
+ Log.d(GeofenceUtils.APPTAG, "transition");
+
+ // An invalid transition was reported
+ } else {
+ // Always log as an error
+ Log.e(GeofenceUtils.APPTAG,
+ context.getString(R.string.geofence_transition_invalid_type,
+ transition));
+ }
+ }
+*/
+
+ /**
+ * Posts a notification in the notification bar when a transition is
+ * detected. If the user clicks the notification, control goes to the main
+ * Activity.
+ *
+ * @param transitionType
+ * The type of transition that occurred.
+ *
+ *//*
+ private void sendNotification(String transitionType, String locationName) {
+
+ // Create an explicit content Intent that starts the main Activity
+ Intent notificationIntent = new Intent(context, MainActivity.class);
+
+ // Construct a task stack
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
+
+ // Adds the main Activity to the task stack as the parent
+ stackBuilder.addParentStack(MainActivity.class);
+
+ // Push the content Intent onto the stack
+ stackBuilder.addNextIntent(notificationIntent);
+
+ // Get a PendingIntent containing the entire back stack
+ PendingIntent notificationPendingIntent = stackBuilder
+ .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ // Get a notification builder that's compatible with platform versions
+ // >= 4
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(
+ context);
+
+ // Set the notification contents
+ builder.setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle(transitionType + ": " + locationName)
+ .setContentText(
+ context.getString(R.string.geofence_transition_notification_text))
+ .setContentIntent(notificationPendingIntent);
+
+ // Get an instance of the Notification manager
+ NotificationManager mNotificationManager = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // Issue the notification
+ mNotificationManager.notify(0, builder.build());
+ }*/
+}
diff --git a/android/src/com/mapswithme/maps/GeofenceService.java b/android/src/com/mapswithme/maps/GeofenceService.java
new file mode 100644
index 0000000000..7e85f6a1e3
--- /dev/null
+++ b/android/src/com/mapswithme/maps/GeofenceService.java
@@ -0,0 +1,7 @@
+package com.mapswithme.maps;
+
+public interface GeofenceService
+{
+ void registryGeofences();
+ void invalidateGeofences();
+}
diff --git a/android/src/com/mapswithme/maps/GeofenceServiceImpl.java b/android/src/com/mapswithme/maps/GeofenceServiceImpl.java
new file mode 100644
index 0000000000..037c913f55
--- /dev/null
+++ b/android/src/com/mapswithme/maps/GeofenceServiceImpl.java
@@ -0,0 +1,19 @@
+package com.mapswithme.maps;
+
+class GeofenceServiceImpl implements GeofenceService
+{
+
+
+
+ @Override
+ public void registryGeofences()
+ {
+
+ }
+
+ @Override
+ public void invalidateGeofences()
+ {
+
+ }
+}
diff --git a/android/src/com/mapswithme/maps/GeofenceServiceStub.java b/android/src/com/mapswithme/maps/GeofenceServiceStub.java
new file mode 100644
index 0000000000..98fcdb864e
--- /dev/null
+++ b/android/src/com/mapswithme/maps/GeofenceServiceStub.java
@@ -0,0 +1,16 @@
+package com.mapswithme.maps;
+
+class GeofenceServiceStub implements GeofenceService
+{
+ @Override
+ public void registryGeofences()
+ {
+
+ }
+
+ @Override
+ public void invalidateGeofences()
+ {
+
+ }
+}
diff --git a/android/src/com/mapswithme/maps/LightFramework.java b/android/src/com/mapswithme/maps/LightFramework.java
index fe61e2b9d8..9bcaa8636d 100644
--- a/android/src/com/mapswithme/maps/LightFramework.java
+++ b/android/src/com/mapswithme/maps/LightFramework.java
@@ -6,14 +6,29 @@ import android.support.annotation.Nullable;
import com.mapswithme.maps.background.NotificationCandidate;
import com.mapswithme.maps.geofence.GeoFenceFeature;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
public class LightFramework
{
public static native boolean nativeIsAuthenticated();
public static native int nativeGetNumberUnsentUGC();
@NonNull
- public static native GeoFenceFeature[] nativeGetLocalAdsFeatures(double lat, double lon,
+ private static native GeoFenceFeature[] nativeGetLocalAdsFeatures(double lat, double lon,
double radiusInMeters,
int maxCount);
+ @NonNull
+ public static List getLocalAdsFeatures(double lat, double lon,
+ double radiusInMeters,
+ int maxCount)
+ {
+ return Collections.unmodifiableList(Arrays.asList(nativeGetLocalAdsFeatures(lat,
+ lon,
+ radiusInMeters,
+ maxCount)));
+ }
+
public static native void nativeLogLocalAdsEvent(int type, double lat, double lon,
int accuracyInMeters, long mwmVersion,
@NonNull String countryId, int featureIndex);
diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java
index 001fa1b31c..19dc556ba1 100644
--- a/android/src/com/mapswithme/maps/MwmActivity.java
+++ b/android/src/com/mapswithme/maps/MwmActivity.java
@@ -3,12 +3,14 @@ package com.mapswithme.maps;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.location.Location;
+import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.CallSuper;
@@ -29,6 +31,12 @@ import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.Toast;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingClient;
+import com.google.android.gms.location.GeofencingRequest;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.tasks.OnFailureListener;
+import com.google.android.gms.tasks.OnSuccessListener;
import com.mapswithme.maps.Framework.MapObjectListener;
import com.mapswithme.maps.activity.CustomNavigateUpListener;
import com.mapswithme.maps.ads.LikesManager;
@@ -66,6 +74,7 @@ import com.mapswithme.maps.editor.EditorHostFragment;
import com.mapswithme.maps.editor.FeatureCategoryActivity;
import com.mapswithme.maps.editor.ReportFragment;
import com.mapswithme.maps.gallery.Items;
+import com.mapswithme.maps.geofence.GeoFenceFeature;
import com.mapswithme.maps.location.CompassData;
import com.mapswithme.maps.location.LocationHelper;
import com.mapswithme.maps.maplayer.MapLayerCompositeController;
@@ -87,6 +96,7 @@ import com.mapswithme.maps.routing.RoutingBottomMenuListener;
import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.maps.routing.RoutingPlanFragment;
import com.mapswithme.maps.routing.RoutingPlanInplaceController;
+import com.mapswithme.maps.scheduling.GeofenceTransitionsIntentService;
import com.mapswithme.maps.search.BookingFilterParams;
import com.mapswithme.maps.search.FilterActivity;
import com.mapswithme.maps.search.FloatingSearchToolbarController;
@@ -134,9 +144,11 @@ import com.mapswithme.util.statistics.PlacePageTracker;
import com.mapswithme.util.statistics.Statistics;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
+import java.util.concurrent.TimeUnit;
public class MwmActivity extends BaseMwmFragmentActivity
implements MapObjectListener,
@@ -597,6 +609,65 @@ public class MwmActivity extends BaseMwmFragmentActivity
}
initTips();
+ GeofencingClient geofencingClient = LocationServices.getGeofencingClient(this);
+
+ Location lastKnownLocation = LocationHelper.INSTANCE.getLastKnownLocation();
+ if (lastKnownLocation == null)
+ return;
+ List adsFeatures = LightFramework.getLocalAdsFeatures(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude(), 50 * 1000, 20);
+ List geofences = new ArrayList<>();
+ Geofence geofence = new Geofence.Builder()
+ // Set the request ID of the geofence. This is a string to identify this
+ // geofence.
+
+ .setRequestId(String.valueOf(System.currentTimeMillis()))
+ .setCircularRegion(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude(), 100)
+ .setExpirationDuration(Geofence.NEVER_EXPIRE)
+ .setLoiteringDelay(5000)
+ .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL)
+ .build();
+
+ geofences.add(geofence);
+ for (GeoFenceFeature each : adsFeatures)
+ {
+
+
+ }
+ GeofencingRequest geofencingRequest = getGeofencingRequest(geofences);
+
+ geofencingClient.addGeofences(geofencingRequest, getGeofencePendingIntent()).addOnSuccessListener(new OnSuccessListener()
+
+
+ {
+ @Override
+ public void onSuccess(Void aVoid)
+ {
+
+ }
+ }).addOnFailureListener(new OnFailureListener()
+ {
+ @Override
+ public void onFailure(@NonNull Exception e)
+ {
+
+ }
+ });
+ }
+ private PendingIntent getGeofencePendingIntent() {
+ // Reuse the PendingIntent if we already have it.
+ PendingIntent mGeofencePendingIntent = null;
+ Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
+ // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
+ // addGeofences() and removeGeofences().
+ mGeofencePendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return mGeofencePendingIntent;
+ }
+
+ private GeofencingRequest getGeofencingRequest(List mGeofenceList) {
+ GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
+ builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL);
+ builder.addGeofences(mGeofenceList);
+ return builder.build();
}
private void initViews()
diff --git a/android/src/com/mapswithme/maps/geofence/GeoFenceFeature.java b/android/src/com/mapswithme/maps/geofence/GeoFenceFeature.java
index c91abea906..14cff71a18 100644
--- a/android/src/com/mapswithme/maps/geofence/GeoFenceFeature.java
+++ b/android/src/com/mapswithme/maps/geofence/GeoFenceFeature.java
@@ -49,4 +49,26 @@ public class GeoFenceFeature
{
return longitude;
}
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ GeoFenceFeature that = (GeoFenceFeature) o;
+
+ if (mwmVersion != that.mwmVersion) return false;
+ if (featureIndex != that.featureIndex) return false;
+ return countryId.equals(that.countryId);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (mwmVersion ^ (mwmVersion >>> 32));
+ result = 31 * result + countryId.hashCode();
+ result = 31 * result + featureIndex;
+ return result;
+ }
}
diff --git a/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java b/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java
index 6a8f02e86c..9d45aae67b 100644
--- a/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java
+++ b/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java
@@ -89,7 +89,7 @@ public class ConnectivityJobScheduler implements ConnectivityListener
.Builder(jobId, component)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
- .setMinimumLatency(TimeUnit.HOURS.toMillis(SCHEDULE_PERIOD_IN_HOURS))
+ .setMinimumLatency(1000 * 60 * 60)
.build();
mJobScheduler.schedule(jobInfo);
}
diff --git a/android/src/com/mapswithme/maps/scheduling/GeofenceTransitionsIntentService.java b/android/src/com/mapswithme/maps/scheduling/GeofenceTransitionsIntentService.java
new file mode 100644
index 0000000000..b18ebce20d
--- /dev/null
+++ b/android/src/com/mapswithme/maps/scheduling/GeofenceTransitionsIntentService.java
@@ -0,0 +1,40 @@
+package com.mapswithme.maps.scheduling;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.v4.app.JobIntentService;
+
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingEvent;
+import com.mapswithme.maps.LightFramework;
+
+public class GeofenceTransitionsIntentService extends JobIntentService
+{
+ @Override
+ protected void onHandleWork(@NonNull Intent intent)
+ {
+ GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
+ if (geofencingEvent.hasError())
+ return;
+ int transitionType = geofencingEvent.getGeofenceTransition();
+ if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER
+ || transitionType == Geofence.GEOFENCE_TRANSITION_EXIT)
+ {
+
+ Geofence geofence = geofencingEvent.getTriggeringGeofences().get(0);
+
+
+// LightFramework.nativeLogLocalAdsEvent(1, /* myPlaceLat */, /* myPlaceLon */, /* locationProviderAccuracy */, , , );
+ }
+ }
+
+ /**
+ * Convenience method for enqueuing work in to this service.
+ */
+ public static void enqueueWork(Context context, Intent intent) {
+ int id = JobIdMap.getId(GeofenceTransitionsIntentService.class);
+ enqueueWork(context, GeofenceTransitionsIntentService.class, id, intent);
+ }
+
+}
diff --git a/android/src/com/mapswithme/maps/scheduling/JobIdMap.java b/android/src/com/mapswithme/maps/scheduling/JobIdMap.java
index b5251156e2..e2804464a6 100644
--- a/android/src/com/mapswithme/maps/scheduling/JobIdMap.java
+++ b/android/src/com/mapswithme/maps/scheduling/JobIdMap.java
@@ -19,6 +19,7 @@ public class JobIdMap
MAP.put(TrackRecorderWakeService.class, calcIdentifier(MAP.size()));
MAP.put(SystemDownloadCompletedService.class, calcIdentifier(MAP.size()));
MAP.put(WorkerService.class, calcIdentifier(MAP.size()));
+ MAP.put(GeofenceTransitionsIntentService.class, calcIdentifier(MAP.size()));
}
private static final int ID_BASIC = 1070;