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;