From 64b24cef1b9827670b11a0a1c2cd3b8a14d4cdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Wrzeszczy=C5=84ski?= Date: Fri, 9 Nov 2018 00:49:09 +0100 Subject: [PATCH 1/7] Add approximated localizations --- app/build.gradle | 2 +- app/src/main/assets/building.geojson | 386 ++++++++++++++++++ .../wmi/findmytutor/activity/MapActivity.java | 1 + .../uam/wmi/findmytutor/model/Coordinate.java | 3 +- .../BackgroundLocalizationService.java | 15 +- .../utils/ApproximatedLocalization.java | 64 +++ .../uam/wmi/findmytutor/utils/mapUtils.java | 2 + app/src/main/res/values/colors.xml | 10 +- 8 files changed, 474 insertions(+), 9 deletions(-) create mode 100644 app/src/main/assets/building.geojson create mode 100644 app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java diff --git a/app/build.gradle b/app/build.gradle index 87b98e5..aad18fc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,7 +58,7 @@ dependencies { implementation 'com.auth0.android:jwtdecode:1.1.1' implementation 'com.annimon:stream:1.2.1' implementation 'com.google.android.gms:play-services-location:16.0.0' - + implementation 'com.mapbox.mapboxsdk:mapbox-sdk-turf:4.0.0' // FloatingBarMenu implementation 'com.getbase:floatingactionbutton:1.10.1' diff --git a/app/src/main/assets/building.geojson b/app/src/main/assets/building.geojson new file mode 100644 index 0000000..e000dd8 --- /dev/null +++ b/app/src/main/assets/building.geojson @@ -0,0 +1,386 @@ +{ + "features": [ + { + "type": "Feature", + "properties": { + "name": "Skrzydło B" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.92618, + 52.466248 + ], + [ + 16.926435, + 52.466201 + ], + [ + 16.92646, + 52.466255 + ], + [ + 16.926516, + 52.466244 + ], + [ + 16.926999, + 52.46711 + ], + [ + 16.926796, + 52.467145 + ], + [ + 16.926782, + 52.467117 + ], + [ + 16.926784, + 52.467121 + ], + [ + 16.926691, + 52.467139 + ], + [ + 16.926662, + 52.46709 + ], + [ + 16.926757, + 52.467072 + ], + [ + 16.926544, + 52.466691 + ], + [ + 16.926434, + 52.46671 + ], + [ + 16.926396, + 52.466655 + ], + [ + 16.926519, + 52.466628 + ], + [ + 16.926323, + 52.466281 + ], + [ + 16.926213, + 52.466307 + ], + [ + 16.92618, + 52.466248 + ] + ] + ], + "type": "Polygon" + }, + "id": "41798cf663bc55c10e6c51c3fe174eda" + }, + { + "type": "Feature", + "properties": { + "name": "Biblioteka" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.927048, + 52.46721 + ], + [ + 16.926627, + 52.467295 + ], + [ + 16.926732, + 52.467482 + ], + [ + 16.926876, + 52.467461 + ], + [ + 16.926967, + 52.467428 + ], + [ + 16.927014, + 52.467402 + ], + [ + 16.927171, + 52.467359 + ], + [ + 16.9271, + 52.467202 + ], + [ + 16.927048, + 52.46721 + ] + ] + ], + "type": "Polygon" + }, + "id": "7328c3c9dffd3e76be8d3dcef4f58ddc" + }, + { + "type": "Feature", + "properties": { + "name": "Łącznik" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926759, + 52.466683 + ], + [ + 16.927088, + 52.46661 + ], + [ + 16.927019, + 52.466502 + ], + [ + 16.9267, + 52.466571 + ], + [ + 16.926759, + 52.466683 + ] + ] + ], + "type": "Polygon" + }, + "id": "8c19ee28e4c07ece9756fd21f290713b" + }, + { + "type": "Feature", + "properties": { + "name": "Skrzydło A" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926771, + 52.466106 + ], + [ + 16.926898, + 52.466079 + ], + [ + 16.926926, + 52.466126 + ], + [ + 16.927026, + 52.466107 + ], + [ + 16.92723, + 52.466473 + ], + [ + 16.927184, + 52.466483 + ], + [ + 16.927243, + 52.466586 + ], + [ + 16.927334, + 52.46656 + ], + [ + 16.927602, + 52.466889 + ], + [ + 16.927275, + 52.466959 + ], + [ + 16.926771, + 52.466106 + ] + ] + ], + "type": "Polygon" + }, + "id": "c33bfba772c85cc38ae417843d31b1ff" + }, + { + "type": "Feature", + "properties": { + "name": "Aule" + }, + "geometry": { + "coordinates": [ + [ + [ + 16.92731, + 52.467158 + ], + [ + 16.927315, + 52.46718 + ], + [ + 16.927348, + 52.467199 + ], + [ + 16.927393, + 52.467224 + ], + [ + 16.927426, + 52.467239 + ], + [ + 16.927476, + 52.467258 + ], + [ + 16.92755, + 52.467264 + ], + [ + 16.927625, + 52.467263 + ], + [ + 16.927699, + 52.467246 + ], + [ + 16.92776, + 52.467212 + ], + [ + 16.927821, + 52.467158 + ], + [ + 16.927838, + 52.467099 + ], + [ + 16.927827, + 52.467059 + ], + [ + 16.927793, + 52.467012 + ], + [ + 16.927738, + 52.466976 + ], + [ + 16.927661, + 52.466949 + ], + [ + 16.927581, + 52.466939 + ], + [ + 16.927534, + 52.466938 + ], + [ + 16.927467, + 52.466949 + ], + [ + 16.927387, + 52.467047 + ], + [ + 16.927315, + 52.467153 + ], + [ + 16.92731, + 52.467158 + ] + ] + ], + "type": "Polygon" + }, + "id": "c779419e3fd7faef8555e1099547a82c" + }, + { + "type": "Feature", + "properties": { + "name": "Hol", + "point" : [52.467088, 16.927150] + }, + "geometry": { + "coordinates": [ + [ + [ + 16.926625, + 52.467294 + ], + [ + 16.926568, + 52.46719 + ], + [ + 16.927008, + 52.467108 + ], + [ + 16.926953, + 52.467024 + ], + [ + 16.927463, + 52.466919 + ], + [ + 16.927467, + 52.466956 + ], + [ + 16.927316, + 52.467153 + ], + [ + 16.926907, + 52.467235 + ], + [ + 16.926625, + 52.467294 + ] + ] + ], + "type": "Polygon" + }, + "id": "facdd5349991758b9ad99d4e123c91cc" + } + ], + "type": "FeatureCollection" +} \ No newline at end of file diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java index 47da9ea..ab134d2 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java @@ -235,6 +235,7 @@ public class MapActivity extends BaseActivity latLng.getLatitude(), latLng.getLongitude(), latLng.getAltitude(), + "approx", PrefUtils.getUserFirstName(getApplicationContext()) + " " + PrefUtils.getUserLastName(getApplicationContext()), PrefUtils.getUserId(getApplicationContext()), PrefUtils.getLocationLevel(getApplicationContext()) diff --git a/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java b/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java index 5c23701..1f838e4 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java @@ -44,7 +44,7 @@ public class Coordinate extends BaseResponse { @SerializedName("label") private String label; - public Coordinate (Double latitude, Double longitude, Double altitude, String label, String userId, String displayMode) { + public Coordinate (Double latitude, Double longitude, Double altitude, String approximatedLocation, String label, String userId, String displayMode) { //if (!latitudeRange.contains(latitude)) throw new IllegalArgumentException("Inappropriate latitude value" + latitude); //if (!longtitudeRange.contains(longitude)) throw new IllegalArgumentException("Inappropriate longitude value" + longitude); @@ -53,6 +53,7 @@ public class Coordinate extends BaseResponse { this.altitude = altitude; this.label = label; this.userId = userId; + this.approximatedLocation = approximatedLocation; this.displayMode = displayMode; } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java index 2b3ca3c..44a0d08 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java @@ -27,14 +27,16 @@ import com.google.android.gms.location.FusedLocationProviderClient; import com.google.android.gms.location.LocationServices; import com.google.android.gms.tasks.OnSuccessListener; import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; +import com.mapbox.geojson.Point; import com.uam.wmi.findmytutor.model.Coordinate; import com.uam.wmi.findmytutor.network.ApiClient; +import com.uam.wmi.findmytutor.utils.ApproximatedLocalization; import com.uam.wmi.findmytutor.utils.PrefUtils; import com.uam.wmi.findmytutor.utils.RestApiHelper; +import com.uam.wmi.findmytutor.utils.mapUtils; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executor; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -43,6 +45,7 @@ import io.reactivex.schedulers.Schedulers; import okhttp3.ResponseBody; import timber.log.Timber; +import static com.mapbox.geojson.Point.fromLngLat; import static com.uam.wmi.findmytutor.utils.PrefUtils.storeBackgroundLocationStatus; public class BackgroundLocalizationService extends Service { @@ -64,6 +67,8 @@ public class BackgroundLocalizationService extends Service { private Runnable mStatusChecker; private FusedLocationProviderClient mFusedLocationClient; + + public BackgroundLocalizationService() { providers.add(LocationManager.GPS_PROVIDER); providers.add(LocationManager.NETWORK_PROVIDER); @@ -74,6 +79,7 @@ public class BackgroundLocalizationService extends Service { new LocationListener(LocationManager.NETWORK_PROVIDER), new LocationListener(LocationManager.PASSIVE_PROVIDER) }; + } @Override @@ -292,20 +298,27 @@ public class BackgroundLocalizationService extends Service { private CoordinateService coordinateService = ApiClient.getClient(getApplicationContext()) .create(CoordinateService.class); + ApproximatedLocalization approximatedLocalization; + private Task(Location location) { latitude = location.getLatitude(); longitude = location.getLongitude(); altitude = location.getAltitude(); + approximatedLocalization = new ApproximatedLocalization(mapUtils.loadJsonFromAsset(getApplicationContext(),"building.geojson")); } @Override protected Object doInBackground(Object[] objects) { +/* + Point point = approximatedLocalization.getPointerBuildingPart("Aule"); + Log.e("APPROXIMATED", String.valueOf(point));*/ try { Coordinate coordinate = new Coordinate( latitude, longitude, altitude, + approximatedLocalization.getNameOfBuildingPart(Point.fromLngLat(longitude, latitude)), PrefUtils.getUserStatus(getApplicationContext()), PrefUtils.getUserId(getApplicationContext()), PrefUtils.getLocationLevel(getApplicationContext()) diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java new file mode 100644 index 0000000..56308d1 --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java @@ -0,0 +1,64 @@ +package com.uam.wmi.findmytutor.utils; + +import android.support.annotation.NonNull; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.mapbox.geojson.BoundingBox; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Geometry; +import com.mapbox.geojson.Point; +import com.mapbox.geojson.Polygon; +import com.mapbox.geojson.gson.BoundingBoxDeserializer; +import com.mapbox.geojson.gson.GeoJsonAdapterFactory; +import com.mapbox.geojson.gson.GeometryDeserializer; +import com.mapbox.geojson.gson.PointDeserializer; +import com.mapbox.turf.TurfJoins; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ApproximatedLocalization { + private FeatureCollection buildingSchema = null; + + public ApproximatedLocalization(String buildingObject){ + buildingSchema = fromJson(buildingObject); + } + + private FeatureCollection fromJson(@NonNull String json) { + GsonBuilder gson = new GsonBuilder(); + gson.registerTypeAdapterFactory(GeoJsonAdapterFactory.create()); + gson.registerTypeAdapter(Point.class, new PointDeserializer()); + gson.registerTypeAdapter(Geometry.class, new GeometryDeserializer()); + gson.registerTypeAdapter(BoundingBox.class, new BoundingBoxDeserializer()); + return gson.create().fromJson(json, FeatureCollection.class); + } + + public String getNameOfBuildingPart(Point point) { + + for(Feature feature : Objects.requireNonNull(buildingSchema.features())){ + boolean isInside = TurfJoins.inside(point, (Polygon) Objects.requireNonNull(feature.geometry())); + + if (isInside){ + return Objects.requireNonNull(Objects.requireNonNull(feature.getStringProperty("name"))); + } + } + + return null; + } +/* + public Point getMiddlePointForBuildingPart(String BuildingPart) { + for(Feature feature : Objects.requireNonNull(buildingSchema.features())){ + String partName = feature.getStringProperty("name"); + + if(BuildingPart.equals(partName)) { + JsonElement pointsList = feature.getProperty("point"); + return Point.fromLngLat(pointsList.getAsNumber().doubleValue(), pointsList.getAsJsonArray().get(1)); + } + } + + return Point.fromLngLat(0,0); + }*/ +} diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/mapUtils.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/mapUtils.java index a58ca10..6c7b948 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/mapUtils.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/mapUtils.java @@ -2,6 +2,7 @@ package com.uam.wmi.findmytutor.utils; import android.animation.TypeEvaluator; import android.content.Context; +import android.content.res.AssetManager; import com.mapbox.mapboxsdk.geometry.LatLng; @@ -41,4 +42,5 @@ public class mapUtils { return null; } } + } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index efbf417..be1a4f3 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,19 +1,17 @@ - @color/mapboxGray - @color/mapboxGrayDark10 - @color/mapboxPink - #cf5e5e + #476483 + #3A3553 + #CF0E0F + #89c3c3c3 #89c3c3c3 #858585 #232323 - #d1e200f6 #ffffff #80ffffff - #F5F5F5 #dfdfdf From 77b7c50e4e2e68ced55bf36a468a96aea353e174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Wrzeszczy=C5=84ski?= Date: Fri, 9 Nov 2018 00:50:15 +0100 Subject: [PATCH 2/7] Add approximated localization --- .../uam/wmi/findmytutor/utils/ApproximatedLocalization.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java index 56308d1..71074ca 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java @@ -2,8 +2,7 @@ package com.uam.wmi.findmytutor.utils; import android.support.annotation.NonNull; import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; +; import com.mapbox.geojson.BoundingBox; import com.mapbox.geojson.Feature; import com.mapbox.geojson.FeatureCollection; @@ -16,8 +15,6 @@ import com.mapbox.geojson.gson.GeometryDeserializer; import com.mapbox.geojson.gson.PointDeserializer; import com.mapbox.turf.TurfJoins; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; public class ApproximatedLocalization { From aa3c2eb9b8be82bddb887094356d89ba9079f859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Wrzeszczy=C5=84ski?= Date: Mon, 19 Nov 2018 00:03:31 +0100 Subject: [PATCH 3/7] Send middle of polygon in approximated mode --- app/src/main/assets/building.geojson | 24 ++++++--- .../findmytutor/activity/SharingFragment.java | 10 ++-- .../uam/wmi/findmytutor/model/Coordinate.java | 1 + .../BackgroundLocalizationService.java | 38 +++++++++----- .../utils/ApproximatedLocalization.java | 50 +++++++++++++------ .../wmi/findmytutor/utils/SharingLevel.java | 18 +++++++ 6 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/com/uam/wmi/findmytutor/utils/SharingLevel.java diff --git a/app/src/main/assets/building.geojson b/app/src/main/assets/building.geojson index e000dd8..a151032 100644 --- a/app/src/main/assets/building.geojson +++ b/app/src/main/assets/building.geojson @@ -3,7 +3,9 @@ { "type": "Feature", "properties": { - "name": "Skrzydło B" + "name": "Skrzydło B", + "longitude" : 52.466669, + "latitude" : 16.926624 }, "geometry": { "coordinates": [ @@ -89,7 +91,9 @@ { "type": "Feature", "properties": { - "name": "Biblioteka" + "name": "Biblioteka", + "longitude" : 52.467351, + "latitude" : 16.926900 }, "geometry": { "coordinates": [ @@ -139,7 +143,9 @@ { "type": "Feature", "properties": { - "name": "Łącznik" + "name": "Łącznik", + "longitude" : 52.466619, + "latitude" : 16.926860 }, "geometry": { "coordinates": [ @@ -173,7 +179,9 @@ { "type": "Feature", "properties": { - "name": "Skrzydło A" + "name": "Skrzydło A", + "longitude" : 52.466559, + "latitude" : 16.927163 }, "geometry": { "coordinates": [ @@ -231,7 +239,10 @@ { "type": "Feature", "properties": { - "name": "Aule" + "name": "Aule", + "longitude" : 52.467114, + "latitude" : 16.927621 + }, "geometry": { "coordinates": [ @@ -334,7 +345,8 @@ "type": "Feature", "properties": { "name": "Hol", - "point" : [52.467088, 16.927150] + "longitude" : 52.4671021, + "latitude" : 16.927122 }, "geometry": { "coordinates": [ diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java index 839f673..38bff8d 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/SharingFragment.java @@ -19,6 +19,7 @@ import android.widget.Toast; import com.uam.wmi.findmytutor.R; import com.uam.wmi.findmytutor.service.BackgroundLocalizationService; import com.uam.wmi.findmytutor.utils.PrefUtils; +import com.uam.wmi.findmytutor.utils.SharingLevel; import java.util.Arrays; import java.util.HashMap; @@ -40,9 +41,10 @@ public class SharingFragment extends PreferenceFragment { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); locationLevelMapping = new HashMap(); - locationLevelMapping.put(0,"presence"); - locationLevelMapping.put(1,"approximated"); - locationLevelMapping.put(2,"exact"); + + locationLevelMapping.put(0, SharingLevel.PRESENCE.toString()); + locationLevelMapping.put(1, SharingLevel.APPROXIMATED.toString()); + locationLevelMapping.put(2, SharingLevel.EXACT.toString()); addPreferencesFromResource(R.layout.pref_sharing); Preference manualStatus = findPreference("key_manual_status"); @@ -60,8 +62,8 @@ public class SharingFragment extends PreferenceFragment { }); locationMode.setOnPreferenceChangeListener((preference, newValue) -> { - ListPreference lp = (ListPreference) preference; PrefUtils.storeLocationMode(getApplicationContext(),locationLevelMapping.get(Integer.parseInt((String) newValue))); + return true; }); diff --git a/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java b/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java index 1f838e4..9b9d580 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/model/Coordinate.java @@ -47,6 +47,7 @@ public class Coordinate extends BaseResponse { public Coordinate (Double latitude, Double longitude, Double altitude, String approximatedLocation, String label, String userId, String displayMode) { //if (!latitudeRange.contains(latitude)) throw new IllegalArgumentException("Inappropriate latitude value" + latitude); //if (!longtitudeRange.contains(longitude)) throw new IllegalArgumentException("Inappropriate longitude value" + longitude); + //if (approximatedLocation == null) throw new IllegalArgumentException("Inappropriate approximatedLocation"); this.latitude = latitude; this.longitude = longitude; diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java index 44a0d08..3a09b6f 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java @@ -33,6 +33,7 @@ import com.uam.wmi.findmytutor.network.ApiClient; import com.uam.wmi.findmytutor.utils.ApproximatedLocalization; import com.uam.wmi.findmytutor.utils.PrefUtils; import com.uam.wmi.findmytutor.utils.RestApiHelper; +import com.uam.wmi.findmytutor.utils.SharingLevel; import com.uam.wmi.findmytutor.utils.mapUtils; import java.util.ArrayList; @@ -45,7 +46,6 @@ import io.reactivex.schedulers.Schedulers; import okhttp3.ResponseBody; import timber.log.Timber; -import static com.mapbox.geojson.Point.fromLngLat; import static com.uam.wmi.findmytutor.utils.PrefUtils.storeBackgroundLocationStatus; public class BackgroundLocalizationService extends Service { @@ -68,7 +68,6 @@ public class BackgroundLocalizationService extends Service { private FusedLocationProviderClient mFusedLocationClient; - public BackgroundLocalizationService() { providers.add(LocationManager.GPS_PROVIDER); providers.add(LocationManager.NETWORK_PROVIDER); @@ -96,7 +95,7 @@ public class BackgroundLocalizationService extends Service { stopService = intent.getBooleanExtra("request_stop", false); } if (stopService) { - storeBackgroundLocationStatus(getApplication(),false); + storeBackgroundLocationStatus(getApplication(), false); stopForeground(true); stopSelf(); return START_STICKY; @@ -108,7 +107,7 @@ public class BackgroundLocalizationService extends Service { @Override public void onCreate() { Log.e(TAG, "onCreate"); - storeBackgroundLocationStatus(getApplication(),true); + storeBackgroundLocationStatus(getApplication(), true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startMyOwnForeground(); @@ -143,7 +142,7 @@ public class BackgroundLocalizationService extends Service { providerIndex++; } - if(!stopService){ + if (!stopService) { mStatusChecker = () -> { try { fn_getlocation(); @@ -289,36 +288,49 @@ public class BackgroundLocalizationService extends Service { } } + @SuppressLint("StaticFieldLeak") private class Task extends AsyncTask { + ApproximatedLocalization approximatedLocalization; private Double latitude; private Double longitude; private Double altitude; - + private String approximatedBuildingPart = null; private CompositeDisposable disposable = new CompositeDisposable(); private CoordinateService coordinateService = ApiClient.getClient(getApplicationContext()) .create(CoordinateService.class); - ApproximatedLocalization approximatedLocalization; - private Task(Location location) { latitude = location.getLatitude(); longitude = location.getLongitude(); altitude = location.getAltitude(); - approximatedLocalization = new ApproximatedLocalization(mapUtils.loadJsonFromAsset(getApplicationContext(),"building.geojson")); + approximatedLocalization = new ApproximatedLocalization(mapUtils.loadJsonFromAsset(getApplicationContext(), "building.geojson")); + approximatedBuildingPart = approximatedLocalization.getNameOfBuildingPart(Point.fromLngLat(longitude, latitude)); } @Override protected Object doInBackground(Object[] objects) { -/* - Point point = approximatedLocalization.getPointerBuildingPart("Aule"); - Log.e("APPROXIMATED", String.valueOf(point));*/ + + if (PrefUtils.getLocationLevel(getApplicationContext()).equals(SharingLevel.PRESENCE.toString())) { + longitude = 52.467491; + latitude = 16.926782; + Log.e(TAG,"PRESENCE"); + } else if (PrefUtils.getLocationLevel(getApplicationContext()).equals(SharingLevel.APPROXIMATED.toString())) { + List points = approximatedLocalization.getPointerBuildingPart(approximatedBuildingPart); + + latitude = points.get(0); + longitude = points.get(1); + Log.e(TAG,"APPROXIMATED"); + }else { + Log.e(TAG,"EXACT"); + } + try { Coordinate coordinate = new Coordinate( latitude, longitude, altitude, - approximatedLocalization.getNameOfBuildingPart(Point.fromLngLat(longitude, latitude)), + approximatedBuildingPart, PrefUtils.getUserStatus(getApplicationContext()), PrefUtils.getUserId(getApplicationContext()), PrefUtils.getLocationLevel(getApplicationContext()) diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java index 71074ca..39aca2a 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java @@ -1,8 +1,12 @@ package com.uam.wmi.findmytutor.utils; import android.support.annotation.NonNull; +import android.util.Log; + import com.google.gson.GsonBuilder; -; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.mapbox.geojson.BoundingBox; import com.mapbox.geojson.Feature; import com.mapbox.geojson.FeatureCollection; @@ -15,47 +19,61 @@ import com.mapbox.geojson.gson.GeometryDeserializer; import com.mapbox.geojson.gson.PointDeserializer; import com.mapbox.turf.TurfJoins; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Objects; +; + public class ApproximatedLocalization { private FeatureCollection buildingSchema = null; - public ApproximatedLocalization(String buildingObject){ + public ApproximatedLocalization(String buildingObject) { buildingSchema = fromJson(buildingObject); } - private FeatureCollection fromJson(@NonNull String json) { + private FeatureCollection fromJson(@NonNull String json) { GsonBuilder gson = new GsonBuilder(); + gson.registerTypeAdapterFactory(GeoJsonAdapterFactory.create()); gson.registerTypeAdapter(Point.class, new PointDeserializer()); gson.registerTypeAdapter(Geometry.class, new GeometryDeserializer()); gson.registerTypeAdapter(BoundingBox.class, new BoundingBoxDeserializer()); + return gson.create().fromJson(json, FeatureCollection.class); } - public String getNameOfBuildingPart(Point point) { + public String getNameOfBuildingPart(Point point) { - for(Feature feature : Objects.requireNonNull(buildingSchema.features())){ + for (Feature feature : Objects.requireNonNull(buildingSchema.features())) { boolean isInside = TurfJoins.inside(point, (Polygon) Objects.requireNonNull(feature.geometry())); - if (isInside){ - return Objects.requireNonNull(Objects.requireNonNull(feature.getStringProperty("name"))); - } + if (isInside) + return Objects.requireNonNull(Objects.requireNonNull(feature.getStringProperty("name"))); } return null; } -/* - public Point getMiddlePointForBuildingPart(String BuildingPart) { - for(Feature feature : Objects.requireNonNull(buildingSchema.features())){ + + public List getPointerBuildingPart(String buildingPart) { + + for (Feature feature : Objects.requireNonNull(buildingSchema.features())) { String partName = feature.getStringProperty("name"); - if(BuildingPart.equals(partName)) { - JsonElement pointsList = feature.getProperty("point"); - return Point.fromLngLat(pointsList.getAsNumber().doubleValue(), pointsList.getAsJsonArray().get(1)); + if (buildingPart != null && buildingPart.equals(partName)) { + Double longitude = feature.getNumberProperty("longitude").doubleValue(); + Double latitude = feature.getNumberProperty("latitude").doubleValue(); + + List list = Arrays.asList(longitude,latitude); + + Log.e("APPROX",partName + " " + buildingPart + list); + + return list; + } } - return Point.fromLngLat(0,0); - }*/ + return Arrays.asList(0.0,0.0); + } } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/SharingLevel.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/SharingLevel.java new file mode 100644 index 0000000..055651b --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/SharingLevel.java @@ -0,0 +1,18 @@ +package com.uam.wmi.findmytutor.utils; + +public enum SharingLevel { + PRESENCE("presence"), + APPROXIMATED("approximated"), + EXACT("exact"); + + private final String text; + + SharingLevel(final String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } +} \ No newline at end of file From d5114a8587e02b8d05f1df4fc10d2c90e7a764ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Wrzeszczy=C5=84ski?= Date: Mon, 19 Nov 2018 11:49:28 +0100 Subject: [PATCH 4/7] Add presence mode --- .../wmi/findmytutor/activity/MapActivity.java | 12 +++++-- .../BackgroundLocalizationService.java | 34 +++++++------------ .../utils/ApproximatedLocalization.java | 16 ++------- .../com/uam/wmi/findmytutor/utils/Consts.java | 7 ++++ 4 files changed, 32 insertions(+), 37 deletions(-) create mode 100644 app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java index ab134d2..9880c1b 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java @@ -21,6 +21,7 @@ import android.widget.Button; import android.widget.TextView; import android.widget.Toast; +import com.annimon.stream.Stream; import com.jakewharton.retrofit2.adapter.rxjava2.HttpException; import com.mapbox.android.core.permissions.PermissionsListener; import com.mapbox.android.core.permissions.PermissionsManager; @@ -50,15 +51,20 @@ import com.uam.wmi.findmytutor.service.CoordinateService; import com.uam.wmi.findmytutor.service.UserService; import com.uam.wmi.findmytutor.utils.PrefUtils; import com.uam.wmi.findmytutor.utils.RestApiHelper; +import com.uam.wmi.findmytutor.utils.SharingLevel; import com.uam.wmi.findmytutor.utils.mapUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.annotations.NonNull; import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.functions.Predicate; import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.schedulers.Schedulers; import okhttp3.ResponseBody; @@ -70,6 +76,7 @@ import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; +import static java.util.stream.Collectors.toList; public class MapActivity extends BaseActivity @@ -339,10 +346,11 @@ public class MapActivity extends BaseActivity private void fetchTopCoords() { disposable.add( - // coordinateService.getTopCoordinates() coordinateService.getOnlineCoordinates() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) + .map(tutors -> Stream.of(tutors). + filterNot(t -> t.getDisplayMode().equals(SharingLevel.PRESENCE.toString())).toList()) .subscribeWith(new DisposableSingleObserver>() { @Override @@ -364,7 +372,7 @@ public class MapActivity extends BaseActivity if (previousCoordsIds.isEmpty()){ previousCoordsIds.addAll(currentCoordsIds); } else { - // here we clear + it returns bool if smthing was removed + // here we clear + it returns bool if sth was removed if (previousCoordsIds.removeAll(currentCoordsIds)) { for (String toRemoveId: previousCoordsIds) { Log.e(tag+ "delete: " , "removing: " + toRemoveId + ": " + markerHash.get(toRemoveId)); diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java index 3a09b6f..331e703 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/service/BackgroundLocalizationService.java @@ -31,6 +31,7 @@ import com.mapbox.geojson.Point; import com.uam.wmi.findmytutor.model.Coordinate; import com.uam.wmi.findmytutor.network.ApiClient; import com.uam.wmi.findmytutor.utils.ApproximatedLocalization; +import com.uam.wmi.findmytutor.utils.Consts; import com.uam.wmi.findmytutor.utils.PrefUtils; import com.uam.wmi.findmytutor.utils.RestApiHelper; import com.uam.wmi.findmytutor.utils.SharingLevel; @@ -46,6 +47,9 @@ import io.reactivex.schedulers.Schedulers; import okhttp3.ResponseBody; import timber.log.Timber; +import static com.uam.wmi.findmytutor.utils.Consts.presenceApproximatedName; +import static com.uam.wmi.findmytutor.utils.Consts.presenceLatitude; +import static com.uam.wmi.findmytutor.utils.Consts.presencelongitude; import static com.uam.wmi.findmytutor.utils.PrefUtils.storeBackgroundLocationStatus; public class BackgroundLocalizationService extends Service { @@ -53,7 +57,6 @@ public class BackgroundLocalizationService extends Service { private static final String TAG = "MyLocationService"; private static final int LOCATION_INTERVAL = 1000; private static final float LOCATION_DISTANCE = 5f; - public static String str_receiver = "background.location.broadcast"; private static long notify_interval = 10000; Location mLastLocation; Boolean stopService = false; @@ -119,7 +122,6 @@ public class BackgroundLocalizationService extends Service { mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); - initializeLocationManager(); Integer providerIndex = 0; @@ -207,20 +209,12 @@ public class BackgroundLocalizationService extends Service { Log.e("Best localization:", String.valueOf(bestLocation)); - /* if (bestLocation != null) - fn_update(bestLocation); -*/ - mFusedLocationClient.getLastLocation().addOnSuccessListener( - new OnSuccessListener() { - @Override - public void onSuccess(Location location) { - if (location != null) { - mLastLocation = location; - fn_update(location); - } + location -> { + if (location != null) { + mLastLocation = location; + fn_update(location); } - }); } @@ -311,20 +305,16 @@ public class BackgroundLocalizationService extends Service { protected Object doInBackground(Object[] objects) { if (PrefUtils.getLocationLevel(getApplicationContext()).equals(SharingLevel.PRESENCE.toString())) { - longitude = 52.467491; - latitude = 16.926782; - Log.e(TAG,"PRESENCE"); + latitude = presenceLatitude; + longitude = presencelongitude; + approximatedBuildingPart = presenceApproximatedName; } else if (PrefUtils.getLocationLevel(getApplicationContext()).equals(SharingLevel.APPROXIMATED.toString())) { - List points = approximatedLocalization.getPointerBuildingPart(approximatedBuildingPart); + List points = approximatedLocalization.getMiddlePointOfBuildingPart(approximatedBuildingPart); latitude = points.get(0); longitude = points.get(1); - Log.e(TAG,"APPROXIMATED"); - }else { - Log.e(TAG,"EXACT"); } - try { Coordinate coordinate = new Coordinate( latitude, diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java index 39aca2a..4a42bf3 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/ApproximatedLocalization.java @@ -1,12 +1,8 @@ package com.uam.wmi.findmytutor.utils; import android.support.annotation.NonNull; -import android.util.Log; import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import com.mapbox.geojson.BoundingBox; import com.mapbox.geojson.Feature; import com.mapbox.geojson.FeatureCollection; @@ -19,7 +15,6 @@ import com.mapbox.geojson.gson.GeometryDeserializer; import com.mapbox.geojson.gson.PointDeserializer; import com.mapbox.turf.TurfJoins; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -56,7 +51,7 @@ public class ApproximatedLocalization { return null; } - public List getPointerBuildingPart(String buildingPart) { + public List getMiddlePointOfBuildingPart(String buildingPart) { for (Feature feature : Objects.requireNonNull(buildingSchema.features())) { String partName = feature.getStringProperty("name"); @@ -65,15 +60,10 @@ public class ApproximatedLocalization { Double longitude = feature.getNumberProperty("longitude").doubleValue(); Double latitude = feature.getNumberProperty("latitude").doubleValue(); - List list = Arrays.asList(longitude,latitude); - - Log.e("APPROX",partName + " " + buildingPart + list); - - return list; - + return Arrays.asList(longitude, latitude); } } - return Arrays.asList(0.0,0.0); + return Arrays.asList(0.0, 0.0); } } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java new file mode 100644 index 0000000..eef58b5 --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java @@ -0,0 +1,7 @@ +package com.uam.wmi.findmytutor.utils; + +public class Consts { + public final static Double presenceLatitude = 52.467491; + public final static Double presencelongitude = 16.926782; + public final static String presenceApproximatedName = "unknown"; +} From 50a427313f5c97e67d6f4df172a304660862109a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Wrzeszczy=C5=84ski?= Date: Tue, 20 Nov 2018 00:08:18 +0100 Subject: [PATCH 5/7] Improve user search --- .../findmytutor/activity/BaseActivity.java | 50 ++++++++++--------- .../wmi/findmytutor/activity/MapActivity.java | 13 +++-- .../activity/UsersListFragment.java | 1 + .../findmytutor/utils/RxSearchObservable.java | 36 +++++++++++++ 4 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java index 2f4cba0..5c2ff61 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java @@ -1,6 +1,7 @@ package com.uam.wmi.findmytutor.activity; import android.Manifest; +import android.annotation.SuppressLint; import android.app.Fragment; import android.app.FragmentTransaction; import android.content.Intent; @@ -19,6 +20,7 @@ import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -29,10 +31,17 @@ import com.uam.wmi.findmytutor.R; import com.uam.wmi.findmytutor.service.BackgroundLocalizationService; import com.uam.wmi.findmytutor.utils.ActiveFragment; import com.uam.wmi.findmytutor.utils.PrefUtils; +import com.uam.wmi.findmytutor.utils.RxSearchObservable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; + +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Consumer; +import io.reactivex.schedulers.Schedulers; import static com.uam.wmi.findmytutor.utils.PrefUtils.storeBackgroundLocationStatus; @@ -61,7 +70,9 @@ public abstract class BaseActivity private Fragment userListFragment; private ActiveFragment activeFragment = ActiveFragment.NONE; + private SearchView searchView; + @SuppressLint("CheckResult") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -121,6 +132,9 @@ public abstract class BaseActivity } + + + } @@ -241,41 +255,31 @@ public abstract class BaseActivity actionBarDrawerToggle.onConfigurationChanged(newConfig); } + @SuppressLint("CheckResult") @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); MenuItem myActionMenuItem = menu.findItem(R.id.action_search); - final SearchView searchView = (SearchView) myActionMenuItem.getActionView(); - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String input) { - if (activeFragment.equals(ActiveFragment.USER_LIST)) { - executeUserListSearch(input); - } + searchView = (SearchView) myActionMenuItem.getActionView(); - return false; - } - - @Override - - public boolean onQueryTextChange(String input) { - if (activeFragment.equals(ActiveFragment.USER_LIST)) { - executeUserListSearch(input); - } - - return true; - } - - }); + RxSearchObservable.fromView(searchView) + .map(String::toLowerCase) + .debounce(300, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::executeSearch); return true; } - private void executeUserListSearch(String input) { - ((UsersListFragment) userListFragment).searchUser(input); + private void executeSearch(String input) { + if (activeFragment.equals(ActiveFragment.USER_LIST)) { + ((UsersListFragment) userListFragment).searchUser(input); + } } + @Override public boolean onOptionsItemSelected(MenuItem item) { if (actionBarDrawerToggle.onOptionsItemSelected(item)) { diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java index 9880c1b..d9b67a6 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java @@ -349,8 +349,6 @@ public class MapActivity extends BaseActivity coordinateService.getOnlineCoordinates() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .map(tutors -> Stream.of(tutors). - filterNot(t -> t.getDisplayMode().equals(SharingLevel.PRESENCE.toString())).toList()) .subscribeWith(new DisposableSingleObserver>() { @Override @@ -362,12 +360,18 @@ public class MapActivity extends BaseActivity return; } - ArrayList tmp = new ArrayList<>(); + for (Coordinate coordinate : coordsList) { - tmp.add(coordinate.getUserId()); + Log.e(tag, coordinate.toString()); + if (coordinate.getDisplayMode().equals(SharingLevel.PRESENCE.toString())) { + Log.e(tag, "presence user omitted"); + } else { + tmp.add(coordinate.getUserId()); + } } + Set currentCoordsIds = new HashSet<>(tmp); if (previousCoordsIds.isEmpty()){ previousCoordsIds.addAll(currentCoordsIds); @@ -388,7 +392,6 @@ public class MapActivity extends BaseActivity - for (Coordinate element : coordsList) { String id = element.getUserId(); String newLabel = element.getLabel(); diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java index 82888b2..c2c8e53 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/UsersListFragment.java @@ -9,6 +9,7 @@ import android.support.v7.app.AlertDialog; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java new file mode 100644 index 0000000..6fc3b7a --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java @@ -0,0 +1,36 @@ +package com.uam.wmi.findmytutor.utils; + + +import android.support.v7.widget.SearchView; + +import io.reactivex.Observable; +import io.reactivex.subjects.PublishSubject; + + +public class RxSearchObservable { + + private RxSearchObservable() { + // no instance + } + + public static Observable fromView(SearchView searchView) { + + final PublishSubject subject = PublishSubject.create(); + + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + subject.onComplete(); + return false; + } + + @Override + public boolean onQueryTextChange(String text) { + subject.onNext(text); + return false; + } + }); + + return subject; + } +} From 892216cc0c96336000bda6b59844e77175fc3fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Wrzeszczy=C5=84ski?= Date: Tue, 20 Nov 2018 00:19:17 +0100 Subject: [PATCH 6/7] Fix search button --- .idea/inspectionProfiles/Project_Default.xml | 9 +++++++++ .../com/uam/wmi/findmytutor/activity/BaseActivity.java | 1 + .../uam/wmi/findmytutor/utils/RxSearchObservable.java | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..d7a7dfa --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java index 5c2ff61..5e2dd6d 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java @@ -266,6 +266,7 @@ public abstract class BaseActivity RxSearchObservable.fromView(searchView) .map(String::toLowerCase) .debounce(300, TimeUnit.MILLISECONDS) + .distinctUntilChanged() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::executeSearch); diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java index 6fc3b7a..a499c8d 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java @@ -20,7 +20,7 @@ public class RxSearchObservable { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { - subject.onComplete(); + subject.onNext(s); return false; } From b02ec917ed74a4b7220820472f16a129637e98e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mieszko=20Wrzeszczy=C5=84ski?= Date: Tue, 20 Nov 2018 22:29:07 +0100 Subject: [PATCH 7/7] Add markers filter --- .../findmytutor/activity/BaseActivity.java | 51 +++---- .../wmi/findmytutor/activity/MapActivity.java | 134 +++++++++--------- .../com/uam/wmi/findmytutor/model/User.java | 9 ++ .../com/uam/wmi/findmytutor/utils/Consts.java | 4 +- .../wmi/findmytutor/utils/MapboxMarker.java | 23 +++ .../findmytutor/utils/RxSearchObservable.java | 3 + app/src/main/res/drawable/custom_marker.png | Bin 923 -> 444 bytes app/src/main/res/drawable/red_marker.png | Bin 3163 -> 1361 bytes 8 files changed, 127 insertions(+), 97 deletions(-) create mode 100644 app/src/main/java/com/uam/wmi/findmytutor/utils/MapboxMarker.java diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java index 5e2dd6d..238526f 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/BaseActivity.java @@ -20,7 +20,6 @@ import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; -import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -38,9 +37,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; -import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import static com.uam.wmi.findmytutor.utils.PrefUtils.storeBackgroundLocationStatus; @@ -50,21 +47,17 @@ public abstract class BaseActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener { - String tag = getClass().getName(); - protected static final int REQUEST_PERMISSIONS = 100; private final static int REQUEST_CODE_ASK_PERMISSIONS = 1; - - private static final String[] REQUIRED_SDK_PERMISSIONS = new String[] { - Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS }; - + private static final String[] REQUIRED_SDK_PERMISSIONS = new String[]{ + Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS}; public DrawerLayout drawerLayout; protected BottomNavigationView navigationView; protected NavigationView drawerNavigationView; protected DrawerLayout sideDrawer; protected Toolbar toolbar; protected boolean isTutor; - + String tag = getClass().getName(); private ActionBarDrawerToggle actionBarDrawerToggle; private SharingFragment sharingFragment; @@ -84,13 +77,13 @@ public abstract class BaseActivity item -> { String itemName = (String) item.getTitle(); Intent launchIntent; - if (itemName.equals(getResources().getString(R.string.navigation_item_whitelist))) { + if (itemName.equals(getResources().getString(R.string.navigation_item_whitelist))) { /* launchIntent = new Intent(getApplicationContext(), WhitelistActivity.class); startActivity(launchIntent);*/ } else if (itemName.equals(getResources().getString(R.string.navigation_item_blacklist))) { /* launchIntent = new Intent(getApplicationContext(), BlacklistActivity.class); startActivity(launchIntent);*/ - } else if (itemName.equals(getResources().getString(R.string.navigation_item_profile))) { + } else if (itemName.equals(getResources().getString(R.string.navigation_item_profile))) { /* launchIntent = new Intent(getApplicationContext(), ProfileActivity.class); startActivity(launchIntent);*/ } else if (itemName.equals(getResources().getString(R.string.navigation_item_settings))) { @@ -98,12 +91,12 @@ public abstract class BaseActivity startActivity(launchIntent); } else if (itemName.equals(getResources().getString(R.string.navigation_item_logout))) { - if(PrefUtils.isBackgroundLocationServiceRunning(getApplicationContext())) { + if (PrefUtils.isBackgroundLocationServiceRunning(getApplicationContext())) { stopBackgroundLocalizationTask(); } - storeBackgroundLocationStatus(getApplication(),false); - PrefUtils.storeIsLoggedIn(getApplicationContext(),false); + storeBackgroundLocationStatus(getApplication(), false); + PrefUtils.storeIsLoggedIn(getApplicationContext(), false); Intent i = getBaseContext().getPackageManager() .getLaunchIntentForPackage(getBaseContext().getPackageName()); @@ -130,14 +123,8 @@ public abstract class BaseActivity if (!isTutor) { navigationView.findViewById(R.id.nav_profile).setVisibility(View.GONE); } - - - - - } - protected void checkPermissions() { final List missingPermissions = new ArrayList(); @@ -201,9 +188,9 @@ public abstract class BaseActivity Boolean shouldServiceRun = PrefUtils.isEnableSharingLocalization(getApplicationContext()) && !PrefUtils.isBackgroundLocationServiceRunning(getApplicationContext()); - if (shouldServiceRun){ + if (shouldServiceRun) { startBackgroundLocalizationTask(); - } else if(PrefUtils.isBackgroundLocationServiceRunning(getApplicationContext()) && + } else if (PrefUtils.isBackgroundLocationServiceRunning(getApplicationContext()) && !PrefUtils.isEnableSharingLocalization(getApplicationContext())) { stopBackgroundLocalizationTask(); } @@ -263,8 +250,16 @@ public abstract class BaseActivity MenuItem myActionMenuItem = menu.findItem(R.id.action_search); searchView = (SearchView) myActionMenuItem.getActionView(); + searchView.setOnQueryTextFocusChangeListener((v, hasFocus) -> { + if (!hasFocus && activeFragment.equals(ActiveFragment.NONE)) { + restoreMapMarkers(); + } + }); + + RxSearchObservable.fromView(searchView) .map(String::toLowerCase) + .filter(t -> !t.trim().isEmpty()) .debounce(300, TimeUnit.MILLISECONDS) .distinctUntilChanged() .subscribeOn(Schedulers.io()) @@ -277,9 +272,16 @@ public abstract class BaseActivity private void executeSearch(String input) { if (activeFragment.equals(ActiveFragment.USER_LIST)) { ((UsersListFragment) userListFragment).searchUser(input); + } else if (activeFragment.equals(ActiveFragment.NONE)) { + searchUser(input); } } + public void searchUser(String textToSearch) { + } + + public void restoreMapMarkers() { + } @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -289,7 +291,6 @@ public abstract class BaseActivity return super.onOptionsItemSelected(item); } - @Override protected void onStart() { super.onStart(); @@ -311,13 +312,13 @@ public abstract class BaseActivity @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { - navigationView.postDelayed(() -> { int itemId = item.getItemId(); if (itemId == R.id.nav_map) { removeFragment(sharingFragment); removeFragment(userListFragment); + activeFragment = ActiveFragment.NONE; } else if (itemId == R.id.nav_profile) { loadUserSettingsFragment(); } else if (itemId == R.id.nav_user_list) { diff --git a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java index d9b67a6..a2a4a08 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/activity/MapActivity.java @@ -6,7 +6,6 @@ import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; -import android.graphics.Color; import android.location.Location; import android.os.Bundle; import android.os.Handler; @@ -40,9 +39,6 @@ import com.mapbox.mapboxsdk.location.modes.RenderMode; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.mapboxsdk.style.layers.CircleLayer; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.sources.VectorSource; import com.uam.wmi.findmytutor.R; import com.uam.wmi.findmytutor.model.Coordinate; import com.uam.wmi.findmytutor.model.User; @@ -51,32 +47,20 @@ import com.uam.wmi.findmytutor.service.CoordinateService; import com.uam.wmi.findmytutor.service.UserService; import com.uam.wmi.findmytutor.utils.PrefUtils; import com.uam.wmi.findmytutor.utils.RestApiHelper; -import com.uam.wmi.findmytutor.utils.SharingLevel; import com.uam.wmi.findmytutor.utils.mapUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Set; -import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.annotations.NonNull; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.functions.Predicate; import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.schedulers.Schedulers; import okhttp3.ResponseBody; import timber.log.Timber; -import java.util.Set; - -import static com.mapbox.mapboxsdk.style.layers.Property.NONE; -import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; -import static java.util.stream.Collectors.toList; public class MapActivity extends BaseActivity @@ -105,6 +89,7 @@ public class MapActivity extends BaseActivity private int bearingParam = 180; private int tiltParam = 30; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -137,7 +122,6 @@ public class MapActivity extends BaseActivity handleBackgroundTaskLifeCycle(); } - @Override public void onMapReady(MapboxMap mapboxMap) { MapActivity.this.mapboxMap = mapboxMap; @@ -185,19 +169,17 @@ public class MapActivity extends BaseActivity alertDialog.show(); } - private void handleError(Throwable error) { showError(error); } - private void showError(Throwable e) { String message; if (e instanceof HttpException) { ResponseBody responseBody = ((HttpException) e).response().errorBody(); message = RestApiHelper.getErrorMessage(responseBody); - }else { + } else { message = "Network Error!"; } @@ -312,37 +294,6 @@ public class MapActivity extends BaseActivity } - private void addStaticLayer() { - // Toggle layer button - final FloatingActionButton button = findViewById(R.id.toggleMarkerLayerButton); - button.setVisibility(View.VISIBLE); - - button.setOnClickListener(view -> { - - Layer layer = mapboxMap.getLayer("museums"); - if (layer != null) { - if (VISIBLE.equals(layer.getVisibility().getValue())) { - layer.setProperties(visibility(NONE)); - } else { - layer.setProperties(visibility(VISIBLE)); - } - } - }); - - // TODO here we create static layer, we are able to hide/show (but we cannot put markers inthere) - VectorSource museums = new VectorSource("museums_source", "mapbox://mapbox.2opop9hr"); - mapboxMap.addSource(museums); - - CircleLayer museumsLayer = new CircleLayer("museums", "museums_source"); - museumsLayer.setSourceLayer("museum-cusco"); - museumsLayer.setProperties( - visibility(VISIBLE), - circleRadius(8f), - circleColor(Color.argb(255, 55, 148, 179)) - ); - mapboxMap.addLayer(museumsLayer); - } - private void fetchTopCoords() { disposable.add( @@ -354,44 +305,37 @@ public class MapActivity extends BaseActivity @Override public void onSuccess(List coordsList) { - if(coordsList.isEmpty()) { - Log.e(tag, "200 empty []"); + if (coordsList.isEmpty() && droppedMarker == null) { + Timber.e("200 empty []"); mapboxMap.clear(); return; } - ArrayList tmp = new ArrayList<>(); + ArrayList tmp = new ArrayList<>(); for (Coordinate coordinate : coordsList) { - Log.e(tag, coordinate.toString()); - if (coordinate.getDisplayMode().equals(SharingLevel.PRESENCE.toString())) { - Log.e(tag, "presence user omitted"); - } else { - tmp.add(coordinate.getUserId()); - } + tmp.add(coordinate.getUserId()); } - Set currentCoordsIds = new HashSet<>(tmp); - if (previousCoordsIds.isEmpty()){ + if (previousCoordsIds.isEmpty()) { previousCoordsIds.addAll(currentCoordsIds); } else { // here we clear + it returns bool if sth was removed if (previousCoordsIds.removeAll(currentCoordsIds)) { - for (String toRemoveId: previousCoordsIds) { - Log.e(tag+ "delete: " , "removing: " + toRemoveId + ": " + markerHash.get(toRemoveId)); + for (String toRemoveId : previousCoordsIds) { + Log.e(tag + "delete: ", "removing: " + toRemoveId + ": " + markerHash.get(toRemoveId)); mapboxMap.removeMarker(markerHash.get(toRemoveId)); markerHash.remove(toRemoveId); coordsMap.remove(toRemoveId); } } else { // TODO double check when some markers api will change - Log.e(tag+ "delete: ","nothing to remove"); + Log.e(tag + "delete: ", "nothing to remove"); } } - for (Coordinate element : coordsList) { String id = element.getUserId(); String newLabel = element.getLabel(); @@ -430,13 +374,16 @@ public class MapActivity extends BaseActivity } } else { - Log.e(tag, "Marker Added: " + id); coordsMap.put(id, element); - Marker marker = mapboxMap.addMarker(new MarkerOptions() + + MarkerOptions markerOptions = new MarkerOptions() .title(id) - .position(new LatLng(element.getLatitude(), element.getLongitude()))); + .position(new LatLng(element.getLatitude(), element.getLongitude())); + + Marker marker = mapboxMap.addMarker(markerOptions); + markerHash.put(id, marker); } @@ -603,4 +550,51 @@ public class MapActivity extends BaseActivity // finish(); // } } + + @Override + public void searchUser(String textToSearch) { + getUserFromApi(textToSearch); + } + + private void getUserFromApi(String userNameToSearch) { + disposable.add( + userService.apiUsersGet() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map(tutors -> Stream.of(tutors).filter(t -> + t.toSearchAbleUserName().toLowerCase().contains(userNameToSearch.toLowerCase())).toList()) + .subscribeWith(new DisposableSingleObserver>() { + @Override + public void onSuccess(List users) { + filterMarkers(users); + } + + @Override + public void onError(Throwable e) { + showError(e); + } + })); + } + + private void filterMarkers(List users) { + restoreMapMarkers(); + + Icon icon1 = IconFactory.getInstance(MapActivity.this).fromResource(R.drawable.custom_marker); + + List markersToSet = Stream.of(mapboxMap.getMarkers()) + .filter(m -> Stream.of(users).anyMatch(u -> u.getId().equals(m.getTitle()))) + .toList(); + + for (Marker marker : markersToSet) { + marker.setIcon(icon1); + } + } + + public void restoreMapMarkers() { + Icon icon = IconFactory.getInstance(MapActivity.this).fromResource(R.drawable.red_marker); + + for (Marker marker : mapboxMap.getMarkers()) { + marker.setIcon(icon); + } + } } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/model/User.java b/app/src/main/java/com/uam/wmi/findmytutor/model/User.java index 0e5c0d5..7788f61 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/model/User.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/model/User.java @@ -712,6 +712,15 @@ public class User extends BaseResponse { return sb.toString(); } + public String toSearchAbleUserName(){ + StringBuilder sb = new StringBuilder(); + sb.append(getFirstName()); + sb.append(getLastName()); + + return sb.toString(); + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java index eef58b5..bedd885 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/Consts.java @@ -1,7 +1,7 @@ package com.uam.wmi.findmytutor.utils; public class Consts { - public final static Double presenceLatitude = 52.467491; - public final static Double presencelongitude = 16.926782; + public final static Double presenceLatitude = 65.600244; + public final static Double presencelongitude = 480.032153; public final static String presenceApproximatedName = "unknown"; } diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/MapboxMarker.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/MapboxMarker.java new file mode 100644 index 0000000..371b819 --- /dev/null +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/MapboxMarker.java @@ -0,0 +1,23 @@ +package com.uam.wmi.findmytutor.utils; + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.annotations.InfoWindow; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.maps.MapView; + +public class MapboxMarker extends Marker { + public MapboxMarker( MarkerOptions markerOptions ){ + super(markerOptions); + } +/* + @NonNull + + private InfoWindow showInfoWindow(InfoWindow iw, MapView mapView) { + iw.open(mapView, this, this.getPosition(), 0, 0); + this.infoWindowShown = true; + return iw; + }*/ + +} diff --git a/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java index a499c8d..33d4aee 100644 --- a/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java +++ b/app/src/main/java/com/uam/wmi/findmytutor/utils/RxSearchObservable.java @@ -29,8 +29,11 @@ public class RxSearchObservable { subject.onNext(text); return false; } + + }); + return subject; } } diff --git a/app/src/main/res/drawable/custom_marker.png b/app/src/main/res/drawable/custom_marker.png index 1df09fdf3fe1229c3801c3b14fe6446cadced80c..8b1f284cbc938ccdaee7a32ab68b2aa891a3bc79 100644 GIT binary patch delta 432 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 mA}|2R0$2r*XBfuP`2K(N@0CRsu