diff --git a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java
index bee9fa2b19..ede3b6f8cf 100644
--- a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java
+++ b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java
@@ -1822,6 +1822,25 @@ public class GPXUtilities {
}
serializer.endTag(null, "metadata");
+ for (WptPt l : file.points) {
+ serializer.startTag(null, "wpt"); //$NON-NLS-1$
+ writeWpt(format, serializer, l);
+ serializer.endTag(null, "wpt"); //$NON-NLS-1$
+ }
+
+ for (Route track : file.routes) {
+ serializer.startTag(null, "rte"); //$NON-NLS-1$
+ writeNotNullText(serializer, "name", track.name);
+ writeNotNullText(serializer, "desc", track.desc);
+
+ for (WptPt p : track.points) {
+ serializer.startTag(null, "rtept"); //$NON-NLS-1$
+ writeWpt(format, serializer, p);
+ serializer.endTag(null, "rtept"); //$NON-NLS-1$
+ }
+ writeExtensions(serializer, track);
+ serializer.endTag(null, "rte"); //$NON-NLS-1$
+ }
for (Track track : file.tracks) {
if (!track.generalTrack) {
@@ -1844,26 +1863,6 @@ public class GPXUtilities {
}
}
- for (Route track : file.routes) {
- serializer.startTag(null, "rte"); //$NON-NLS-1$
- writeNotNullText(serializer, "name", track.name);
- writeNotNullText(serializer, "desc", track.desc);
-
- for (WptPt p : track.points) {
- serializer.startTag(null, "rtept"); //$NON-NLS-1$
- writeWpt(format, serializer, p);
- serializer.endTag(null, "rtept"); //$NON-NLS-1$
- }
- writeExtensions(serializer, track);
- serializer.endTag(null, "rte"); //$NON-NLS-1$
- }
-
- for (WptPt l : file.points) {
- serializer.startTag(null, "wpt"); //$NON-NLS-1$
- writeWpt(format, serializer, l);
- serializer.endTag(null, "wpt"); //$NON-NLS-1$
- }
-
writeExtensions(serializer, file);
serializer.endTag(null, "gpx"); //$NON-NLS-1$
diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java
index 2f607aef3a..d3c151c12a 100644
--- a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java
+++ b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java
@@ -20,7 +20,7 @@ public abstract class StringBundleWriter {
public void writeBundle() {
for (Entry> entry : bundle.getMap().entrySet()) {
- writeItem(entry.getKey(), entry.getValue());
+ writeItem("osmand:" + entry.getKey(), entry.getValue());
}
}
}
diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle
index 7da4480bd7..9f909d50c8 100644
--- a/OsmAnd/build.gradle
+++ b/OsmAnd/build.gradle
@@ -108,18 +108,25 @@ android {
manifest.srcFile "AndroidManifest-debug.xml"
}
full {
- java.srcDirs = ["src-google"]
+ java.srcDirs = ["src-nogms", "src-google"]
+ }
+ fullGms {
+ java.srcDirs = ["src-gms", "src-google"]
}
free {
- java.srcDirs = ["src-google"]
+ java.srcDirs = ["src-nogms", "src-google"]
+ manifest.srcFile "AndroidManifest-free.xml"
+ }
+ freeGms {
+ java.srcDirs = ["src-gms", "src-google"]
manifest.srcFile "AndroidManifest-free.xml"
}
freedev {
- java.srcDirs = ["src-google"]
+ java.srcDirs = ["src-nogms", "src-google"]
manifest.srcFile "AndroidManifest-freedev.xml"
}
freehuawei {
- java.srcDirs = ["src-huawei"]
+ java.srcDirs = ["src-nogms", "src-google"]
manifest.srcFile "AndroidManifest-freehuawei.xml"
}
@@ -172,10 +179,18 @@ android {
dimension "version"
applicationId "net.osmand"
}
+ freeGms {
+ dimension "version"
+ applicationId "net.osmand"
+ }
full {
dimension "version"
applicationId "net.osmand.plus"
}
+ fullGms {
+ dimension "version"
+ applicationId "net.osmand.plus"
+ }
freehuawei {
dimension "version"
applicationId "net.osmand.huawei"
@@ -507,8 +522,10 @@ dependencies {
exclude group: "com.fasterxml.jackson.core"
}
implementation 'com.jaredrummler:colorpicker:1.1.0'
+ implementation "org.bouncycastle:bcpkix-jdk15on:1.56"
freehuaweiImplementation 'com.huawei.hms:iap:5.0.2.300'
- implementation "org.bouncycastle:bcpkix-jdk15on:1.56"
+ freeGmsImplementation 'com.google.android.gms:play-services-location:17.1.0'
+ fullGmsImplementation 'com.google.android.gms:play-services-location:17.1.0'
}
diff --git a/OsmAnd/build.gradle.lib b/OsmAnd/build.gradle.lib
index 6b0f4da990..f4f9c019ed 100644
--- a/OsmAnd/build.gradle.lib
+++ b/OsmAnd/build.gradle.lib
@@ -38,7 +38,7 @@ android {
jni.srcDirs = []
jniLibs.srcDirs = ["libs"]
aidl.srcDirs = ["src"]
- java.srcDirs = ["src", "src-google"]
+ java.srcDirs = ["src", "src-gms", "src-google"]
resources.srcDirs = ["src"]
renderscript.srcDirs = ["src"]
res.srcDirs = ["res"]
@@ -409,6 +409,7 @@ dependencies {
exclude group: "com.fasterxml.jackson.core"
}
implementation 'com.jaredrummler:colorpicker:1.1.0'
-
implementation 'org.bouncycastle:bcpkix-jdk15on:1.56'
+
+ implementation 'com.google.android.gms:play-services-location:17.1.0'
}
diff --git a/OsmAnd/res/layout/bottom_sheet_item_with_descr_64dp.xml b/OsmAnd/res/layout/bottom_sheet_item_with_descr_64dp.xml
index 2ff6a1bf58..95091e1086 100644
--- a/OsmAnd/res/layout/bottom_sheet_item_with_descr_64dp.xml
+++ b/OsmAnd/res/layout/bottom_sheet_item_with_descr_64dp.xml
@@ -27,6 +27,8 @@
android:layout_weight="1"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginStart="@dimen/content_padding"
+ android:paddingTop="@dimen/content_padding_small"
+ android:paddingBottom="@dimen/content_padding_small"
android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OsmAnd/res/layout/online_routing_preference_segment.xml b/OsmAnd/res/layout/online_routing_preference_segment.xml
index 5f095e126c..437ba875e5 100644
--- a/OsmAnd/res/layout/online_routing_preference_segment.xml
+++ b/OsmAnd/res/layout/online_routing_preference_segment.xml
@@ -129,6 +129,19 @@
tools:visibility="visible"
android:visibility="gone" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml
index bd3397afc2..4a9fde833f 100644
--- a/OsmAnd/res/values-ar/strings.xml
+++ b/OsmAnd/res/values-ar/strings.xml
@@ -4023,7 +4023,6 @@
قدم
دراجة هوائية
سيارة
- خطأ، أعد التحقق من المعايير
نسخ العنوان
محرك التوجيه عبر الإنترنت
محركات التوجيه عبر الإنترنت
diff --git a/OsmAnd/res/values-cs/strings.xml b/OsmAnd/res/values-cs/strings.xml
index 38af3fcceb..6d6b906b06 100644
--- a/OsmAnd/res/values-cs/strings.xml
+++ b/OsmAnd/res/values-cs/strings.xml
@@ -3960,7 +3960,6 @@
Chůze
Kolo
Auto
- Chyba, zkontrolujte parametry
Kopírovat adresu
Online navigační služba
Online navigační služby
diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml
index e66df6bcd7..b216a47c7c 100644
--- a/OsmAnd/res/values-de/strings.xml
+++ b/OsmAnd/res/values-de/strings.xml
@@ -3962,7 +3962,6 @@
Zu Fuß
Fahrrad
Auto
- Fehler, Parameter erneut prüfen
Subtyp
Leer lassen, wenn kein API-Schlüssel vorhanden
Adresse kopieren
diff --git a/OsmAnd/res/values-eo/strings.xml b/OsmAnd/res/values-eo/strings.xml
index 391e96ba42..9671d14e5f 100644
--- a/OsmAnd/res/values-eo/strings.xml
+++ b/OsmAnd/res/values-eo/strings.xml
@@ -3960,7 +3960,6 @@
Piediranto
Biciklo
Aŭtomobilo
- Eraro, rekontrolu parametrojn
Kopii adreson
Enreta navigilo
Enretaj navigiloj
diff --git a/OsmAnd/res/values-es-rAR/strings.xml b/OsmAnd/res/values-es-rAR/strings.xml
index fd54cd500f..83700ce7cd 100644
--- a/OsmAnd/res/values-es-rAR/strings.xml
+++ b/OsmAnd/res/values-es-rAR/strings.xml
@@ -3963,7 +3963,6 @@
Peatón
Bicicleta
Automóvil
- Error, vuelve a comprobar los parámetros
Copiar dirección
Motor de navegación en línea
Motores de navegación en línea
diff --git a/OsmAnd/res/values-eu/strings.xml b/OsmAnd/res/values-eu/strings.xml
index ee2a825985..dced5cf049 100644
--- a/OsmAnd/res/values-eu/strings.xml
+++ b/OsmAnd/res/values-eu/strings.xml
@@ -3933,6 +3933,5 @@ Area honi dagokio: %1$s x %2$s
Oinez
Bizikleta
Kotxea
- Errorea, egiaztatu parametroak berriro
Kopiatu helbidea
\ No newline at end of file
diff --git a/OsmAnd/res/values-fa/strings.xml b/OsmAnd/res/values-fa/strings.xml
index 3cb0760eac..226a00cc5c 100644
--- a/OsmAnd/res/values-fa/strings.xml
+++ b/OsmAnd/res/values-fa/strings.xml
@@ -3953,5 +3953,4 @@
پا
دوچرخه
خودرو
- خطا، پارامترها را بازبینی کنید
\ No newline at end of file
diff --git a/OsmAnd/res/values-fr/strings.xml b/OsmAnd/res/values-fr/strings.xml
index 0b7dc82b1c..e5f41539c2 100644
--- a/OsmAnd/res/values-fr/strings.xml
+++ b/OsmAnd/res/values-fr/strings.xml
@@ -3947,7 +3947,6 @@
A pieds
Vélo
Automobile
- Erreur, vérifiez les paramètres
Ajouter un moteur de routage en ligne
Modifier le moteur de routage en ligne
L\'URL avec tous les paramètres sera de la forme :
diff --git a/OsmAnd/res/values-gl/strings.xml b/OsmAnd/res/values-gl/strings.xml
index 2e0c0053d3..5570577760 100644
--- a/OsmAnd/res/values-gl/strings.xml
+++ b/OsmAnd/res/values-gl/strings.xml
@@ -3968,7 +3968,6 @@ Lon %2$s
Pé
Bicicleta
Coche
- Erro, verifica novamente os parámetros
Copiar enderezo
Horarios dos avisos por voz
Motor de navegación en liña
diff --git a/OsmAnd/res/values-hu/strings.xml b/OsmAnd/res/values-hu/strings.xml
index d893dcf47e..64d40e287c 100644
--- a/OsmAnd/res/values-hu/strings.xml
+++ b/OsmAnd/res/values-hu/strings.xml
@@ -3954,7 +3954,6 @@
Hagyja üresen, ha nem
Az összes paraméterrel rendelkező URL így néz ki:
Útvonaltervezés kipróbálása
- Hiba, ellenőrizze újra a paramétereket
Cím másolása
Online útvonaltervező
Online útvonaltervezők
diff --git a/OsmAnd/res/values-is/strings.xml b/OsmAnd/res/values-is/strings.xml
index 3449458457..0b94e361f7 100644
--- a/OsmAnd/res/values-is/strings.xml
+++ b/OsmAnd/res/values-is/strings.xml
@@ -3965,6 +3965,5 @@
Gangandi
Hjólandi
Bíll
- Villa, yfirfarðu breytur
Afrita heimilisfang
\ No newline at end of file
diff --git a/OsmAnd/res/values-iw/strings.xml b/OsmAnd/res/values-iw/strings.xml
index 44f2c8c034..7ca3bcdb8d 100644
--- a/OsmAnd/res/values-iw/strings.xml
+++ b/OsmAnd/res/values-iw/strings.xml
@@ -3963,7 +3963,6 @@
ברגל
אופנוע
מכונית
- שגיאה, נא לבדוק את המשתנים מחדש
העתקת כתובת
מנוע ניווט מקוון
מנועי ניווט מקוונים
diff --git a/OsmAnd/res/values-ka/strings.xml b/OsmAnd/res/values-ka/strings.xml
index 770916e3b3..330d43d2c4 100644
--- a/OsmAnd/res/values-ka/strings.xml
+++ b/OsmAnd/res/values-ka/strings.xml
@@ -2449,7 +2449,6 @@
OsmAndის გამოწერა დაპაუზებულია
OsmAndის გამოწერა შეჩერებულია
URL ყველა პარამეტრით აი ასეთი იქნება:
- შეცდომა, გადაამოწმეთ პარამეტრები
Mapillary-ის გამოსახულება
Mapillary-ის ღილაკი
ფერთა გამა
diff --git a/OsmAnd/res/values-lt/strings.xml b/OsmAnd/res/values-lt/strings.xml
index 65c711462f..09ee2a83fd 100644
--- a/OsmAnd/res/values-lt/strings.xml
+++ b/OsmAnd/res/values-lt/strings.xml
@@ -2770,7 +2770,6 @@ Tai yra puikus būdas paremti OsmAnd ir OSM, jei jie jums patinka.
Patikrinti maršruto apskaičiavimą
Dviratis
Automobilis
- Įvyko klaida, patikrinkite paametrus
Kopijuoti adresą
Folderiai
Pasirinkite folderį
diff --git a/OsmAnd/res/values-nl/strings.xml b/OsmAnd/res/values-nl/strings.xml
index 04da6132ce..4420cd6987 100644
--- a/OsmAnd/res/values-nl/strings.xml
+++ b/OsmAnd/res/values-nl/strings.xml
@@ -3916,7 +3916,6 @@
Te voet
Fiets
Auto
- Fout, controleer parameters opnieuw
Kopieer adres
Online routeplanningssysteem
Online routeplanningssystemen
diff --git a/OsmAnd/res/values-pl/strings.xml b/OsmAnd/res/values-pl/strings.xml
index cf4852f488..840fa52189 100644
--- a/OsmAnd/res/values-pl/strings.xml
+++ b/OsmAnd/res/values-pl/strings.xml
@@ -3956,7 +3956,6 @@
Obliczanie trasy testowej
Rower
Samochód
- Błąd, ponownie sprawdź parametry
Skopiuj adres
Jazda
Stopa
diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml
index da70b09ce8..52d578c035 100644
--- a/OsmAnd/res/values-pt-rBR/strings.xml
+++ b/OsmAnd/res/values-pt-rBR/strings.xml
@@ -3955,7 +3955,6 @@
Pé
Bicicleta
Carro
- Erro, verifique novamente os parâmetros
Copiar endereço
Motor de encaminhamento online
Mecanismos de roteamento online
diff --git a/OsmAnd/res/values-sc/strings.xml b/OsmAnd/res/values-sc/strings.xml
index f5ff13cd8b..aa05e82d25 100644
--- a/OsmAnd/res/values-sc/strings.xml
+++ b/OsmAnd/res/values-sc/strings.xml
@@ -3957,7 +3957,6 @@
A pee
Bitzicleta
Màchina
- Errore, torra a verificare sos paràmetros
Còpia s\'indiritzu
Motore de càrculu in lìnia
Motores de càrculu in lìnia
diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml
index 6d8fb45d93..81a9d73353 100644
--- a/OsmAnd/res/values-sk/strings.xml
+++ b/OsmAnd/res/values-sk/strings.xml
@@ -3955,7 +3955,6 @@
Chôdza
Bicykel
Auto
- Chyba, skontrolujte parametre
Kopírovať adresu
Online navigačná služba
Online navigačné služby
diff --git a/OsmAnd/res/values-tr/strings.xml b/OsmAnd/res/values-tr/strings.xml
index ee5e25124a..344183c102 100644
--- a/OsmAnd/res/values-tr/strings.xml
+++ b/OsmAnd/res/values-tr/strings.xml
@@ -3961,7 +3961,6 @@
Yürüme
Bisiklet
Araba
- Hata, parametreleri tekrar gözden geçirin
Adresi kopyala
Çevrim içi yönlendirme motoru
Çevrim içi yönlendirme motorları
diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml
index 2738eda283..6910c9a902 100644
--- a/OsmAnd/res/values-uk/strings.xml
+++ b/OsmAnd/res/values-uk/strings.xml
@@ -3959,7 +3959,6 @@
Пішки
Велосипед
Автомобіль
- Помилка, повторно перевірте параметри
Копіювати адресу
Мережний рушій маршрутизації
Мережні рушії маршрутизації
diff --git a/OsmAnd/res/values-zh-rTW/strings.xml b/OsmAnd/res/values-zh-rTW/strings.xml
index 738ede85e2..0f19668c01 100644
--- a/OsmAnd/res/values-zh-rTW/strings.xml
+++ b/OsmAnd/res/values-zh-rTW/strings.xml
@@ -3953,7 +3953,6 @@
步行
自行車
汽車
- 錯誤,重新檢查參數
複製地址
線上路線計算引擎
線上路線計算引擎
diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml
index bbb71f001a..8605ea2189 100644
--- a/OsmAnd/res/values/strings.xml
+++ b/OsmAnd/res/values/strings.xml
@@ -12,6 +12,23 @@
-->
+ The name is already exists
+ Server error: %1$s
+ MTB
+ Racing bike
+ Scooter
+ Truck
+ Small truck
+ HGV
+ Regular cycling
+ Road cycling
+ Mountain cycling
+ Electric cycling
+ Walking
+ Hiking
+ Wheelchair
+ Show track on map
+ Start recording
Announcement time
Announcement time of different voice prompts depends on prompt type, current navigation speed and default navigation speed.
Time and distance intervals
@@ -35,7 +52,6 @@
Online routing engines
Online routing engine
Copy address
- Error, recheck parameters
Car
Bike
Foot
diff --git a/OsmAnd/src-gms/net/osmand/plus/LocationServiceHelperImpl.java b/OsmAnd/src-gms/net/osmand/plus/LocationServiceHelperImpl.java
new file mode 100644
index 0000000000..e8182955ec
--- /dev/null
+++ b/OsmAnd/src-gms/net/osmand/plus/LocationServiceHelperImpl.java
@@ -0,0 +1,158 @@
+package net.osmand.plus;
+
+import android.location.Location;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.gms.location.FusedLocationProviderClient;
+import com.google.android.gms.location.LocationAvailability;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationResult;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.android.gms.tasks.Task;
+import com.google.android.gms.tasks.Tasks;
+
+import net.osmand.PlatformUtil;
+import net.osmand.plus.helpers.DayNightHelper;
+import net.osmand.plus.helpers.LocationServiceHelper;
+
+import org.apache.commons.logging.Log;
+
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class LocationServiceHelperImpl extends LocationServiceHelper {
+
+ private static final Log LOG = PlatformUtil.getLog(DayNightHelper.class);
+
+ private final OsmandApplication app;
+
+ // FusedLocationProviderClient - Main class for receiving location updates.
+ private final FusedLocationProviderClient fusedLocationProviderClient;
+
+ // LocationRequest - Requirements for the location updates, i.e., how often you should receive
+ // updates, the priority, etc.
+ private final LocationRequest fusedLocationRequest;
+
+ // LocationCallback - Called when FusedLocationProviderClient has a new Location.
+ private final com.google.android.gms.location.LocationCallback fusedLocationCallback;
+
+ private LocationCallback locationCallback;
+
+ public LocationServiceHelperImpl(@NonNull OsmandApplication app) {
+ this.app = app;
+
+ fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(app);
+
+ fusedLocationRequest = new LocationRequest()
+ // Sets the desired interval for active location updates. This interval is inexact. You
+ // may not receive updates at all if no location sources are available, or you may
+ // receive them less frequently than requested. You may also receive updates more
+ // frequently than requested if other applications are requesting location at a more
+ // frequent interval.
+ //
+ // IMPORTANT NOTE: Apps running on Android 8.0 and higher devices (regardless of
+ // targetSdkVersion) may receive updates less frequently than this interval when the app
+ // is no longer in the foreground.
+ .setInterval(100)
+
+ // Sets the fastest rate for active location updates. This interval is exact, and your
+ // application will never receive updates more frequently than this value.
+ .setFastestInterval(50)
+
+ // Sets the maximum time when batched location updates are delivered. Updates may be
+ // delivered sooner than this interval.
+ .setMaxWaitTime(200)
+
+ .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+
+ fusedLocationCallback = new com.google.android.gms.location.LocationCallback() {
+ @Override
+ public void onLocationResult(LocationResult locationResult) {
+ LocationCallback locationCallback = LocationServiceHelperImpl.this.locationCallback;
+ if (locationCallback != null) {
+ Location location = locationResult != null ? locationResult.getLastLocation() : null;
+ net.osmand.Location l = convertLocation(location);
+ locationCallback.onLocationResult(l == null
+ ? Collections.emptyList() : Collections.singletonList(l));
+ }
+
+ }
+
+ @Override
+ public void onLocationAvailability(LocationAvailability locationAvailability) {
+ LocationCallback locationCallback = LocationServiceHelperImpl.this.locationCallback;
+ if (locationAvailability != null && locationCallback != null) {
+ locationCallback.onLocationAvailability(locationAvailability.isLocationAvailable());
+ }
+ }
+ };
+ }
+
+ @Override
+ public void requestLocationUpdates(@NonNull LocationCallback locationCallback) {
+ this.locationCallback = locationCallback;
+ // request location updates
+ try {
+ fusedLocationProviderClient.requestLocationUpdates(
+ fusedLocationRequest, fusedLocationCallback, Looper.myLooper());
+ } catch (SecurityException e) {
+ LOG.debug("Location service permission not granted");
+ throw e;
+ } catch (IllegalArgumentException e) {
+ LOG.debug("GPS location provider not available");
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean isNetworkLocationUpdatesSupported() {
+ return false;
+ }
+
+ @Override
+ public void requestNetworkLocationUpdates(@NonNull LocationCallback locationCallback) {
+ }
+
+ @Override
+ public void removeLocationUpdates() {
+ // remove location updates
+ try {
+ fusedLocationProviderClient.removeLocationUpdates(fusedLocationCallback);
+ } catch (SecurityException e) {
+ LOG.debug("Location service permission not granted", e);
+ throw e;
+ }
+ }
+
+ @Nullable
+ public net.osmand.Location getFirstTimeRunDefaultLocation() {
+ final net.osmand.Location[] location = {null};
+ /*
+ try {
+ Task lastLocation = fusedLocationProviderClient.getLastLocation();
+ lastLocation.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(Location loc) {
+ location[0] = convertLocation(loc);
+ }
+ });
+ } catch (SecurityException e) {
+ LOG.debug("Location service permission not granted");
+ } catch (IllegalArgumentException e) {
+ LOG.debug("GPS location provider not available");
+ }
+ */
+ return location[0];
+ }
+
+ @Nullable
+ private net.osmand.Location convertLocation(@Nullable Location location) {
+ return location == null ? null : OsmAndLocationProvider.convertLocation(location, app);
+ }
+}
diff --git a/OsmAnd/src-nogms/net/osmand/plus/LocationServiceHelperImpl.java b/OsmAnd/src-nogms/net/osmand/plus/LocationServiceHelperImpl.java
new file mode 100644
index 0000000000..5f49607954
--- /dev/null
+++ b/OsmAnd/src-nogms/net/osmand/plus/LocationServiceHelperImpl.java
@@ -0,0 +1,188 @@
+package net.osmand.plus;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.PlatformUtil;
+import net.osmand.plus.helpers.DayNightHelper;
+import net.osmand.plus.helpers.LocationServiceHelper;
+
+import org.apache.commons.logging.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import static android.content.Context.LOCATION_SERVICE;
+
+public class LocationServiceHelperImpl extends LocationServiceHelper implements LocationListener {
+
+ private static final Log LOG = PlatformUtil.getLog(DayNightHelper.class);
+
+ private final OsmandApplication app;
+
+ private LocationCallback locationCallback;
+ private LocationCallback networkLocationCallback;
+ private final LinkedList networkListeners = new LinkedList<>();
+
+ // Working with location checkListeners
+ private class NetworkListener implements LocationListener {
+
+ @Override
+ public void onLocationChanged(Location location) {
+ LocationCallback locationCallback = LocationServiceHelperImpl.this.networkLocationCallback;
+ if (locationCallback != null) {
+ net.osmand.Location l = convertLocation(location);
+ locationCallback.onLocationResult(l == null
+ ? Collections.emptyList() : Collections.singletonList(l));
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ }
+
+ public LocationServiceHelperImpl(@NonNull OsmandApplication app) {
+ this.app = app;
+ }
+
+ @Override
+ public void requestLocationUpdates(@NonNull LocationCallback locationCallback) {
+ this.locationCallback = locationCallback;
+ // request location updates
+ LocationManager locationManager = (LocationManager) app.getSystemService(LOCATION_SERVICE);
+ try {
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
+ } catch (SecurityException e) {
+ LOG.debug("Location service permission not granted");
+ throw e;
+ } catch (IllegalArgumentException e) {
+ LOG.debug("GPS location provider not available");
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean isNetworkLocationUpdatesSupported() {
+ return true;
+ }
+
+ @Override
+ public void requestNetworkLocationUpdates(@NonNull LocationCallback locationCallback) {
+ this.networkLocationCallback = locationCallback;
+ // request location updates
+ LocationManager locationManager = (LocationManager) app.getSystemService(LOCATION_SERVICE);
+ List providers = locationManager.getProviders(true);
+ for (String provider : providers) {
+ if (provider == null || provider.equals(LocationManager.GPS_PROVIDER)) {
+ continue;
+ }
+ try {
+ NetworkListener networkListener = new NetworkListener();
+ locationManager.requestLocationUpdates(provider, 0, 0, networkListener);
+ networkListeners.add(networkListener);
+ } catch (SecurityException e) {
+ LOG.debug(provider + " location service permission not granted");
+ } catch (IllegalArgumentException e) {
+ LOG.debug(provider + " location provider not available");
+ }
+ }
+ }
+
+ @Override
+ public void removeLocationUpdates() {
+ // remove location updates
+ LocationManager locationManager = (LocationManager) app.getSystemService(LOCATION_SERVICE);
+ try {
+ locationManager.removeUpdates(this);
+ } catch (SecurityException e) {
+ LOG.debug("Location service permission not granted", e);
+ throw e;
+ } finally {
+ while (!networkListeners.isEmpty()) {
+ LocationListener listener = networkListeners.poll();
+ if (listener != null) {
+ locationManager.removeUpdates(listener);
+ }
+ }
+ }
+ }
+
+ @Nullable
+ public net.osmand.Location getFirstTimeRunDefaultLocation() {
+ LocationManager locationManager = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
+ List providers = new ArrayList<>(locationManager.getProviders(true));
+ // note, passive provider is from API_LEVEL 8 but it is a constant, we can check for it.
+ // constant should not be changed in future
+ int passiveFirst = providers.indexOf(LocationManager.PASSIVE_PROVIDER);
+ // put passive provider to first place
+ if (passiveFirst > -1) {
+ providers.add(0, providers.remove(passiveFirst));
+ }
+ // find location
+ for (String provider : providers) {
+ try {
+ net.osmand.Location location = convertLocation(locationManager.getLastKnownLocation(provider));
+ if (location != null) {
+ return location;
+ }
+ } catch (SecurityException e) {
+ // location service permission not granted
+ } catch (IllegalArgumentException e) {
+ // location provider not available
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private net.osmand.Location convertLocation(@Nullable Location location) {
+ return location == null ? null : OsmAndLocationProvider.convertLocation(location, app);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ LocationCallback locationCallback = this.locationCallback;
+ if (locationCallback != null) {
+ net.osmand.Location l = convertLocation(location);
+ locationCallback.onLocationResult(l == null
+ ? Collections.emptyList() : Collections.singletonList(l));
+ }
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ LocationCallback locationCallback = this.locationCallback;
+ if (locationCallback != null) {
+ locationCallback.onLocationAvailability(true);
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ LocationCallback locationCallback = this.locationCallback;
+ if (locationCallback != null) {
+ locationCallback.onLocationAvailability(false);
+ }
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/NavigationService.java b/OsmAnd/src/net/osmand/plus/NavigationService.java
index 3a824f937e..16f1afddcc 100644
--- a/OsmAnd/src/net/osmand/plus/NavigationService.java
+++ b/OsmAnd/src/net/osmand/plus/NavigationService.java
@@ -4,20 +4,24 @@ import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
-import android.location.Location;
-import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+
+import net.osmand.Location;
import net.osmand.PlatformUtil;
+import net.osmand.plus.helpers.LocationServiceHelper;
+import net.osmand.plus.helpers.LocationServiceHelper.LocationCallback;
import net.osmand.plus.notifications.OsmandNotification;
import net.osmand.plus.settings.backend.OsmandSettings;
-public class NavigationService extends Service implements LocationListener {
+import java.util.List;
+
+public class NavigationService extends Service {
public static class NavigationServiceBinder extends Binder {
}
@@ -29,11 +33,11 @@ public class NavigationService extends Service implements LocationListener {
private final NavigationServiceBinder binder = new NavigationServiceBinder();
- private String serviceOffProvider;
private OsmandSettings settings;
protected int usedBy = 0;
private OsmAndLocationProvider locationProvider;
+ private LocationServiceHelper locationServiceHelper;
@Override
public IBinder onBind(Intent intent) {
@@ -72,21 +76,37 @@ public class NavigationService extends Service implements LocationListener {
settings = app.getSettings();
usedBy = intent.getIntExtra(USAGE_INTENT, 0);
- // use only gps provider
- serviceOffProvider = LocationManager.GPS_PROVIDER;
locationProvider = app.getLocationProvider();
+ locationServiceHelper = app.createLocationServiceHelper();
app.setNavigationService(this);
// request location updates
- LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
- locationManager.requestLocationUpdates(serviceOffProvider, 0, 0, NavigationService.this);
+ locationServiceHelper.requestLocationUpdates(new LocationCallback() {
+ @Override
+ public void onLocationResult(@NonNull List locations) {
+ if (!locations.isEmpty()) {
+ Location location = locations.get(locations.size() - 1);
+ if (!settings.MAP_ACTIVITY_ENABLED.get()) {
+ locationProvider.setLocationFromService(location);
+ }
+ }
+ }
+
+ @Override
+ public void onLocationAvailability(boolean locationAvailable) {
+ if (!locationAvailable) {
+ OsmandApplication app = (OsmandApplication) getApplication();
+ if (app != null) {
+ app.showToastMessage(getString(R.string.off_router_service_no_gps_available));
+ }
+ }
+ }
+ });
} catch (SecurityException e) {
Toast.makeText(this, R.string.no_location_permission, Toast.LENGTH_LONG).show();
- Log.d(PlatformUtil.TAG, "Location service permission not granted"); //$NON-NLS-1$
} catch (IllegalArgumentException e) {
Toast.makeText(this, R.string.gps_not_available, Toast.LENGTH_LONG).show();
- Log.d(PlatformUtil.TAG, "GPS location provider not available"); //$NON-NLS-1$
}
// registering icon at top level
@@ -117,11 +137,10 @@ public class NavigationService extends Service implements LocationListener {
app.setNavigationService(null);
usedBy = 0;
// remove updates
- LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
- locationManager.removeUpdates(this);
+ locationServiceHelper.removeLocationUpdates();
} catch (SecurityException e) {
- Log.d(PlatformUtil.TAG, "Location service permission not granted"); //$NON-NLS-1$
+ // Location service permission not granted
}
// remove notification
stopForeground(Boolean.TRUE);
@@ -134,29 +153,6 @@ public class NavigationService extends Service implements LocationListener {
}, 500);
}
- @Override
- public void onLocationChanged(Location l) {
- if (l != null && !settings.MAP_ACTIVITY_ENABLED.get()) {
- net.osmand.Location location = OsmAndLocationProvider.convertLocation(l, (OsmandApplication) getApplication());
- locationProvider.setLocationFromService(location);
- }
- }
-
- @Override
- public void onProviderDisabled(String provider) {
- Toast.makeText(this, getString(R.string.off_router_service_no_gps_available), Toast.LENGTH_LONG).show();
- }
-
-
- @Override
- public void onProviderEnabled(String provider) {
- }
-
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
-
@Override
public void onTaskRemoved(Intent rootIntent) {
OsmandApplication app = ((OsmandApplication) getApplication());
diff --git a/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java b/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java
index a19ebd3023..ee875fbb17 100644
--- a/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java
+++ b/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java
@@ -1,6 +1,7 @@
package net.osmand.plus;
import android.Manifest;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
@@ -16,7 +17,6 @@ import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.GpsStatus.Listener;
import android.location.Location;
-import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Build.VERSION;
@@ -25,6 +25,7 @@ import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
@@ -38,8 +39,9 @@ import net.osmand.binary.RouteDataObject;
import net.osmand.data.LatLon;
import net.osmand.data.QuadPoint;
import net.osmand.plus.TargetPointsHelper.TargetPoint;
-import net.osmand.plus.routing.RoutingHelper;
+import net.osmand.plus.helpers.LocationServiceHelper;
import net.osmand.plus.routing.RouteSegmentSearchResult;
+import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.router.RouteSegmentResult;
@@ -47,8 +49,6 @@ import net.osmand.util.MapUtils;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -75,8 +75,6 @@ public class OsmAndLocationProvider implements SensorEventListener {
private static final float ACCURACY_FOR_GPX_AND_ROUTING = 50;
- private static final int GPS_TIMEOUT_REQUEST = 0;
- private static final int GPS_DIST_REQUEST = 0;
private static final int NOT_SWITCH_TO_NETWORK_WHEN_GPS_LOST_MS = 12000;
private static final long LOCATION_TIMEOUT_TO_BE_STALE = 1000 * 60 * 2; // 2 minutes
@@ -85,9 +83,8 @@ public class OsmAndLocationProvider implements SensorEventListener {
private static final long AGPS_TO_REDOWNLOAD = 16 * 60 * 60 * 1000; // 16 hours
private static final int REQUESTS_BEFORE_CHECK_LOCATION = 100;
- private AtomicInteger locationRequestsCounter = new AtomicInteger();
- private AtomicInteger staleLocationRequestsCounter = new AtomicInteger();
-
+ private final AtomicInteger locationRequestsCounter = new AtomicInteger();
+ private final AtomicInteger staleLocationRequestsCounter = new AtomicInteger();
private long lastTimeGPSLocationFixed = 0;
@@ -121,23 +118,23 @@ public class OsmAndLocationProvider implements SensorEventListener {
// Current screen orientation
private int currentScreenOrientation;
- private OsmandApplication app;
+ private final OsmandApplication app;
- private NavigationInfo navigationInfo;
- private CurrentPositionHelper currentPositionHelper;
- private OsmAndLocationSimulation locationSimulation;
+ private final NavigationInfo navigationInfo;
+ private final CurrentPositionHelper currentPositionHelper;
+ private final OsmAndLocationSimulation locationSimulation;
+ private final LocationServiceHelper locationServiceHelper;
private net.osmand.Location location = null;
private GPSInfo gpsInfo = new GPSInfo();
- private List locationListeners = new ArrayList();
- private List compassListeners = new ArrayList();
+ private List locationListeners = new ArrayList<>();
+ private List compassListeners = new ArrayList<>();
private Object gpsStatusListener;
private float[] mRotationM = new float[9];
-
- public class SimulationProvider {
+ public static class SimulationProvider {
private int currentRoad;
private int currentSegment;
private QuadPoint currentPoint;
@@ -227,6 +224,7 @@ public class OsmAndLocationProvider implements SensorEventListener {
navigationInfo = new NavigationInfo(app);
currentPositionHelper = new CurrentPositionHelper(app);
locationSimulation = new OsmAndLocationSimulation(app, this);
+ locationServiceHelper = app.createLocationServiceHelper();
addLocationListener(navigationInfo);
addCompassListener(navigationInfo);
}
@@ -235,7 +233,7 @@ public class OsmAndLocationProvider implements SensorEventListener {
final LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
if (app.getSettings().isInternetConnectionAvailable()) {
if (System.currentTimeMillis() - app.getSettings().AGPS_DATA_LAST_TIME_DOWNLOADED.get() > AGPS_TO_REDOWNLOAD) {
- //force an updated check for internet connectivity here before destroying A-GPS-data
+ // force an updated check for internet connectivity here before destroying A-GPS-data
if (app.getSettings().isInternetConnectionAvailable(true)) {
redownloadAGPS();
}
@@ -244,27 +242,34 @@ public class OsmAndLocationProvider implements SensorEventListener {
if (isLocationPermissionAvailable(app)) {
registerGpsStatusListener(service);
try {
- service.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_TIMEOUT_REQUEST, GPS_DIST_REQUEST, gpsListener);
+ locationServiceHelper.requestLocationUpdates(new LocationServiceHelper.LocationCallback() {
+ @Override
+ public void onLocationResult(@NonNull List locations) {
+ net.osmand.Location location = null;
+ if (!locations.isEmpty()) {
+ location = locations.get(locations.size() - 1);
+ lastTimeGPSLocationFixed = System.currentTimeMillis();
+ }
+ if (!locationSimulation.isRouteAnimating()) {
+ setLocation(location);
+ }
+ }
+ });
+ } catch (SecurityException e) {
+ // Location service permission not granted
} catch (IllegalArgumentException e) {
- Log.d(PlatformUtil.TAG, "GPS location provider not available"); //$NON-NLS-1$
+ // GPS location provider not available
}
// try to always ask for network provide : it is faster way to find location
-
- List providers = service.getProviders(true);
- if (providers == null) {
- return;
- }
- for (String provider : providers) {
- if (provider == null || provider.equals(LocationManager.GPS_PROVIDER)) {
- continue;
- }
- try {
- NetworkListener networkListener = new NetworkListener();
- service.requestLocationUpdates(provider, GPS_TIMEOUT_REQUEST, GPS_DIST_REQUEST, networkListener);
- networkListeners.add(networkListener);
- } catch (IllegalArgumentException e) {
- Log.d(PlatformUtil.TAG, provider + " location provider not available"); //$NON-NLS-1$
- }
+ if (locationServiceHelper.isNetworkLocationUpdatesSupported()) {
+ locationServiceHelper.requestNetworkLocationUpdates(new LocationServiceHelper.LocationCallback() {
+ @Override
+ public void onLocationResult(@NonNull List locations) {
+ if (!locations.isEmpty() && !useOnlyGPS() && !locationSimulation.isRouteAnimating()) {
+ setLocation(locations.get(locations.size() - 1));
+ }
+ }
+ });
}
}
}
@@ -284,8 +289,9 @@ public class OsmAndLocationProvider implements SensorEventListener {
}
}
- private void registerGpsStatusListener(final LocationManager service) {
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ @SuppressLint("MissingPermission")
+ private void registerGpsStatusListener(@NonNull final LocationManager service) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
gpsStatusListener = new GnssStatus.Callback() {
@Override
@@ -320,7 +326,7 @@ public class OsmAndLocationProvider implements SensorEventListener {
updateLocation(location);
}
};
- service.registerGnssStatusCallback((GnssStatus.Callback) gpsStatusListener);
+ service.registerGnssStatusCallback((GnssStatus.Callback) gpsStatusListener, null);
} else {
gpsStatusListener = new Listener() {
private GpsStatus gpsStatus;
@@ -335,14 +341,12 @@ public class OsmAndLocationProvider implements SensorEventListener {
}
}
- private void updateGPSInfo(GpsStatus s) {
+ private void updateGPSInfo(@Nullable GpsStatus s) {
boolean fixed = false;
int n = 0;
int u = 0;
if (s != null) {
- Iterator iterator = s.getSatellites().iterator();
- while (iterator.hasNext()) {
- GpsSatellite g = iterator.next();
+ for (GpsSatellite g : s.getSatellites()) {
n++;
if (g.usedInFix()) {
u++;
@@ -354,7 +358,8 @@ public class OsmAndLocationProvider implements SensorEventListener {
gpsInfo.foundSatellites = n;
gpsInfo.usedSatellites = u;
}
-
+
+ @NonNull
public GPSInfo getGPSInfo(){
return gpsInfo;
}
@@ -363,51 +368,29 @@ public class OsmAndLocationProvider implements SensorEventListener {
currentScreenOrientation = orientation;
}
- public void addLocationListener(OsmAndLocationListener listener){
- if(!locationListeners.contains(listener)) {
+ public void addLocationListener(@NonNull OsmAndLocationListener listener) {
+ if (!locationListeners.contains(listener)) {
locationListeners.add(listener);
}
}
- public void removeLocationListener(OsmAndLocationListener listener){
+ public void removeLocationListener(@NonNull OsmAndLocationListener listener) {
locationListeners.remove(listener);
}
- public void addCompassListener(OsmAndCompassListener listener){
- if(!compassListeners.contains(listener)) {
+ public void addCompassListener(@NonNull OsmAndCompassListener listener) {
+ if (!compassListeners.contains(listener)) {
compassListeners.add(listener);
}
}
- public void removeCompassListener(OsmAndCompassListener listener){
+ public void removeCompassListener(@NonNull OsmAndCompassListener listener) {
compassListeners.remove(listener);
}
+ @Nullable
public net.osmand.Location getFirstTimeRunDefaultLocation() {
- if (!isLocationPermissionAvailable(app)) {
- return null;
- }
- LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
- List ps = service.getProviders(true);
- if(ps == null) {
- return null;
- }
- List providers = new ArrayList(ps);
- // note, passive provider is from API_LEVEL 8 but it is a constant, we can check for it.
- // constant should not be changed in future
- int passiveFirst = providers.indexOf("passive"); // LocationManager.PASSIVE_PROVIDER
- // put passive provider to first place
- if (passiveFirst > -1) {
- providers.add(0, providers.remove(passiveFirst));
- }
- // find location
- for (String provider : providers) {
- net.osmand.Location location = convertLocation(service.getLastKnownLocation(provider), app);
- if (location != null) {
- return location;
- }
- }
- return null;
+ return isLocationPermissionAvailable(app) ? locationServiceHelper.getFirstTimeRunDefaultLocation() : null;
}
public synchronized void registerOrUnregisterCompassListener(boolean register) {
@@ -466,10 +449,7 @@ public class OsmAndLocationProvider implements SensorEventListener {
}
private boolean isRunningOnEmulator() {
- if (Build.DEVICE.equals("generic")) { //$NON-NLS-1$
- return true;
- }
- return false;
+ return Build.DEVICE.equals("generic");
}
@Override
@@ -599,78 +579,25 @@ public class OsmAndLocationProvider implements SensorEventListener {
return MapUtils.unifyRotationTo360((float) (Math.atan2(sinA, cosA) * 180 / Math.PI));
}
-
private void updateLocation(net.osmand.Location loc) {
for (OsmAndLocationListener l : locationListeners) {
l.updateLocation(loc);
}
}
-
-
- private LocationListener gpsListener = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- if (location != null) {
- // lastTimeGPSLocationFixed = location.getTime();
- lastTimeGPSLocationFixed = System.currentTimeMillis();
- }
- if(!locationSimulation.isRouteAnimating()) {
- setLocation(convertLocation(location, app));
- }
- }
-
- @Override
- public void onProviderDisabled(String provider) {
- }
-
- @Override
- public void onProviderEnabled(String provider) {
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
- };
- private LinkedList networkListeners = new LinkedList();
-
private boolean useOnlyGPS() {
- if(app.getRoutingHelper().isFollowingMode()) {
+ if (app.getRoutingHelper().isFollowingMode()) {
return true;
}
- if((System.currentTimeMillis() - lastTimeGPSLocationFixed) < NOT_SWITCH_TO_NETWORK_WHEN_GPS_LOST_MS) {
+ if ((System.currentTimeMillis() - lastTimeGPSLocationFixed) < NOT_SWITCH_TO_NETWORK_WHEN_GPS_LOST_MS) {
return true;
}
- if(isRunningOnEmulator()) {
+ if (isRunningOnEmulator()) {
return true;
}
return false;
}
- // Working with location checkListeners
- private class NetworkListener implements LocationListener {
-
- @Override
- public void onLocationChanged(Location location) {
- if (!useOnlyGPS() && !locationSimulation.isRouteAnimating()) {
- setLocation(convertLocation(location, app));
- }
- }
-
- @Override
- public void onProviderDisabled(String provider) {
- }
-
- @Override
- public void onProviderEnabled(String provider) {
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
-
- };
-
private void stopLocationRequests() {
LocationManager service = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE);
if (gpsStatusListener != null) {
@@ -680,9 +607,10 @@ public class OsmAndLocationProvider implements SensorEventListener {
service.removeGpsStatusListener((Listener) gpsStatusListener);
}
}
- service.removeUpdates(gpsListener);
- while (!networkListeners.isEmpty()) {
- service.removeUpdates(networkListeners.poll());
+ try {
+ locationServiceHelper.removeLocationUpdates();
+ } catch (SecurityException e) {
+ // Location service permission not granted
}
}
@@ -963,7 +891,7 @@ public class OsmAndLocationProvider implements SensorEventListener {
}
public static boolean isNotSimulatedLocation(net.osmand.Location l) {
- if(l != null) {
+ if (l != null) {
return !SIMULATED_PROVIDER.equals(l.getProvider());
}
return true;
@@ -984,7 +912,7 @@ public class OsmAndLocationProvider implements SensorEventListener {
networkenabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch(Exception ex) {}
- if(!gpsenabled && !networkenabled) {
+ if (!gpsenabled && !networkenabled) {
// notify user
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setMessage(context.getResources().getString(R.string.gps_network_not_enabled));
@@ -1003,11 +931,8 @@ public class OsmAndLocationProvider implements SensorEventListener {
}
public static boolean isLocationPermissionAvailable(Context context) {
- if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- return true;
+ return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED;
}
public static void requestFineLocationPermissionIfNeeded(Activity activity) {
diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java
index 8deb9c81a8..842a36237e 100644
--- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java
+++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java
@@ -57,6 +57,7 @@ import net.osmand.plus.download.DownloadService;
import net.osmand.plus.download.IndexItem;
import net.osmand.plus.helpers.AvoidSpecificRoads;
import net.osmand.plus.helpers.DayNightHelper;
+import net.osmand.plus.helpers.LocationServiceHelper;
import net.osmand.plus.helpers.LockHelper;
import net.osmand.plus.helpers.WaypointHelper;
import net.osmand.plus.helpers.enums.DrivingRegion;
@@ -300,7 +301,11 @@ public class OsmandApplication extends MultiDexApplication {
public QuickActionRegistry getQuickActionRegistry() {
return quickActionRegistry;
}
-
+
+ public LocationServiceHelper createLocationServiceHelper() {
+ return new LocationServiceHelperImpl(this);
+ }
+
public void setAppCustomization(OsmAndAppCustomization appCustomization) {
this.appCustomization = appCustomization;
this.appCustomization.setup(this);
diff --git a/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java b/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java
index 63d82ecc26..472899862b 100644
--- a/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java
+++ b/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java
@@ -445,7 +445,7 @@ public class TargetPointsHelper {
Location lastKnownLocation = ctx.getLocationProvider().getLastKnownLocation();
LatLon latLon = lastKnownLocation != null ?
new LatLon(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()) : null;
- RoutingHelperUtils.checkAndUpdateStartLocation(ctx, latLon);
+ RoutingHelperUtils.checkAndUpdateStartLocation(ctx, latLon, false);
setMyLocationPoint(latLon, false, null);
}
}
diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java
index 10fa1a066b..007280144e 100644
--- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java
+++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java
@@ -118,6 +118,7 @@ import net.osmand.plus.measurementtool.LoginBottomSheetFragment;
import net.osmand.plus.measurementtool.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementToolFragment;
import net.osmand.plus.measurementtool.SnapTrackWarningFragment;
+import net.osmand.plus.monitoring.TripRecordingBottomSheet;
import net.osmand.plus.render.RendererRegistry;
import net.osmand.plus.resources.ResourceManager;
import net.osmand.plus.routepreparationmenu.ChooseRouteFragment;
@@ -1408,6 +1409,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
updateMapSettings();
app.getPoiFilters().loadSelectedPoiFilters();
mapViewTrackingUtilities.updateSettings();
+ mapViewTrackingUtilities.resetDrivingRegionUpdate();
//app.getRoutingHelper().setAppMode(settings.getApplicationMode());
if (mapLayers.getMapInfoLayer() != null) {
mapLayers.getMapInfoLayer().recreateControls();
@@ -2204,6 +2206,10 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
return getFragment(MeasurementToolFragment.TAG);
}
+ public TripRecordingBottomSheet getTripRecordingBottomSheet() {
+ return getFragment(TripRecordingBottomSheet.TAG);
+ }
+
public ChooseRouteFragment getChooseRouteFragment() {
return getFragment(ChooseRouteFragment.TAG);
}
@@ -2220,7 +2226,6 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
return getFragment(SnapTrackWarningFragment.TAG);
}
- @NonNull
public TrackMenuFragment getTrackMenuFragment() {
return getFragment(TrackMenuFragment.TAG);
}
diff --git a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java
index fa66a5c91a..14a6623eae 100644
--- a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java
+++ b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java
@@ -13,8 +13,6 @@ import net.osmand.data.LatLon;
import net.osmand.data.RotatedTileBox;
import net.osmand.map.IMapLocationListener;
import net.osmand.map.WorldRegion;
-import net.osmand.plus.mapmarkers.MapMarker;
-import net.osmand.plus.mapmarkers.MapMarkersHelper.MapMarkerChangedListener;
import net.osmand.plus.OsmAndConstants;
import net.osmand.plus.OsmAndLocationProvider;
import net.osmand.plus.OsmAndLocationProvider.OsmAndCompassListener;
@@ -22,10 +20,14 @@ import net.osmand.plus.OsmAndLocationProvider.OsmAndLocationListener;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.dashboard.DashboardOnMap;
+import net.osmand.plus.helpers.enums.DrivingRegion;
import net.osmand.plus.mapcontextmenu.MapContextMenu;
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
+import net.osmand.plus.mapmarkers.MapMarker;
+import net.osmand.plus.mapmarkers.MapMarkersHelper.MapMarkerChangedListener;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.routing.RoutingHelperUtils;
+import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.views.AnimateDraggingMapThread;
import net.osmand.plus.views.OsmandMapTileView;
@@ -176,7 +178,7 @@ public class MapViewTrackingUtilities implements OsmAndLocationListener, IMapLoc
locationProvider = location.getProvider();
if (settings.DRIVING_REGION_AUTOMATIC.get() && !drivingRegionUpdated && !app.isApplicationInitializing()) {
drivingRegionUpdated = true;
- RoutingHelperUtils.checkAndUpdateStartLocation(app, location);
+ RoutingHelperUtils.checkAndUpdateStartLocation(app, location, true);
}
}
if (mapView != null) {
@@ -489,7 +491,15 @@ public class MapViewTrackingUtilities implements OsmAndLocationListener, IMapLoc
@Override
protected void onPostExecute(WorldRegion worldRegion) {
if (worldRegion != null) {
+ DrivingRegion oldRegion = app.getSettings().DRIVING_REGION.get();
+
app.setupDrivingRegion(worldRegion);
+
+ DrivingRegion currentRegion = app.getSettings().DRIVING_REGION.get();
+ if (oldRegion.leftHandDriving != currentRegion.leftHandDriving) {
+ ApplicationMode mode = app.getRoutingHelper().getAppMode();
+ app.getRoutingHelper().onSettingsChanged(mode, true);
+ }
}
}
}
diff --git a/OsmAnd/src/net/osmand/plus/helpers/DayNightHelper.java b/OsmAnd/src/net/osmand/plus/helpers/DayNightHelper.java
index d3ebe9d6ae..20820d379e 100644
--- a/OsmAnd/src/net/osmand/plus/helpers/DayNightHelper.java
+++ b/OsmAnd/src/net/osmand/plus/helpers/DayNightHelper.java
@@ -43,10 +43,10 @@ public class DayNightHelper implements SensorEventListener {
private static final Log log = PlatformUtil.getLog(DayNightHelper.class);
- private final OsmandApplication osmandApplication;
+ private final OsmandApplication app;
- public DayNightHelper(OsmandApplication osmandApplication) {
- this.osmandApplication = osmandApplication;
+ public DayNightHelper(OsmandApplication app) {
+ this.app = app;
}
private DayNightHelper listener;
@@ -56,11 +56,11 @@ public class DayNightHelper implements SensorEventListener {
private StateChangedListener sensorStateListener;
public boolean isNightModeForMapControls() {
- return isNightModeForMapControlsForProfile(osmandApplication.getSettings().APPLICATION_MODE.get());
+ return isNightModeForMapControlsForProfile(app.getSettings().APPLICATION_MODE.get());
}
public boolean isNightModeForMapControlsForProfile(ApplicationMode mode) {
- if (osmandApplication.getSettings().isLightContentForMode(mode)) {
+ if (app.getSettings().isLightContentForMode(mode)) {
return isNightModeForProfile(mode);
} else {
return true;
@@ -72,11 +72,11 @@ public class DayNightHelper implements SensorEventListener {
* @return true if day is supposed to be
*/
public boolean isNightMode() {
- return isNightModeForProfile(osmandApplication.getSettings().APPLICATION_MODE.get());
+ return isNightModeForProfile(app.getSettings().APPLICATION_MODE.get());
}
public boolean isNightModeForProfile(ApplicationMode mode) {
- DayNightMode dayNightMode = osmandApplication.getSettings().DAYNIGHT_MODE.getModeValue(mode);
+ DayNightMode dayNightMode = app.getSettings().DAYNIGHT_MODE.getModeValue(mode);
if (dayNightMode.isDay()) {
return false;
} else if (dayNightMode.isNight()) {
@@ -108,24 +108,23 @@ public class DayNightHelper implements SensorEventListener {
}
public SunriseSunset getSunriseSunset() {
- Location lastKnownLocation = osmandApplication.getLocationProvider().getLastKnownLocation();
- if(lastKnownLocation == null) {
- lastKnownLocation = osmandApplication.getLocationProvider().getFirstTimeRunDefaultLocation();
+ Location lastKnownLocation = app.getLocationProvider().getLastKnownLocation();
+ if (lastKnownLocation == null) {
+ lastKnownLocation = app.getLocationProvider().getFirstTimeRunDefaultLocation();
}
if (lastKnownLocation == null) {
return null;
}
double longitude = lastKnownLocation.getLongitude();
Date actualTime = new Date();
- SunriseSunset daynightSwitch = new SunriseSunset(lastKnownLocation.getLatitude(),
+ return new SunriseSunset(lastKnownLocation.getLatitude(),
longitude < 0 ? 360 + longitude : longitude,
actualTime, TimeZone.getDefault());
- return daynightSwitch;
}
public void stopSensorIfNeeded() {
if (listener != null) {
- SensorManager mSensorManager = (SensorManager) osmandApplication
+ SensorManager mSensorManager = (SensorManager) app
.getSystemService(Context.SENSOR_SERVICE);
Sensor mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
mSensorManager.unregisterListener(listener, mLight);
@@ -135,9 +134,9 @@ public class DayNightHelper implements SensorEventListener {
public void startSensorIfNeeded(StateChangedListener sensorStateListener) {
this.sensorStateListener = sensorStateListener;
- DayNightMode dayNightMode = osmandApplication.getSettings().DAYNIGHT_MODE.get();
+ DayNightMode dayNightMode = app.getSettings().DAYNIGHT_MODE.get();
if (listener == null && dayNightMode.isSensor()) {
- SensorManager mSensorManager = (SensorManager) osmandApplication.getSystemService(Context.SENSOR_SERVICE);
+ SensorManager mSensorManager = (SensorManager) app.getSystemService(Context.SENSOR_SERVICE);
Sensor mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
List list = mSensorManager.getSensorList(Sensor.TYPE_LIGHT);
log.info("Light sensors:" + list.size()); //$NON-NLS-1$
diff --git a/OsmAnd/src/net/osmand/plus/helpers/LocationServiceHelper.java b/OsmAnd/src/net/osmand/plus/helpers/LocationServiceHelper.java
new file mode 100644
index 0000000000..94602e98fc
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/helpers/LocationServiceHelper.java
@@ -0,0 +1,29 @@
+package net.osmand.plus.helpers;
+
+import androidx.annotation.NonNull;
+
+import net.osmand.Location;
+
+import java.util.List;
+
+public abstract class LocationServiceHelper {
+
+ public static abstract class LocationCallback {
+
+ public void onLocationResult(@NonNull List locations) {
+ }
+
+ public void onLocationAvailability(boolean locationAvailable) {
+ }
+ }
+
+ public abstract void requestLocationUpdates(@NonNull LocationCallback locationCallback);
+
+ public abstract boolean isNetworkLocationUpdatesSupported();
+
+ public abstract void requestNetworkLocationUpdates(@NonNull LocationCallback locationCallback);
+
+ public abstract void removeLocationUpdates();
+
+ public abstract Location getFirstTimeRunDefaultLocation();
+}
diff --git a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java
index 16c60911df..42bae2ab62 100644
--- a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java
+++ b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java
@@ -100,12 +100,12 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
public void updateLocation(Location location) {
liveMonitoringHelper.updateLocation(location);
}
-
+
@Override
public int getLogoResourceId() {
return R.drawable.ic_action_gps_info;
}
-
+
@Override
public Drawable getAssetResourceImage() {
return app.getUIUtilities().getIcon(R.drawable.trip_recording);
@@ -140,7 +140,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
private void registerWidget(MapActivity activity) {
MapInfoLayer layer = activity.getMapLayers().getMapInfoLayer();
monitoringControl = createMonitoringControl(activity);
-
+
layer.registerSideWidget(monitoringControl,
R.drawable.ic_action_play_dark, R.string.map_widget_monitoring, "monitoring", false, 30);
layer.recreateControls();
@@ -161,7 +161,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
}
}
}
-
+
public static final int[] SECONDS = new int[] {0, 1, 2, 3, 5, 10, 15, 20, 30, 60, 90};
public static final int[] MINUTES = new int[] {2, 3, 5};
public static final int[] MAX_INTERVAL_TO_SEND_MINUTES = new int[] {1, 2, 5, 10, 15, 20, 30, 60, 90, 2 * 60, 3 * 60, 4 * 60, 6 * 60, 12 * 60, 24 * 60};
@@ -287,7 +287,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
controlDialog(map, true);
}
-
+
});
return monitoringControl;
}
@@ -422,7 +422,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
public void saveCurrentTrack() {
saveCurrentTrack(null, null);
}
-
+
public void saveCurrentTrack(@Nullable final Runnable onComplete) {
saveCurrentTrack(onComplete, null);
}
@@ -464,7 +464,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
SaveGPXBottomSheetFragment.showInstance(((FragmentActivity) a).getSupportFragmentManager(), result.getFilenames());
}
}
-
+
if (onComplete != null) {
onComplete.run();
}
@@ -505,15 +505,9 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
};
if (choice.value || map == null) {
runnable.run();
- } else {
- showIntervalChooseDialog(map, app.getString(R.string.save_track_interval_globally) + " : %s",
- app.getString(R.string.save_track_to_gpx_globally), SECONDS, MINUTES, choice, vs, showTrackSelection,
- new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- runnable.run();
- }
- });
+ } else if (map instanceof FragmentActivity) {
+ FragmentActivity activity = (FragmentActivity) map;
+ TripRecordingBottomSheet.showInstance(activity.getSupportFragmentManager());
}
}
@@ -588,7 +582,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin {
tv.setText(String.format(patternMsg, s));
}
});
-
+
for (int i = 0; i < secondsLength + minutesLength - 1; i++) {
if (i < secondsLength) {
if (v.value <= seconds[i] * 1000) {
diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java
new file mode 100644
index 0000000000..f33f7dfa38
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java
@@ -0,0 +1,273 @@
+package net.osmand.plus.monitoring;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.SwitchCompat;
+import androidx.fragment.app.FragmentManager;
+
+import com.google.android.material.slider.RangeSlider;
+
+import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
+import net.osmand.plus.NavigationService;
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.R;
+import net.osmand.plus.UiUtilities;
+import net.osmand.plus.UiUtilities.DialogButtonType;
+import net.osmand.plus.activities.MapActivity;
+import net.osmand.plus.base.MenuBottomSheetDialogFragment;
+import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription;
+import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem;
+import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.helpers.FontCache;
+import net.osmand.plus.settings.backend.OsmandSettings;
+import net.osmand.plus.track.TrackAppearanceFragment;
+
+import static net.osmand.plus.UiUtilities.CompoundButtonType.PROFILE_DEPENDENT;
+import static net.osmand.plus.monitoring.OsmandMonitoringPlugin.MINUTES;
+import static net.osmand.plus.monitoring.OsmandMonitoringPlugin.SECONDS;
+
+public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment {
+
+ public static final String TAG = TripRecordingBottomSheet.class.getSimpleName();
+
+ private OsmandApplication app;
+ private OsmandSettings settings;
+
+ private ImageView upDownBtn;
+ private SwitchCompat confirmEveryRun;
+ private TextView intervalValueView;
+
+ private boolean infoExpanded;
+
+ @Override
+ public void createMenuItems(Bundle savedInstanceState) {
+ app = requiredMyApplication();
+ settings = app.getSettings();
+ Context context = requireContext();
+
+ LayoutInflater inflater = UiUtilities.getInflater(context, nightMode);
+ View itemView = inflater.inflate(R.layout.trip_recording_fragment, null, false);
+ items.add(new BottomSheetItemWithDescription.Builder()
+ .setCustomView(itemView)
+ .create());
+
+ int padding = getResources().getDimensionPixelSize(R.dimen.content_padding_small);
+ final int paddingSmall = getResources().getDimensionPixelSize(R.dimen.content_padding_small);
+
+ items.add(new DividerSpaceItem(context, padding));
+
+ LinearLayout showTrackOnMapView = itemView.findViewById(R.id.show_track_on_map);
+ TextView showTrackOnMapTitle = showTrackOnMapView.findViewById(R.id.title);
+ showTrackOnMapTitle.setText(R.string.show_track_on_map);
+
+ ImageView trackAppearanceIcon = showTrackOnMapView.findViewById(R.id.icon_after_divider);
+
+ int color = settings.CURRENT_TRACK_COLOR.get();
+ String width = settings.CURRENT_TRACK_WIDTH.get();
+ boolean showArrows = settings.CURRENT_TRACK_SHOW_ARROWS.get();
+ Drawable drawable = TrackAppearanceFragment.getTrackIcon(app, width, showArrows, color);
+
+ trackAppearanceIcon.setImageDrawable(drawable);
+ trackAppearanceIcon.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ MapActivity mapActivity = getMapActivity();
+ if (mapActivity != null) {
+ hide();
+ SelectedGpxFile selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
+ TrackAppearanceFragment.showInstance(mapActivity, selectedGpxFile);
+ }
+ }
+ });
+
+ upDownBtn = itemView.findViewById(R.id.up_down_button);
+ upDownBtn.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ toggleInfoView();
+ }
+ });
+
+ final int secondsLength = SECONDS.length;
+ final int minutesLength = MINUTES.length;
+
+ intervalValueView = itemView.findViewById(R.id.interval_value);
+ updateIntervalLegend();
+
+ RangeSlider intervalSlider = itemView.findViewById(R.id.interval_slider);
+ intervalSlider.setValueTo(secondsLength + minutesLength - 1);
+ intervalSlider.addOnChangeListener(new RangeSlider.OnChangeListener() {
+
+ @Override
+ public void onValueChange(@NonNull RangeSlider slider, float value, boolean fromUser) {
+ int progress = (int) value;
+ if (progress == 0) {
+ settings.SAVE_GLOBAL_TRACK_INTERVAL.set(0);
+ } else if (progress < secondsLength) {
+ settings.SAVE_GLOBAL_TRACK_INTERVAL.set(SECONDS[progress] * 1000);
+ } else {
+ settings.SAVE_GLOBAL_TRACK_INTERVAL.set(MINUTES[progress - secondsLength] * 60 * 1000);
+ }
+ updateIntervalLegend();
+ }
+ });
+ for (int i = 0; i < secondsLength + minutesLength; i++) {
+ if (i < secondsLength) {
+ if (settings.SAVE_GLOBAL_TRACK_INTERVAL.get() <= SECONDS[i] * 1000) {
+ intervalSlider.setValues((float) i);
+ break;
+ }
+ } else {
+ if (settings.SAVE_GLOBAL_TRACK_INTERVAL.get() <= MINUTES[i - secondsLength] * 1000 * 60) {
+ intervalSlider.setValues((float) i);
+ break;
+ }
+ }
+ }
+ boolean checked = !settings.SAVE_GLOBAL_TRACK_REMEMBER.get();
+ confirmEveryRun = itemView.findViewById(R.id.confirm_every_run);
+ confirmEveryRun.setChecked(checked);
+ setBackgroundAndPadding(checked, paddingSmall);
+ confirmEveryRun.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setBackgroundAndPadding(isChecked, paddingSmall);
+ settings.SAVE_GLOBAL_TRACK_REMEMBER.set(!isChecked);
+ }
+ });
+
+ SwitchCompat showTrackOnMapButton = showTrackOnMapView.findViewById(R.id.switch_button);
+ showTrackOnMapButton.setChecked(app.getSelectedGpxHelper().getSelectedCurrentRecordingTrack() != null);
+ showTrackOnMapButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ app.getSelectedGpxHelper().selectGpxFile(app.getSavingTrackHelper().getCurrentGpx(), isChecked, false);
+ }
+ });
+ UiUtilities.setupCompoundButton(showTrackOnMapButton, nightMode, PROFILE_DEPENDENT);
+
+ updateUpDownBtn();
+ }
+
+ private void updateIntervalLegend() {
+ String text = getString(R.string.save_track_interval_globally);
+ String textValue;
+ int interval = settings.SAVE_GLOBAL_TRACK_INTERVAL.get();
+ if (interval == 0) {
+ textValue = getString(R.string.int_continuosly);
+ } else {
+ int seconds = interval / 1000;
+ if (seconds <= SECONDS[SECONDS.length - 1]) {
+ textValue = seconds + " " + getString(R.string.int_seconds);
+ } else {
+ textValue = (seconds / 60) + " " + getString(R.string.int_min);
+ }
+ }
+ String textAll = getString(R.string.ltr_or_rtl_combine_via_colon, text, textValue);
+ Typeface typeface = FontCache.getRobotoMedium(app);
+ SpannableString spannableString = UiUtilities.createCustomFontSpannable(typeface, textAll, textValue);
+ intervalValueView.setText(spannableString);
+ }
+
+ public void show() {
+ Dialog dialog = getDialog();
+ if (dialog != null) {
+ dialog.show();
+ }
+ }
+
+ public void hide() {
+ Dialog dialog = getDialog();
+ if (dialog != null) {
+ dialog.hide();
+ }
+ }
+
+ private void setBackgroundAndPadding(boolean isChecked, int paddingSmall) {
+ if (nightMode) {
+ confirmEveryRun.setBackgroundResource(
+ isChecked ? R.drawable.layout_bg_dark_solid : R.drawable.layout_bg_dark);
+ } else {
+ confirmEveryRun.setBackgroundResource(
+ isChecked ? R.drawable.layout_bg_solid : R.drawable.layout_bg);
+ }
+ confirmEveryRun.setPadding(paddingSmall, 0, paddingSmall, 0);
+ }
+
+ private void updateUpDownBtn() {
+ int iconId = infoExpanded ? R.drawable.ic_action_arrow_down : R.drawable.ic_action_arrow_up;
+ upDownBtn.setImageDrawable(getContentIcon(iconId));
+ }
+
+ private void toggleInfoView() {
+ infoExpanded = !infoExpanded;
+ AndroidUiHelper.updateVisibility(confirmEveryRun, infoExpanded);
+ updateUpDownBtn();
+ }
+
+ @Override
+ protected boolean useVerticalButtons() {
+ return true;
+ }
+
+ @Override
+ protected int getRightBottomButtonTextId() {
+ return R.string.start_recording;
+ }
+
+ @Override
+ protected int getDismissButtonTextId() {
+ return R.string.shared_string_cancel;
+ }
+
+ @Override
+ protected DialogButtonType getRightBottomButtonType() {
+ return DialogButtonType.PRIMARY;
+ }
+
+ @Override
+ public int getSecondDividerHeight() {
+ return getResources().getDimensionPixelSize(R.dimen.bottom_sheet_icon_margin);
+ }
+
+ @Override
+ protected void onRightBottomButtonClick() {
+ app.getSavingTrackHelper().startNewSegment();
+ settings.SAVE_GLOBAL_TRACK_TO_GPX.set(true);
+ app.startNavigationService(NavigationService.USED_BY_GPX);
+ dismiss();
+ }
+
+ @Nullable
+ public MapActivity getMapActivity() {
+ Activity activity = getActivity();
+ if (activity instanceof MapActivity) {
+ return (MapActivity) activity;
+ }
+ return null;
+ }
+
+ public static void showInstance(@NonNull FragmentManager fragmentManager) {
+ if (!fragmentManager.isStateSaved()) {
+ TripRecordingBottomSheet fragment = new TripRecordingBottomSheet();
+ fragment.show(fragmentManager, TAG);
+ }
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/EngineParameter.java b/OsmAnd/src/net/osmand/plus/onlinerouting/EngineParameter.java
new file mode 100644
index 0000000000..fa3ccd81c7
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/EngineParameter.java
@@ -0,0 +1,10 @@
+package net.osmand.plus.onlinerouting;
+
+public enum EngineParameter {
+ KEY,
+ VEHICLE_KEY,
+ CUSTOM_NAME,
+ NAME_INDEX,
+ CUSTOM_URL,
+ API_KEY
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/EngineType.java b/OsmAnd/src/net/osmand/plus/onlinerouting/EngineType.java
deleted file mode 100644
index fe5080f14c..0000000000
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/EngineType.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package net.osmand.plus.onlinerouting;
-
-public enum EngineType {
-
- GRAPHHOPPER("Graphhopper", "https://graphhopper.com/api/1/route"),
- OSRM("OSRM", "https://router.project-osrm.org/route/v1/"),
- ORS("Openroute Service", "https://api.openrouteservice.org/v2/directions/");
-
- private String title;
- private String standardUrl;
-
- EngineType(String title, String standardUrl) {
- this.title = title;
- this.standardUrl = standardUrl;
- }
-
- public String getTitle() {
- return title;
- }
-
- public String getStandardUrl() {
- return standardUrl;
- }
-}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingEngine.java
deleted file mode 100644
index 3193d86d09..0000000000
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingEngine.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package net.osmand.plus.onlinerouting;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import net.osmand.plus.R;
-import net.osmand.util.Algorithms;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class OnlineRoutingEngine {
-
- public final static String ONLINE_ROUTING_ENGINE_PREFIX = "online_routing_engine_";
-
- public enum EngineParameter {
- CUSTOM_NAME,
- CUSTOM_URL,
- API_KEY
- }
-
- private String stringKey;
- private EngineType type;
- private String vehicleKey;
- private Map params = new HashMap<>();
-
- public OnlineRoutingEngine(@NonNull String stringKey,
- @NonNull EngineType type,
- @NonNull String vehicleKey,
- @Nullable Map params) {
- this(stringKey, type, vehicleKey);
- if (!Algorithms.isEmpty(params)) {
- this.params.putAll(params);
- }
- }
-
- public OnlineRoutingEngine(@NonNull String stringKey,
- @NonNull EngineType type,
- @NonNull String vehicleKey) {
- this.stringKey = stringKey;
- this.type = type;
- this.vehicleKey = vehicleKey;
- }
-
- public String getStringKey() {
- return stringKey;
- }
-
- public EngineType getType() {
- return type;
- }
-
- public String getBaseUrl() {
- String customUrl = getParameter(EngineParameter.CUSTOM_URL);
- if (Algorithms.isEmpty(customUrl)) {
- return type.getStandardUrl();
- }
- return customUrl;
- }
-
- public String getVehicleKey() {
- return vehicleKey;
- }
-
- public Map getParams() {
- return params;
- }
-
- public String getParameter(EngineParameter paramKey) {
- return params.get(paramKey.name());
- }
-
- public void putParameter(EngineParameter paramKey, String paramValue) {
- params.put(paramKey.name(), paramValue);
- }
-
- public String getName(@NonNull Context ctx) {
- String customName = getParameter(EngineParameter.CUSTOM_NAME);
- if (customName != null) {
- return customName;
- } else {
- return getStandardName(ctx);
- }
- }
-
- private String getStandardName(@NonNull Context ctx) {
- return getStandardName(ctx, type, vehicleKey);
- }
-
- public static String getStandardName(@NonNull Context ctx,
- @NonNull EngineType type,
- @NonNull String vehicleKey) {
- String vehicleTitle = VehicleType.toHumanString(ctx, vehicleKey);
- String pattern = ctx.getString(R.string.ltr_or_rtl_combine_via_dash);
- return String.format(pattern, type.getTitle(), vehicleTitle);
- }
-
- public static OnlineRoutingEngine createNewEngine(@NonNull EngineType type,
- @NonNull String vehicleKey,
- @Nullable Map params) {
- return new OnlineRoutingEngine(generateKey(), type, vehicleKey, params);
- }
-
- private static String generateKey() {
- return ONLINE_ROUTING_ENGINE_PREFIX + System.currentTimeMillis();
- }
-}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingEngineFragment.java b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingEngineFragment.java
deleted file mode 100644
index 59df4ef698..0000000000
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingEngineFragment.java
+++ /dev/null
@@ -1,609 +0,0 @@
-package net.osmand.plus.onlinerouting;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.widget.Toolbar;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-
-import net.osmand.AndroidNetworkUtils;
-import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
-import net.osmand.AndroidUtils;
-import net.osmand.CallbackWithObject;
-import net.osmand.data.LatLon;
-import net.osmand.plus.OsmandApplication;
-import net.osmand.plus.R;
-import net.osmand.plus.UiUtilities;
-import net.osmand.plus.UiUtilities.DialogButtonType;
-import net.osmand.plus.activities.MapActivity;
-import net.osmand.plus.base.BaseOsmAndFragment;
-import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem;
-import net.osmand.plus.onlinerouting.OnlineRoutingCard.OnTextChangedListener;
-import net.osmand.plus.onlinerouting.OnlineRoutingEngine.EngineParameter;
-import net.osmand.plus.routepreparationmenu.cards.BaseCard;
-import net.osmand.plus.settings.backend.ApplicationMode;
-import net.osmand.util.Algorithms;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
-
- public static final String TAG = OnlineRoutingEngineFragment.class.getSimpleName();
-
- private static final String ENGINE_NAME_KEY = "engine_name";
- private static final String ENGINE_SERVER_KEY = "engine_server";
- private static final String ENGINE_SERVER_URL_KEY = "engine_server_url";
- private static final String ENGINE_VEHICLE_TYPE_KEY = "engine_vehicle_type";
- private static final String ENGINE_CUSTOM_VEHICLE_KEY = "engine_custom_vehicle";
- private static final String ENGINE_API_KEY_KEY = "engine_api_key";
- private static final String EXAMPLE_LOCATION_KEY = "example_location";
- private static final String APP_MODE_KEY = "app_mode";
- private static final String EDITED_ENGINE_KEY = "edited_engine_key";
-
- private OsmandApplication app;
- private MapActivity mapActivity;
- private OnlineRoutingHelper helper;
-
- private View view;
- private ViewGroup segmentsContainer;
- private OnlineRoutingCard nameCard;
- private OnlineRoutingCard typeCard;
- private OnlineRoutingCard vehicleCard;
- private OnlineRoutingCard apiKeyCard;
- private OnlineRoutingCard exampleCard;
- private View testResultsContainer;
-
- private ApplicationMode appMode;
-
- private OnlineRoutingEngineObject engine;
- private ExampleLocation selectedLocation;
- private String editedEngineKey;
-
- private enum ExampleLocation {
-
- AMSTERDAM("Amsterdam",
- new LatLon(52.379189, 4.899431),
- new LatLon(52.308056, 4.764167)),
-
- BERLIN("Berlin",
- new LatLon(52.520008, 13.404954),
- new LatLon(52.3666652, 13.501997992)),
-
- NEW_YORK("New York",
- new LatLon(43.000000, -75.000000),
- new LatLon(40.641766, -73.780968)),
-
- PARIS("Paris",
- new LatLon(48.864716, 2.349014),
- new LatLon(48.948437, 2.434931));
-
- ExampleLocation(String name, LatLon cityCenterLatLon, LatLon cityAirportLatLon) {
- this.name = name;
- this.cityCenterLatLon = cityCenterLatLon;
- this.cityAirportLatLon = cityAirportLatLon;
- }
-
- private String name;
- private LatLon cityCenterLatLon;
- private LatLon cityAirportLatLon;
-
- public String getName() {
- return name;
- }
-
- public LatLon getCityCenterLatLon() {
- return cityCenterLatLon;
- }
-
- public LatLon getCityAirportLatLon() {
- return cityAirportLatLon;
- }
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- app = requireMyApplication();
- mapActivity = getMapActivity();
- helper = app.getOnlineRoutingHelper();
- engine = new OnlineRoutingEngineObject();
- if (savedInstanceState != null) {
- restoreState(savedInstanceState);
- } else {
- initState();
- }
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- view = getInflater().inflate(
- R.layout.online_routing_engine_fragment, container, false);
- segmentsContainer = (ViewGroup) view.findViewById(R.id.segments_container);
- if (Build.VERSION.SDK_INT >= 21) {
- AndroidUtils.addStatusBarPadding21v(getContext(), view);
- }
- setupToolbar((Toolbar) view.findViewById(R.id.toolbar));
-
- setupNameCard();
- setupTypeCard();
- setupVehicleCard();
- setupApiKeyCard();
- setupExampleCard();
- setupResultsContainer();
- addSpaceSegment();
-
- setupButtons();
-
- updateCardViews(nameCard, typeCard, vehicleCard, exampleCard);
- return view;
- }
-
- private void setupNameCard() {
- nameCard = new OnlineRoutingCard(mapActivity, isNightMode());
- nameCard.build(mapActivity);
- nameCard.setDescription(getString(R.string.select_nav_profile_dialog_message));
- nameCard.setEditedText(engine.getName(app));
- nameCard.setFieldBoxLabelText(getString(R.string.shared_string_name));
- nameCard.setOnTextChangedListener(new OnTextChangedListener() {
- @Override
- public void onTextChanged(boolean changedByUser, String text) {
- if (changedByUser) {
- engine.customName = text;
- }
- }
- });
- nameCard.showDivider();
- segmentsContainer.addView(nameCard.getView());
- }
-
- private void setupTypeCard() {
- typeCard = new OnlineRoutingCard(mapActivity, isNightMode());
- typeCard.build(mapActivity);
- typeCard.setHeaderTitle(getString(R.string.shared_string_type));
- List serverItems = new ArrayList<>();
- for (EngineType server : EngineType.values()) {
- serverItems.add(new HorizontalSelectionItem(server.getTitle(), server));
- }
- typeCard.setSelectionMenu(serverItems, engine.type.getTitle(),
- new CallbackWithObject() {
- @Override
- public boolean processResult(HorizontalSelectionItem result) {
- EngineType type = (EngineType) result.getObject();
- if (engine.type != type) {
- engine.type = type;
- updateCardViews(nameCard, typeCard, exampleCard);
- return true;
- }
- return false;
- }
- });
- typeCard.setOnTextChangedListener(new OnTextChangedListener() {
- @Override
- public void onTextChanged(boolean editedByUser, String text) {
- if (editedByUser) {
- engine.customServerUrl = text;
- updateCardViews(exampleCard);
- }
- }
- });
- typeCard.setFieldBoxLabelText(getString(R.string.shared_string_server_url));
- typeCard.showDivider();
- segmentsContainer.addView(typeCard.getView());
- }
-
- private void setupVehicleCard() {
- vehicleCard = new OnlineRoutingCard(mapActivity, isNightMode());
- vehicleCard.build(mapActivity);
- vehicleCard.setHeaderTitle(getString(R.string.shared_string_vehicle));
- List vehicleItems = new ArrayList<>();
- for (VehicleType vehicle : VehicleType.values()) {
- vehicleItems.add(new HorizontalSelectionItem(vehicle.getTitle(app), vehicle));
- }
- vehicleCard.setSelectionMenu(vehicleItems, engine.vehicleType.getTitle(app),
- new CallbackWithObject() {
- @Override
- public boolean processResult(HorizontalSelectionItem result) {
- VehicleType vehicle = (VehicleType) result.getObject();
- if (engine.vehicleType != vehicle) {
- engine.vehicleType = vehicle;
- updateCardViews(nameCard, vehicleCard, exampleCard);
- return true;
- }
- return false;
- }
- });
- vehicleCard.setFieldBoxLabelText(getString(R.string.shared_string_custom));
- vehicleCard.setOnTextChangedListener(new OnTextChangedListener() {
- @Override
- public void onTextChanged(boolean editedByUser, String text) {
- if (editedByUser) {
- engine.customVehicleKey = text;
- updateCardViews(nameCard, exampleCard);
- }
- }
- });
- vehicleCard.setEditedText(engine.customVehicleKey);
- vehicleCard.setFieldBoxHelperText(getString(R.string.shared_string_enter_param));
- vehicleCard.showDivider();
- segmentsContainer.addView(vehicleCard.getView());
- }
-
- private void setupApiKeyCard() {
- apiKeyCard = new OnlineRoutingCard(mapActivity, isNightMode());
- apiKeyCard.build(mapActivity);
- apiKeyCard.setHeaderTitle(getString(R.string.shared_string_api_key));
- apiKeyCard.setFieldBoxLabelText(getString(R.string.keep_it_empty_if_not));
- apiKeyCard.setEditedText(engine.apiKey);
- apiKeyCard.showDivider();
- apiKeyCard.setOnTextChangedListener(new OnTextChangedListener() {
- @Override
- public void onTextChanged(boolean editedByUser, String text) {
- engine.apiKey = text;
- updateCardViews(exampleCard);
- }
- });
- segmentsContainer.addView(apiKeyCard.getView());
- }
-
- private void setupExampleCard() {
- exampleCard = new OnlineRoutingCard(mapActivity, isNightMode());
- exampleCard.build(mapActivity);
- exampleCard.setHeaderTitle(getString(R.string.shared_string_example));
- List locationItems = new ArrayList<>();
- for (ExampleLocation location : ExampleLocation.values()) {
- locationItems.add(new HorizontalSelectionItem(location.getName(), location));
- }
- exampleCard.setSelectionMenu(locationItems, selectedLocation.getName(),
- new CallbackWithObject() {
- @Override
- public boolean processResult(HorizontalSelectionItem result) {
- ExampleLocation location = (ExampleLocation) result.getObject();
- if (selectedLocation != location) {
- selectedLocation = location;
- updateCardViews(exampleCard);
- return true;
- }
- return false;
- }
- });
- exampleCard.setFieldBoxHelperText(getString(R.string.online_routing_example_hint));
- exampleCard.setButton(getString(R.string.test_route_calculation), new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- testEngineWork();
- }
- });
- segmentsContainer.addView(exampleCard.getView());
- }
-
- private void setupResultsContainer() {
- testResultsContainer = getInflater().inflate(
- R.layout.bottom_sheet_item_with_descr_64dp, segmentsContainer, false);
- testResultsContainer.setVisibility(View.GONE);
- segmentsContainer.addView(testResultsContainer);
- }
-
- private void addSpaceSegment() {
- int space = (int) getResources().getDimension(R.dimen.empty_state_text_button_padding_top);
- View bottomSpaceView = new View(app);
- bottomSpaceView.setLayoutParams(
- new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, space));
- segmentsContainer.addView(bottomSpaceView);
- }
-
- private void setupToolbar(Toolbar toolbar) {
- ImageView navigationIcon = toolbar.findViewById(R.id.close_button);
- navigationIcon.setImageResource(R.drawable.ic_action_close);
- navigationIcon.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- dismiss();
- }
- });
- TextView title = toolbar.findViewById(R.id.toolbar_title);
- toolbar.findViewById(R.id.toolbar_subtitle).setVisibility(View.GONE);
- View actionBtn = toolbar.findViewById(R.id.action_button);
- if (isEditingMode()) {
- title.setText(getString(R.string.edit_online_routing_engine));
- ImageView ivBtn = toolbar.findViewById(R.id.action_button_icon);
- ivBtn.setImageDrawable(
- getIcon(R.drawable.ic_action_delete_dark, R.color.color_osm_edit_delete));
- actionBtn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- deleteEngine();
- dismiss();
- }
- });
- } else {
- title.setText(getString(R.string.add_online_routing_engine));
- actionBtn.setVisibility(View.GONE);
- }
- }
-
- private void updateCardViews(BaseCard... cardsToUpdate) {
- for (BaseCard card : cardsToUpdate) {
- if (nameCard.equals(card)) {
- if (Algorithms.isEmpty(engine.customName)) {
- String name;
- if (Algorithms.isEmpty(engine.getVehicleKey())) {
- name = engine.type.getTitle();
- } else {
- name = OnlineRoutingEngine.getStandardName(app, engine.type, engine.getVehicleKey());
- }
- nameCard.setEditedText(name);
- }
-
- } else if (typeCard.equals(card)) {
- typeCard.setHeaderSubtitle(engine.type.getTitle());
- typeCard.setEditedText(engine.getBaseUrl());
- if (engine.type == EngineType.GRAPHHOPPER || engine.type == EngineType.ORS) {
- apiKeyCard.show();
- } else {
- apiKeyCard.hide();
- }
-
- } else if (vehicleCard.equals(card)) {
- VehicleType vt = VehicleType.getVehicleByKey(engine.getVehicleKey());
- vehicleCard.setHeaderSubtitle(vt.getTitle(app));
- if (vt == VehicleType.CUSTOM) {
- vehicleCard.showFieldBox();
- vehicleCard.setEditedText(engine.getVehicleKey());
- } else {
- vehicleCard.hideFieldBox();
- }
-
- } else if (exampleCard.equals(card)) {
- exampleCard.setEditedText(getTestUrl());
- }
- }
- }
-
- private void setupButtons() {
- boolean nightMode = isNightMode();
- View cancelButton = view.findViewById(R.id.dismiss_button);
- UiUtilities.setupDialogButton(nightMode, cancelButton,
- DialogButtonType.SECONDARY, R.string.shared_string_cancel);
- cancelButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- dismiss();
- }
- });
-
- view.findViewById(R.id.buttons_divider).setVisibility(View.VISIBLE);
-
- View saveButton = view.findViewById(R.id.right_bottom_button);
- UiUtilities.setupDialogButton(nightMode, saveButton,
- UiUtilities.DialogButtonType.PRIMARY, R.string.shared_string_save);
- saveButton.setVisibility(View.VISIBLE);
- saveButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- saveChanges();
- dismiss();
- }
- });
- }
-
- private void saveChanges() {
- OnlineRoutingEngine engineToSave;
- if (isEditingMode()) {
- engineToSave = new OnlineRoutingEngine(editedEngineKey, engine.type, engine.getVehicleKey());
- } else {
- engineToSave = OnlineRoutingEngine.createNewEngine(engine.type, engine.getVehicleKey(), null);
- }
-
- engineToSave.putParameter(EngineParameter.CUSTOM_NAME, engine.customName);
- engineToSave.putParameter(EngineParameter.CUSTOM_URL, engine.customServerUrl);
- if (engine.type == EngineType.GRAPHHOPPER || engine.type == EngineType.ORS) {
- engineToSave.putParameter(EngineParameter.API_KEY, engine.apiKey);
- }
-
- helper.saveEngine(engineToSave);
- }
-
- private void deleteEngine() {
- helper.deleteEngine(editedEngineKey);
- }
-
- private String getTestUrl() {
- List path = new ArrayList<>();
- path.add(selectedLocation.getCityCenterLatLon());
- path.add(selectedLocation.getCityAirportLatLon());
- OnlineRoutingEngine tmpEngine =
- OnlineRoutingEngine.createNewEngine(engine.type, engine.getVehicleKey(), null);
- tmpEngine.putParameter(EngineParameter.CUSTOM_URL, engine.customServerUrl);
- tmpEngine.putParameter(EngineParameter.API_KEY, engine.apiKey);
- return helper.createFullUrl(tmpEngine, path);
- }
-
- private void testEngineWork() {
- final EngineType type = engine.type;
- final ExampleLocation location = selectedLocation;
- AndroidNetworkUtils.sendRequestAsync(app, exampleCard.getEditedText(), null,
- null, false, false, new OnRequestResultListener() {
- @Override
- public void onResult(String response) {
- boolean resultOk = false;
- if (response != null) {
- try {
- JSONObject obj = new JSONObject(response);
-
- if (type == EngineType.GRAPHHOPPER) {
- resultOk = obj.has("paths");
- } else if (type == EngineType.OSRM) {
- resultOk = obj.has("routes");
- } else if (type == EngineType.ORS) {
- resultOk = obj.has("features");
- }
- } catch (JSONException e) {
-
- }
- }
- showTestResults(resultOk, location);
- }
- });
- }
-
- private void showTestResults(boolean resultOk, ExampleLocation location) {
- testResultsContainer.setVisibility(View.VISIBLE);
- ImageView ivImage = testResultsContainer.findViewById(R.id.icon);
- TextView tvTitle = testResultsContainer.findViewById(R.id.title);
- TextView tvDescription = testResultsContainer.findViewById(R.id.description);
- if (resultOk) {
- ivImage.setImageDrawable(getContentIcon(R.drawable.ic_action_gdirections_dark));
- tvTitle.setText(getString(R.string.shared_string_ok));
- } else {
- ivImage.setImageDrawable(getContentIcon(R.drawable.ic_action_alert));
- tvTitle.setText(getString(R.string.message_error_recheck_parameters));
- }
- tvDescription.setText(location.getName());
- }
-
- private boolean isEditingMode() {
- return editedEngineKey != null;
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- saveState(outState);
- }
-
- private void saveState(Bundle outState) {
- outState.putString(ENGINE_NAME_KEY, engine.customName);
- outState.putString(ENGINE_SERVER_KEY, engine.type.name());
- outState.putString(ENGINE_SERVER_URL_KEY, engine.customServerUrl);
- outState.putString(ENGINE_VEHICLE_TYPE_KEY, engine.vehicleType.name());
- outState.putString(ENGINE_CUSTOM_VEHICLE_KEY, engine.customVehicleKey);
- outState.putString(ENGINE_API_KEY_KEY, engine.apiKey);
- outState.putString(EXAMPLE_LOCATION_KEY, selectedLocation.name());
- if (appMode != null) {
- outState.putString(APP_MODE_KEY, appMode.getStringKey());
- }
- outState.putString(EDITED_ENGINE_KEY, editedEngineKey);
- }
-
- private void restoreState(Bundle savedState) {
- engine.customName = savedState.getString(ENGINE_NAME_KEY);
- engine.type = EngineType.valueOf(savedState.getString(ENGINE_SERVER_KEY));
- engine.customServerUrl = savedState.getString(ENGINE_SERVER_URL_KEY);
- engine.vehicleType = VehicleType.valueOf(savedState.getString(ENGINE_VEHICLE_TYPE_KEY));
- engine.customVehicleKey = savedState.getString(ENGINE_CUSTOM_VEHICLE_KEY);
- engine.apiKey = savedState.getString(ENGINE_API_KEY_KEY);
- selectedLocation = ExampleLocation.valueOf(savedState.getString(EXAMPLE_LOCATION_KEY));
- appMode = ApplicationMode.valueOfStringKey(savedState.getString(APP_MODE_KEY), null);
- editedEngineKey = savedState.getString(EDITED_ENGINE_KEY);
- }
-
- private void initState() {
- engine.type = EngineType.values()[0];
- engine.vehicleType = VehicleType.values()[0];
- selectedLocation = ExampleLocation.values()[0];
-
- if (isEditingMode()) {
- OnlineRoutingEngine editedEngine = helper.getEngineByKey(editedEngineKey);
- if (editedEngine != null) {
- engine.customName = editedEngine.getParameter(EngineParameter.CUSTOM_NAME);
- engine.type = editedEngine.getType();
- String vehicleKey = editedEngine.getVehicleKey();
- if (vehicleKey != null) {
- VehicleType vehicleType = VehicleType.getVehicleByKey(vehicleKey);
- if (vehicleType == VehicleType.CUSTOM) {
- engine.customVehicleKey = vehicleKey;
- }
- engine.vehicleType = vehicleType;
- }
- engine.apiKey = editedEngine.getParameter(EngineParameter.API_KEY);
- }
- }
- }
-
- private void dismiss() {
- FragmentActivity activity = getActivity();
- if (activity != null) {
- activity.onBackPressed();
- }
- }
-
- private boolean isNightMode() {
- return !app.getSettings().isLightContentForMode(appMode);
- }
-
- @Nullable
- private MapActivity getMapActivity() {
- FragmentActivity activity = getActivity();
- if (activity instanceof MapActivity) {
- return (MapActivity) activity;
- } else {
- return null;
- }
- }
-
- private LayoutInflater getInflater() {
- return UiUtilities.getInflater(mapActivity, isNightMode());
- }
-
- public static void showInstance(@NonNull FragmentActivity activity,
- @NonNull ApplicationMode appMode,
- String editedEngineKey) {
- FragmentManager fm = activity.getSupportFragmentManager();
- if (!fm.isStateSaved() && fm.findFragmentByTag(OnlineRoutingEngineFragment.TAG) == null) {
- OnlineRoutingEngineFragment fragment = new OnlineRoutingEngineFragment();
- fragment.appMode = appMode;
- fragment.editedEngineKey = editedEngineKey;
- fm.beginTransaction()
- .add(R.id.fragmentContainer, fragment, TAG)
- .addToBackStack(TAG).commitAllowingStateLoss();
- }
- }
-
- private static class OnlineRoutingEngineObject {
- private String customName;
- private EngineType type;
- private String customServerUrl;
- private VehicleType vehicleType;
- private String customVehicleKey;
- private String apiKey;
-
- public String getVehicleKey() {
- if (vehicleType == VehicleType.CUSTOM) {
- return customVehicleKey;
- }
- return vehicleType.getKey();
- }
-
- public String getName(Context ctx) {
- if (customName != null) {
- return customName;
- }
- return OnlineRoutingEngine.getStandardName(ctx, type, getVehicleKey());
- }
-
- public String getBaseUrl() {
- if (Algorithms.isEmpty(customServerUrl)) {
- return type.getStandardUrl();
- }
- return customServerUrl;
- }
- }
-}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingFactory.java b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingFactory.java
new file mode 100644
index 0000000000..4b92000847
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingFactory.java
@@ -0,0 +1,36 @@
+package net.osmand.plus.onlinerouting;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.plus.onlinerouting.engine.EngineType;
+import net.osmand.plus.onlinerouting.engine.GraphhopperEngine;
+import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
+import net.osmand.plus.onlinerouting.engine.OrsEngine;
+import net.osmand.plus.onlinerouting.engine.OsrmEngine;
+
+import java.util.Map;
+
+public class OnlineRoutingFactory {
+
+ public static OnlineRoutingEngine createEngine(@NonNull EngineType type) {
+ return createEngine(type, null);
+ }
+
+ @NonNull
+ public static OnlineRoutingEngine createEngine(@NonNull EngineType type,
+ @Nullable Map params) {
+ switch (type) {
+ case GRAPHHOPPER:
+ return new GraphhopperEngine(params);
+ case OSRM:
+ return new OsrmEngine(params);
+ case ORS:
+ return new OrsEngine(params);
+ default:
+ throw new IllegalArgumentException(
+ "Online routing type {" + type.name() + "} not supported");
+ }
+ }
+
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java
index b54746ddd6..b28d98d55f 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java
@@ -1,6 +1,7 @@
package net.osmand.plus.onlinerouting;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@@ -10,10 +11,10 @@ import net.osmand.data.LatLon;
import net.osmand.osm.io.NetworkUtils;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.Version;
-import net.osmand.plus.onlinerouting.OnlineRoutingEngine.EngineParameter;
+import net.osmand.plus.onlinerouting.engine.EngineType;
+import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.util.Algorithms;
-import net.osmand.util.GeoPolylineParserUtil;
import org.apache.commons.logging.Log;
import org.json.JSONArray;
@@ -24,7 +25,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
-import java.net.URLConnection;
+import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -35,14 +36,18 @@ public class OnlineRoutingHelper {
private static final Log LOG = PlatformUtil.getLog(OnlineRoutingHelper.class);
+ private static final String ITEMS = "items";
+ private static final String TYPE = "type";
+ private static final String PARAMS = "params";
+
private OsmandApplication app;
private OsmandSettings settings;
private Map cachedEngines;
- public OnlineRoutingHelper(OsmandApplication app) {
+ public OnlineRoutingHelper(@NonNull OsmandApplication app) {
this.app = app;
this.settings = app.getSettings();
- loadFromSettings();
+ this.cachedEngines = loadSavedEngines();
}
@NonNull
@@ -50,104 +55,42 @@ public class OnlineRoutingHelper {
return new ArrayList<>(cachedEngines.values());
}
- public OnlineRoutingEngine getEngineByKey(String stringKey) {
+ @NonNull
+ public List getEnginesExceptMentioned(@Nullable String ... excludeKeys) {
+ List engines = getEngines();
+ if (excludeKeys != null) {
+ for (String key : excludeKeys) {
+ OnlineRoutingEngine engine = getEngineByKey(key);
+ engines.remove(engine);
+ }
+ }
+ return engines;
+ }
+
+ @Nullable
+ public OnlineRoutingEngine getEngineByKey(@Nullable String stringKey) {
return cachedEngines.get(stringKey);
}
+ @NonNull
public List calculateRouteOnline(@NonNull OnlineRoutingEngine engine,
@NonNull List path) throws IOException, JSONException {
- String fullUrl = createFullUrl(engine, path);
- String content = makeRequest(fullUrl);
- return parseResponse(engine, content);
+ String url = engine.getFullUrl(path);
+ String content = makeRequest(url);
+ return engine.parseServerResponse(content);
}
- public String createFullUrl(OnlineRoutingEngine engine, List path) {
- StringBuilder sb = new StringBuilder(engine.getBaseUrl());
- String vehicle = engine.getVehicleKey();
- String apiKey = engine.getParameter(EngineParameter.API_KEY);
- switch (engine.getType()) {
-
- case GRAPHHOPPER:
- sb.append("?");
- for (LatLon point : path) {
- sb.append("point=")
- .append(point.getLatitude())
- .append(',')
- .append(point.getLongitude())
- .append('&');
- }
- sb.append("vehicle=").append(vehicle);
-
- if (!Algorithms.isEmpty(apiKey)) {
- sb.append('&').append("key=").append(apiKey);
- }
- break;
-
- case OSRM:
- sb.append(vehicle).append('/');
- for (int i = 0; i < path.size(); i++) {
- LatLon point = path.get(i);
- sb.append(point.getLongitude()).append(',').append(point.getLatitude());
- if (i < path.size() - 1) {
- sb.append(';');
- }
- }
- break;
-
- case ORS:
- if (path.size() > 1) {
- sb.append("driving-car").append('?'); // todo only for testing
- if (!Algorithms.isEmpty(apiKey)) {
- sb.append("api_key=").append(apiKey);
- }
- LatLon start = path.get(0);
- LatLon end = path.get(path.size() - 1);
- sb.append('&').append("start=")
- .append(start.getLatitude()).append(',').append(start.getLongitude());
- sb.append('&').append("end=")
- .append(end.getLatitude()).append(',').append(end.getLongitude());
- }
- break;
-
- }
- return sb.toString();
- }
-
- private List parseResponse(OnlineRoutingEngine engine, String content) throws JSONException {
- JSONObject obj = new JSONObject(content);
-
- switch (engine.getType()) {
-
- case GRAPHHOPPER:
- return GeoPolylineParserUtil.parse(
- obj.getJSONArray("paths").getJSONObject(0).getString("points"),
- GeoPolylineParserUtil.PRECISION_5);
-
- case OSRM:
- return GeoPolylineParserUtil.parse(
- obj.getJSONArray("routes").getJSONObject(0).getString("geometry"),
- GeoPolylineParserUtil.PRECISION_5);
-
- case ORS:
- JSONArray array = obj.getJSONArray("features").getJSONObject(0)
- .getJSONObject("geometry").getJSONArray("coordinates");
- List track = new ArrayList<>();
- for (int i = 0; i < array.length(); i++) {
- JSONArray point = array.getJSONArray(i);
- double lat = Double.parseDouble(point.getString(0));
- double lon = Double.parseDouble(point.getString(1));
- track.add(new LatLon(lat, lon));
- }
- return track;
- }
- return new ArrayList<>();
- }
-
- private String makeRequest(String url) throws IOException {
- URLConnection connection = NetworkUtils.getHttpURLConnection(url);
+ @NonNull
+ public String makeRequest(@NonNull String url) throws IOException {
+ HttpURLConnection connection = NetworkUtils.getHttpURLConnection(url);
connection.setRequestProperty("User-Agent", Version.getFullVersion(app));
StringBuilder content = new StringBuilder();
- BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ BufferedReader reader;
+ if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ } else {
+ reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
+ }
String s;
while ((s = reader.readLine()) != null) {
content.append(s);
@@ -160,32 +103,49 @@ public class OnlineRoutingHelper {
}
public void saveEngine(@NonNull OnlineRoutingEngine engine) {
- String stringKey = engine.getStringKey();
- cachedEngines.put(stringKey, engine);
- saveToSettings();
- }
-
- public void deleteEngine(@NonNull String stringKey) {
- OnlineRoutingEngine engine = getEngineByKey(stringKey);
- if (engine != null) {
- deleteEngine(engine);
- }
+ deleteInaccessibleParameters(engine);
+ String key = createEngineKeyIfNeeded(engine);
+ cachedEngines.put(key, engine);
+ saveCacheToSettings();
}
public void deleteEngine(@NonNull OnlineRoutingEngine engine) {
String stringKey = engine.getStringKey();
- if (cachedEngines.containsKey(stringKey)) {
+ deleteEngine(stringKey);
+ }
+
+ public void deleteEngine(@Nullable String stringKey) {
+ if (stringKey != null) {
cachedEngines.remove(stringKey);
- saveToSettings();
+ saveCacheToSettings();
}
}
- private void loadFromSettings() {
+ private void deleteInaccessibleParameters(@NonNull OnlineRoutingEngine engine) {
+ for (EngineParameter key : EngineParameter.values()) {
+ if (!engine.isParameterAllowed(key)) {
+ engine.remove(key);
+ }
+ }
+ }
+
+ @NonNull
+ private String createEngineKeyIfNeeded(@NonNull OnlineRoutingEngine engine) {
+ String key = engine.get(EngineParameter.KEY);
+ if (Algorithms.isEmpty(key)) {
+ key = OnlineRoutingEngine.generateKey();
+ engine.put(EngineParameter.KEY, key);
+ }
+ return key;
+ }
+
+ @NonNull
+ private Map loadSavedEngines() {
Map cachedEngines = new LinkedHashMap<>();
for (OnlineRoutingEngine engine : readFromSettings()) {
cachedEngines.put(engine.getStringKey(), engine);
}
- this.cachedEngines = cachedEngines;
+ return cachedEngines;
}
@NonNull
@@ -196,14 +156,14 @@ public class OnlineRoutingHelper {
try {
JSONObject json = new JSONObject(jsonString);
readFromJson(json, engines);
- } catch (JSONException e) {
+ } catch (JSONException | IllegalArgumentException e) {
LOG.debug("Error when reading engines from JSON ", e);
}
}
return engines;
}
- private void saveToSettings() {
+ private void saveCacheToSettings() {
if (!Algorithms.isEmpty(cachedEngines)) {
try {
JSONObject json = new JSONObject();
@@ -217,38 +177,44 @@ public class OnlineRoutingHelper {
}
}
- public static void readFromJson(JSONObject json, List engines) throws JSONException {
+ public static void readFromJson(@NonNull JSONObject json,
+ @NonNull List engines) throws JSONException {
if (!json.has("items")) {
return;
}
Gson gson = new Gson();
- Type type = new TypeToken>() {
+ Type typeToken = new TypeToken>() {
}.getType();
- JSONArray itemsJson = json.getJSONArray("items");
+ JSONArray itemsJson = json.getJSONArray(ITEMS);
for (int i = 0; i < itemsJson.length(); i++) {
JSONObject object = itemsJson.getJSONObject(i);
- String key = object.getString("key");
- String vehicleKey = object.getString("vehicle");
- EngineType engineType = EngineType.valueOf(object.getString("type"));
- String paramsString = object.getString("params");
- HashMap params = gson.fromJson(paramsString, type);
- engines.add(new OnlineRoutingEngine(key, engineType, vehicleKey, params));
+ if (object.has(TYPE) && object.has(PARAMS)) {
+ EngineType type = EngineType.getTypeByName(object.getString(TYPE));
+ String paramsString = object.getString(PARAMS);
+ HashMap params = gson.fromJson(paramsString, typeToken);
+ OnlineRoutingEngine engine = OnlineRoutingFactory.createEngine(type, params);
+ if (!Algorithms.isEmpty(engine.getStringKey())) {
+ engines.add(engine);
+ }
+ }
}
}
- public static void writeToJson(JSONObject json, List engines) throws JSONException {
+ public static void writeToJson(@NonNull JSONObject json,
+ @NonNull List engines) throws JSONException {
JSONArray jsonArray = new JSONArray();
Gson gson = new Gson();
Type type = new TypeToken>() {
}.getType();
for (OnlineRoutingEngine engine : engines) {
+ if (Algorithms.isEmpty(engine.getStringKey())) {
+ continue;
+ }
JSONObject jsonObject = new JSONObject();
- jsonObject.put("key", engine.getStringKey());
- jsonObject.put("type", engine.getType().name());
- jsonObject.put("vehicle", engine.getVehicleKey());
- jsonObject.put("params", gson.toJson(engine.getParams(), type));
+ jsonObject.put(TYPE, engine.getType().name());
+ jsonObject.put(PARAMS, gson.toJson(engine.getParams(), type));
jsonArray.put(jsonObject);
}
- json.put("items", jsonArray);
+ json.put(ITEMS, jsonArray);
}
}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/VehicleType.java b/OsmAnd/src/net/osmand/plus/onlinerouting/VehicleType.java
index 3e3fea6660..9936f8efbf 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/VehicleType.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/VehicleType.java
@@ -3,48 +3,26 @@ package net.osmand.plus.onlinerouting;
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
-import net.osmand.plus.R;
-import net.osmand.util.Algorithms;
+public class VehicleType {
+ private final String key;
+ @StringRes
+ private final int titleId;
-public enum VehicleType {
- CAR("car", R.string.routing_engine_vehicle_type_car),
- BIKE("bike", R.string.routing_engine_vehicle_type_bike),
- FOOT("foot", R.string.routing_engine_vehicle_type_foot),
- DRIVING("driving", R.string.routing_engine_vehicle_type_driving),
- CUSTOM("", R.string.shared_string_custom);
-
- VehicleType(String key, int titleId) {
+ public VehicleType(@NonNull String key,
+ @StringRes int titleId) {
this.key = key;
this.titleId = titleId;
}
- private String key;
- private int titleId;
-
+ @NonNull
public String getKey() {
return key;
}
- public String getTitle(Context ctx) {
+ @NonNull
+ public String getTitle(@NonNull Context ctx) {
return ctx.getString(titleId);
}
-
- public static String toHumanString(@NonNull Context ctx,
- @NonNull String key) {
- VehicleType vehicleType = getVehicleByKey(key);
- if (vehicleType == CUSTOM) {
- return Algorithms.capitalizeFirstLetter(key);
- }
- return vehicleType.getTitle(ctx);
- }
-
- public static VehicleType getVehicleByKey(String key) {
- for (VehicleType v : values()) {
- if (Algorithms.objectEquals(v.getKey(), key)) {
- return v;
- }
- }
- return CUSTOM;
- }
-}
\ No newline at end of file
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/EngineType.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/EngineType.java
new file mode 100644
index 0000000000..431ad33a3d
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/EngineType.java
@@ -0,0 +1,34 @@
+package net.osmand.plus.onlinerouting.engine;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.util.Algorithms;
+
+public enum EngineType {
+ GRAPHHOPPER("Graphhopper"),
+ OSRM("OSRM"),
+ ORS("Openroute Service");
+
+ private final String title;
+
+ EngineType(String title) {
+ this.title = title;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @NonNull
+ public static EngineType getTypeByName(@Nullable String name) {
+ if (!Algorithms.isEmpty(name)) {
+ for (EngineType type : values()) {
+ if (type.name().equals(name)) {
+ return type;
+ }
+ }
+ }
+ return values()[0];
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/GraphhopperEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/GraphhopperEngine.java
new file mode 100644
index 0000000000..d676e1e154
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/GraphhopperEngine.java
@@ -0,0 +1,95 @@
+package net.osmand.plus.onlinerouting.engine;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.data.LatLon;
+import net.osmand.plus.R;
+import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.VehicleType;
+import net.osmand.util.GeoPolylineParserUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.List;
+import java.util.Map;
+
+import static net.osmand.util.Algorithms.isEmpty;
+
+public class GraphhopperEngine extends OnlineRoutingEngine {
+
+ public GraphhopperEngine(@Nullable Map params) {
+ super(params);
+ }
+
+ @Override
+ public @NonNull EngineType getType() {
+ return EngineType.GRAPHHOPPER;
+ }
+
+ @NonNull
+ @Override
+ public String getStandardUrl() {
+ return "https://graphhopper.com/api/1/route";
+ }
+
+ @Override
+ protected void collectAllowedParameters() {
+ allowParameters(EngineParameter.API_KEY);
+ }
+
+ @Override
+ protected void collectAllowedVehicles(@NonNull List vehicles) {
+ vehicles.add(new VehicleType("car", R.string.routing_engine_vehicle_type_car));
+ vehicles.add(new VehicleType("bike", R.string.routing_engine_vehicle_type_bike));
+ vehicles.add(new VehicleType("foot", R.string.routing_engine_vehicle_type_foot));
+ vehicles.add(new VehicleType("hike", R.string.routing_engine_vehicle_type_hiking));
+ vehicles.add(new VehicleType("mtb", R.string.routing_engine_vehicle_type_mtb));
+ vehicles.add(new VehicleType("racingbike", R.string.routing_engine_vehicle_type_racingbike));
+ vehicles.add(new VehicleType("scooter", R.string.routing_engine_vehicle_type_scooter));
+ vehicles.add(new VehicleType("truck", R.string.routing_engine_vehicle_type_truck));
+ vehicles.add(new VehicleType("small_truck", R.string.routing_engine_vehicle_type_small_truck));
+ }
+
+ @Override
+ protected void makeFullUrl(@NonNull StringBuilder sb,
+ @NonNull List path) {
+ sb.append("?");
+ for (LatLon point : path) {
+ sb.append("point=")
+ .append(point.getLatitude())
+ .append(',')
+ .append(point.getLongitude())
+ .append('&');
+ }
+ String vehicle = get(EngineParameter.VEHICLE_KEY);
+ if (isEmpty(vehicle)) {
+ sb.append("vehicle=").append(vehicle);
+ }
+ String apiKey = get(EngineParameter.API_KEY);
+ if (isEmpty(apiKey)) {
+ sb.append('&').append("key=").append(apiKey);
+ }
+ }
+
+ @NonNull
+ @Override
+ public List parseServerResponse(@NonNull String content) throws JSONException {
+ JSONObject obj = new JSONObject(content);
+ return GeoPolylineParserUtil.parse(
+ obj.getJSONArray("paths").getJSONObject(0).getString("points"),
+ GeoPolylineParserUtil.PRECISION_5);
+ }
+
+ @Override
+ public boolean parseServerMessage(@NonNull StringBuilder sb,
+ @NonNull String content) throws JSONException {
+ JSONObject obj = new JSONObject(content);
+ if (obj.has("message")) {
+ String message = obj.getString("message");
+ sb.append(message);
+ }
+ return obj.has("paths");
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OnlineRoutingEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OnlineRoutingEngine.java
new file mode 100644
index 0000000000..e3f2d416ce
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OnlineRoutingEngine.java
@@ -0,0 +1,205 @@
+package net.osmand.plus.onlinerouting.engine;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.data.LatLon;
+import net.osmand.plus.R;
+import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
+import net.osmand.plus.onlinerouting.VehicleType;
+import net.osmand.util.Algorithms;
+
+import org.json.JSONException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static net.osmand.util.Algorithms.isEmpty;
+
+public abstract class OnlineRoutingEngine implements Cloneable {
+
+ public final static String ONLINE_ROUTING_ENGINE_PREFIX = "online_routing_engine_";
+ public static final VehicleType CUSTOM_VEHICLE = new VehicleType("", R.string.shared_string_custom);
+
+ private final Map params = new HashMap<>();
+ private final List allowedVehicles = new ArrayList<>();
+ private final Set allowedParameters = new HashSet<>();
+
+ public OnlineRoutingEngine(@Nullable Map params) {
+ if (!isEmpty(params)) {
+ this.params.putAll(params);
+ }
+ collectAllowedVehiclesInternal();
+ collectAllowedParametersInternal();
+ }
+
+ @NonNull
+ public abstract EngineType getType();
+
+ @Nullable
+ public String getStringKey() {
+ return get(EngineParameter.KEY);
+ }
+
+ @NonNull
+ public String getName(@NonNull Context ctx) {
+ String customName = get(EngineParameter.CUSTOM_NAME);
+ if (customName != null) {
+ return customName;
+ } else {
+ return getStandardName(ctx);
+ }
+ }
+
+ @NonNull
+ private String getStandardName(@NonNull Context ctx) {
+ String base = getBaseName(ctx);
+ String index = get(EngineParameter.NAME_INDEX);
+ return !isEmpty(index) ? base + " " + index : base;
+ }
+
+ @NonNull
+ public String getBaseName(@NonNull Context ctx) {
+ String vehicleTitle = getSelectedVehicleName(ctx);
+ if (isEmpty(vehicleTitle)) {
+ return getType().getTitle();
+ } else {
+ String pattern = ctx.getString(R.string.ltr_or_rtl_combine_via_dash);
+ return String.format(pattern, getType().getTitle(), vehicleTitle);
+ }
+ }
+
+ @NonNull
+ public String getBaseUrl() {
+ String customUrl = get(EngineParameter.CUSTOM_URL);
+ if (isEmpty(customUrl)) {
+ return getStandardUrl();
+ }
+ return customUrl;
+ }
+
+ @NonNull
+ public String getFullUrl(@NonNull List path) {
+ StringBuilder sb = new StringBuilder(getBaseUrl());
+ makeFullUrl(sb, path);
+ return sb.toString();
+ }
+
+ protected abstract void makeFullUrl(@NonNull StringBuilder sb,
+ @NonNull List path);
+
+ @NonNull
+ public abstract List parseServerResponse(@NonNull String content) throws JSONException;
+
+ @NonNull
+ public abstract String getStandardUrl();
+
+ @NonNull
+ public Map getParams() {
+ return params;
+ }
+
+ @Nullable
+ public String get(@NonNull EngineParameter key) {
+ return params.get(key.name());
+ }
+
+ public void put(@NonNull EngineParameter key, @NonNull String value) {
+ params.put(key.name(), value);
+ }
+
+ public void remove(@NonNull EngineParameter key) {
+ params.remove(key.name());
+ }
+
+ private void collectAllowedVehiclesInternal() {
+ allowedVehicles.clear();
+ collectAllowedVehicles(allowedVehicles);
+ allowedVehicles.add(CUSTOM_VEHICLE);
+ }
+
+ protected abstract void collectAllowedVehicles(@NonNull List vehicles);
+
+ @NonNull
+ public List getAllowedVehicles() {
+ return Collections.unmodifiableList(allowedVehicles);
+ }
+
+ private void collectAllowedParametersInternal() {
+ allowedParameters.clear();
+ allowParameters(EngineParameter.KEY, EngineParameter.VEHICLE_KEY,
+ EngineParameter.CUSTOM_NAME, EngineParameter.NAME_INDEX, EngineParameter.CUSTOM_URL);
+ collectAllowedParameters();
+ }
+
+ protected abstract void collectAllowedParameters();
+
+ public boolean isParameterAllowed(EngineParameter key) {
+ return allowedParameters.contains(key);
+ }
+
+ protected void allowParameters(@NonNull EngineParameter ... allowedParams) {
+ allowedParameters.addAll(Arrays.asList(allowedParams));
+ }
+
+ @Nullable
+ private String getSelectedVehicleName(@NonNull Context ctx) {
+ String key = get(EngineParameter.VEHICLE_KEY);
+ VehicleType vt = getVehicleTypeByKey(key);
+ if (!vt.equals(CUSTOM_VEHICLE)) {
+ return vt.getTitle(ctx);
+ }
+ return key != null ? Algorithms.capitalizeFirstLetter(key) : null;
+ }
+
+ @NonNull
+ public VehicleType getSelectedVehicleType() {
+ String key = get(EngineParameter.VEHICLE_KEY);
+ return getVehicleTypeByKey(key);
+ }
+
+ @NonNull
+ public VehicleType getVehicleTypeByKey(@Nullable String vehicleKey) {
+ if (!isEmpty(vehicleKey)) {
+ for (VehicleType vt : allowedVehicles) {
+ if (Algorithms.objectEquals(vt.getKey(), vehicleKey)) {
+ return vt;
+ }
+ }
+ }
+ return CUSTOM_VEHICLE;
+ }
+
+ public abstract boolean parseServerMessage(@NonNull StringBuilder sb,
+ @NonNull String content) throws JSONException;
+
+ @NonNull
+ @Override
+ public Object clone() {
+ return OnlineRoutingFactory.createEngine(getType(), getParams());
+ }
+
+ @NonNull
+ public static String generateKey() {
+ return ONLINE_ROUTING_ENGINE_PREFIX + System.currentTimeMillis();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OnlineRoutingEngine)) return false;
+
+ OnlineRoutingEngine engine = (OnlineRoutingEngine) o;
+ if (getType() != engine.getType()) return false;
+ return Algorithms.objectEquals(getParams(), engine.getParams());
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OrsEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OrsEngine.java
new file mode 100644
index 0000000000..7c57737d46
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OrsEngine.java
@@ -0,0 +1,104 @@
+package net.osmand.plus.onlinerouting.engine;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.data.LatLon;
+import net.osmand.plus.R;
+import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.VehicleType;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static net.osmand.util.Algorithms.isEmpty;
+
+public class OrsEngine extends OnlineRoutingEngine {
+
+ public OrsEngine(@Nullable Map params) {
+ super(params);
+ }
+
+ @Override
+ public @NonNull EngineType getType() {
+ return EngineType.ORS;
+ }
+
+ @NonNull
+ @Override
+ public String getStandardUrl() {
+ return "https://api.openrouteservice.org/v2/directions/";
+ }
+
+ @Override
+ protected void collectAllowedParameters() {
+ allowParameters(EngineParameter.API_KEY);
+ }
+
+ @Override
+ protected void collectAllowedVehicles(@NonNull List vehicles) {
+ vehicles.add(new VehicleType("driving-car", R.string.routing_engine_vehicle_type_car));
+ vehicles.add(new VehicleType("driving-hgv", R.string.routing_engine_vehicle_type_hgv));
+ vehicles.add(new VehicleType("cycling-regular", R.string.routing_engine_vehicle_type_cycling_regular));
+ vehicles.add(new VehicleType("cycling-road", R.string.routing_engine_vehicle_type_cycling_road));
+ vehicles.add(new VehicleType("cycling-mountain", R.string.routing_engine_vehicle_type_cycling_mountain));
+ vehicles.add(new VehicleType("cycling-electric", R.string.routing_engine_vehicle_type_cycling_electric));
+ vehicles.add(new VehicleType("foot-walking", R.string.routing_engine_vehicle_type_walking));
+ vehicles.add(new VehicleType("foot-hiking", R.string.routing_engine_vehicle_type_hiking));
+ vehicles.add(new VehicleType("wheelchair", R.string.routing_engine_vehicle_type_wheelchair));
+ }
+
+ @Override
+ protected void makeFullUrl(@NonNull StringBuilder sb,
+ @NonNull List path) {
+ if (path.size() > 1) {
+ String vehicleKey = get(EngineParameter.VEHICLE_KEY);
+ if (!isEmpty(vehicleKey)) {
+ sb.append(vehicleKey);
+ }
+ sb.append('?');
+ String apiKey = get(EngineParameter.API_KEY);
+ if (!isEmpty(apiKey)) {
+ sb.append("api_key=").append(apiKey);
+ }
+ LatLon start = path.get(0);
+ LatLon end = path.get(path.size() - 1);
+ sb.append('&').append("start=")
+ .append(start.getLongitude()).append(',').append(start.getLatitude());
+ sb.append('&').append("end=")
+ .append(end.getLongitude()).append(',').append(end.getLatitude());
+ }
+ }
+
+ @NonNull
+ @Override
+ public List parseServerResponse(@NonNull String content) throws JSONException {
+ JSONObject obj = new JSONObject(content);
+ JSONArray array = obj.getJSONArray("features").getJSONObject(0)
+ .getJSONObject("geometry").getJSONArray("coordinates");
+ List track = new ArrayList<>();
+ for (int i = 0; i < array.length(); i++) {
+ JSONArray point = array.getJSONArray(i);
+ double lon = Double.parseDouble(point.getString(0));
+ double lat = Double.parseDouble(point.getString(1));
+ track.add(new LatLon(lat, lon));
+ }
+ return track;
+ }
+
+ @Override
+ public boolean parseServerMessage(@NonNull StringBuilder sb,
+ @NonNull String content) throws JSONException {
+ JSONObject obj = new JSONObject(content);
+ if (obj.has("error")) {
+ String message = obj.getString("error");
+ sb.append(message);
+ }
+ return obj.has("features");
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OsrmEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OsrmEngine.java
new file mode 100644
index 0000000000..1ef9c1a622
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OsrmEngine.java
@@ -0,0 +1,84 @@
+package net.osmand.plus.onlinerouting.engine;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.data.LatLon;
+import net.osmand.plus.R;
+import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.VehicleType;
+import net.osmand.util.GeoPolylineParserUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.List;
+import java.util.Map;
+
+import static net.osmand.util.Algorithms.isEmpty;
+
+public class OsrmEngine extends OnlineRoutingEngine {
+
+ public OsrmEngine(@Nullable Map params) {
+ super(params);
+ }
+
+ @Override
+ public @NonNull EngineType getType() {
+ return EngineType.OSRM;
+ }
+
+ @NonNull
+ @Override
+ public String getStandardUrl() {
+ return "https://router.project-osrm.org/route/v1/";
+ }
+
+ @Override
+ protected void collectAllowedParameters() { }
+
+ @Override
+ protected void collectAllowedVehicles(@NonNull List vehicles) {
+ vehicles.add(new VehicleType("car", R.string.routing_engine_vehicle_type_car));
+ vehicles.add(new VehicleType("bike", R.string.routing_engine_vehicle_type_bike));
+ vehicles.add(new VehicleType("foot", R.string.routing_engine_vehicle_type_foot));
+ }
+
+ @Override
+ protected void makeFullUrl(@NonNull StringBuilder sb,
+ @NonNull List path) {
+ String vehicleKey = get(EngineParameter.VEHICLE_KEY);
+ if (!isEmpty(vehicleKey)) {
+ sb.append(vehicleKey).append('/');
+ }
+ for (int i = 0; i < path.size(); i++) {
+ LatLon point = path.get(i);
+ sb.append(point.getLongitude()).append(',').append(point.getLatitude());
+ if (i < path.size() - 1) {
+ sb.append(';');
+ }
+ }
+ sb.append('?');
+ sb.append("overview=full");
+ }
+
+ @NonNull
+ @Override
+ public List parseServerResponse(@NonNull String content) throws JSONException {
+ JSONObject obj = new JSONObject(content);
+ return GeoPolylineParserUtil.parse(
+ obj.getJSONArray("routes").getJSONObject(0).getString("geometry"),
+ GeoPolylineParserUtil.PRECISION_5);
+ }
+
+ @Override
+ public boolean parseServerMessage(@NonNull StringBuilder sb,
+ @NonNull String content) throws JSONException {
+ JSONObject obj = new JSONObject(content);
+ if (obj.has("message")) {
+ String message = obj.getString("message");
+ sb.append(message);
+ }
+ return obj.has("routes");
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/ui/ExampleLocation.java b/OsmAnd/src/net/osmand/plus/onlinerouting/ui/ExampleLocation.java
new file mode 100644
index 0000000000..0a384002f7
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/ui/ExampleLocation.java
@@ -0,0 +1,52 @@
+package net.osmand.plus.onlinerouting.ui;
+
+import androidx.annotation.NonNull;
+
+import net.osmand.data.LatLon;
+
+public enum ExampleLocation {
+
+ AMSTERDAM("Amsterdam",
+ new LatLon(52.379189, 4.899431),
+ new LatLon(52.308056, 4.764167)),
+
+ BERLIN("Berlin",
+ new LatLon(52.520008, 13.404954),
+ new LatLon(52.3666652, 13.501997992)),
+
+ NEW_YORK("New York",
+ new LatLon(43.000000, -75.000000),
+ new LatLon(40.641766, -73.780968)),
+
+ PARIS("Paris",
+ new LatLon(48.864716, 2.349014),
+ new LatLon(48.948437, 2.434931));
+
+ ExampleLocation(@NonNull String name,
+ @NonNull LatLon cityCenterLatLon,
+ @NonNull LatLon cityAirportLatLon) {
+ this.name = name;
+ this.cityCenterLatLon = cityCenterLatLon;
+ this.cityAirportLatLon = cityAirportLatLon;
+ }
+
+ private String name;
+ private LatLon cityCenterLatLon;
+ private LatLon cityAirportLatLon;
+
+ @NonNull
+ public String getName() {
+ return name;
+ }
+
+ @NonNull
+ public LatLon getCityCenterLatLon() {
+ return cityCenterLatLon;
+ }
+
+ @NonNull
+ public LatLon getCityAirportLatLon() {
+ return cityAirportLatLon;
+ }
+
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingCard.java b/OsmAnd/src/net/osmand/plus/onlinerouting/ui/OnlineRoutingCard.java
similarity index 69%
rename from OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingCard.java
rename to OsmAnd/src/net/osmand/plus/onlinerouting/ui/OnlineRoutingCard.java
index 1b9e7f3b29..4403d512f4 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingCard.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/ui/OnlineRoutingCard.java
@@ -1,14 +1,17 @@
-package net.osmand.plus.onlinerouting;
+package net.osmand.plus.onlinerouting.ui;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -22,7 +25,10 @@ import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter;
import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionAdapterListener;
import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem;
+import net.osmand.plus.onlinerouting.VehicleType;
+import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
+import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.widgets.OsmandTextFieldBoxes;
import java.util.List;
@@ -39,13 +45,18 @@ public class OnlineRoutingCard extends BaseCard {
private OsmandTextFieldBoxes textFieldBoxes;
private EditText editText;
private TextView tvHelperText;
+ private TextView tvErrorText;
private View bottomDivider;
private View button;
private OnTextChangedListener onTextChangedListener;
+ private boolean fieldBoxHelperTextShowed;
- public OnlineRoutingCard(@NonNull MapActivity mapActivity, boolean nightMode) {
+ private ApplicationMode appMode;
+
+ public OnlineRoutingCard(@NonNull MapActivity mapActivity, boolean nightMode, ApplicationMode appMode) {
super(mapActivity);
this.nightMode = nightMode;
+ this.appMode = appMode;
}
@Override
@@ -64,9 +75,13 @@ public class OnlineRoutingCard extends BaseCard {
textFieldBoxes = view.findViewById(R.id.field_box);
editText = view.findViewById(R.id.edit_text);
tvHelperText = view.findViewById(R.id.helper_text);
+ tvErrorText = view.findViewById(R.id.error_text);
bottomDivider = view.findViewById(R.id.bottom_divider);
button = view.findViewById(R.id.button);
+ int activeColor = ContextCompat.getColor(app, appMode.getIconColorInfo().getColor(nightMode));
+ textFieldBoxes.setPrimaryColor(activeColor);
+
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -80,7 +95,7 @@ public class OnlineRoutingCard extends BaseCard {
public void afterTextChanged(Editable s) {
if (onTextChangedListener != null) {
boolean editedByUser = editText.getTag() == null;
- String text = editText.getText().toString();
+ String text = editText.getText().toString().trim();
onTextChangedListener.onTextChanged(editedByUser, text);
}
}
@@ -107,9 +122,9 @@ public class OnlineRoutingCard extends BaseCard {
tvHeaderSubtitle.setText(subtitle);
}
- public void setSelectionMenu(List items,
- String selectedItemTitle,
- final CallbackWithObject callback) {
+ public void setSelectionMenu(@NonNull List items,
+ @NonNull String selectedItemTitle,
+ @NonNull final CallbackWithObject callback) {
showElements(rvSelectionMenu);
rvSelectionMenu.setLayoutManager(
new LinearLayoutManager(app, RecyclerView.HORIZONTAL, false));
@@ -122,11 +137,25 @@ public class OnlineRoutingCard extends BaseCard {
if (callback.processResult(item)) {
adapter.setSelectedItem(item);
}
+ Object obj = item.getObject();
+ updateBottomMarginSelectionMenu(obj);
}
});
+ Object item = adapter.getItemByTitle(selectedItemTitle).getObject();
+ updateBottomMarginSelectionMenu(item);
rvSelectionMenu.setAdapter(adapter);
}
+ private void updateBottomMarginSelectionMenu(Object item) {
+ if (item instanceof VehicleType) {
+ VehicleType vt = (VehicleType) item;
+ boolean hasPadding = vt.equals(OnlineRoutingEngine.CUSTOM_VEHICLE);
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) rvSelectionMenu.getLayoutParams();
+ int contentPadding = app.getResources().getDimensionPixelSize(R.dimen.content_padding);
+ params.bottomMargin = hasPadding ? contentPadding : 0;
+ }
+ }
+
public void setDescription(@NonNull String description) {
showElements(tvDescription);
tvDescription.setText(description);
@@ -139,15 +168,31 @@ public class OnlineRoutingCard extends BaseCard {
public void setFieldBoxHelperText(@NonNull String helperText) {
showElements(fieldBoxContainer, tvHelperText);
+ fieldBoxHelperTextShowed = true;
tvHelperText.setText(helperText);
}
+ public void showFieldBoxError(@NonNull String errorText) {
+ showElements(fieldBoxContainer, tvErrorText);
+ hideElements(tvHelperText);
+ tvErrorText.setText(errorText);
+ }
+
+ public void hideFieldBoxError() {
+ hideElements(tvErrorText);
+ if (fieldBoxHelperTextShowed) {
+ showElements(tvHelperText);
+ }
+ }
+
public void setEditedText(@NonNull String text) {
- editText.setTag(""); // needed to indicate that the text was edited programmatically
+ showElements(fieldBoxContainer);
+ editText.setTag(""); // indicate that the text was edited programmatically
editText.setText(text);
editText.setTag(null);
}
+ @NonNull
public String getEditedText() {
return editText.getText().toString();
}
@@ -156,7 +201,8 @@ public class OnlineRoutingCard extends BaseCard {
showElements(bottomDivider);
}
- public void setButton(String title, OnClickListener listener) {
+ public void setButton(@NonNull String title,
+ @NonNull OnClickListener listener) {
showElements(button);
button.setOnClickListener(listener);
UiUtilities.setupDialogButton(nightMode, button, DialogButtonType.PRIMARY, title);
@@ -186,11 +232,11 @@ public class OnlineRoutingCard extends BaseCard {
AndroidUiHelper.setVisibility(View.GONE, views);
}
- public void setOnTextChangedListener(OnTextChangedListener onTextChangedListener) {
+ public void setOnTextChangedListener(@Nullable OnTextChangedListener onTextChangedListener) {
this.onTextChangedListener = onTextChangedListener;
}
public interface OnTextChangedListener {
- void onTextChanged(boolean editedByUser, String text);
+ void onTextChanged(boolean editedByUser, @NonNull String text);
}
}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/ui/OnlineRoutingEngineFragment.java b/OsmAnd/src/net/osmand/plus/onlinerouting/ui/OnlineRoutingEngineFragment.java
new file mode 100644
index 0000000000..4c0bca61a6
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/ui/OnlineRoutingEngineFragment.java
@@ -0,0 +1,721 @@
+package net.osmand.plus.onlinerouting.ui;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.activity.OnBackPressedCallback;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+import net.osmand.AndroidUtils;
+import net.osmand.CallbackWithObject;
+import net.osmand.data.LatLon;
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.R;
+import net.osmand.plus.UiUtilities;
+import net.osmand.plus.UiUtilities.DialogButtonType;
+import net.osmand.plus.activities.MapActivity;
+import net.osmand.plus.base.BaseOsmAndFragment;
+import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem;
+import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
+import net.osmand.plus.onlinerouting.OnlineRoutingHelper;
+import net.osmand.plus.onlinerouting.VehicleType;
+import net.osmand.plus.onlinerouting.ui.OnlineRoutingCard.OnTextChangedListener;
+import net.osmand.plus.onlinerouting.engine.EngineType;
+import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
+import net.osmand.plus.routepreparationmenu.cards.BaseCard;
+import net.osmand.plus.settings.backend.ApplicationMode;
+import net.osmand.util.Algorithms;
+
+import org.json.JSONException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine.CUSTOM_VEHICLE;
+
+public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
+
+ public static final String TAG = OnlineRoutingEngineFragment.class.getSimpleName();
+
+ private static final String ENGINE_TYPE_KEY = "engine_type";
+ private static final String ENGINE_CUSTOM_VEHICLE_KEY = "engine_custom_vehicle";
+ private static final String EXAMPLE_LOCATION_KEY = "example_location";
+ private static final String APP_MODE_KEY = "app_mode";
+ private static final String EDITED_ENGINE_KEY = "edited_engine_key";
+
+ private OsmandApplication app;
+ private ApplicationMode appMode;
+ private MapActivity mapActivity;
+ private OnlineRoutingHelper helper;
+
+ private View view;
+ private ViewGroup segmentsContainer;
+ private OnlineRoutingCard nameCard;
+ private OnlineRoutingCard typeCard;
+ private OnlineRoutingCard vehicleCard;
+ private OnlineRoutingCard apiKeyCard;
+ private OnlineRoutingCard exampleCard;
+ private View testResultsContainer;
+ private View saveButton;
+ private ScrollView scrollView;
+ private OnGlobalLayoutListener onGlobalLayout;
+ private boolean isKeyboardShown = false;
+
+ private OnlineRoutingEngine engine;
+ private OnlineRoutingEngine initEngine;
+ private String customVehicleKey;
+ private ExampleLocation selectedLocation;
+ private String editedEngineKey;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ app = requireMyApplication();
+ mapActivity = getMapActivity();
+ helper = app.getOnlineRoutingHelper();
+ if (savedInstanceState != null) {
+ restoreState(savedInstanceState);
+ } else {
+ initState();
+ }
+ requireMyActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
+ public void handleOnBackPressed() {
+ MapActivity mapActivity = getMapActivity();
+ if (mapActivity != null) {
+ showExitDialog();
+ }
+ }
+ });
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ view = getInflater().inflate(
+ R.layout.online_routing_engine_fragment, container, false);
+ segmentsContainer = (ViewGroup) view.findViewById(R.id.segments_container);
+ scrollView = (ScrollView) segmentsContainer.getParent();
+ if (Build.VERSION.SDK_INT >= 21) {
+ AndroidUtils.addStatusBarPadding21v(getContext(), view);
+ }
+ setupToolbar((Toolbar) view.findViewById(R.id.toolbar));
+
+ setupNameCard();
+ setupTypeCard();
+ setupVehicleCard();
+ setupApiKeyCard();
+ setupExampleCard();
+ setupResultsContainer();
+ setupButtons();
+
+ generateUniqueNameIfNeeded();
+ updateCardViews(nameCard, typeCard, vehicleCard, exampleCard);
+
+ scrollView.setOnTouchListener(new View.OnTouchListener() {
+ int scrollViewY = 0;
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ int y = scrollView.getScrollY();
+ if (isKeyboardShown && scrollViewY != y) {
+ scrollViewY = y;
+ View focus = mapActivity.getCurrentFocus();
+ if (focus != null) {
+ AndroidUtils.hideSoftKeyboard(mapActivity, focus);
+ focus.clearFocus();
+ }
+ }
+ return false;
+ }
+ });
+
+ onGlobalLayout = new ViewTreeObserver.OnGlobalLayoutListener() {
+ private int layoutHeightPrevious;
+ private int layoutHeightMin;
+
+ @Override
+ public void onGlobalLayout() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ } else {
+ view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ }
+
+ Rect visibleDisplayFrame = new Rect();
+ view.getWindowVisibleDisplayFrame(visibleDisplayFrame);
+ int layoutHeight = visibleDisplayFrame.bottom;
+
+ if (layoutHeight < layoutHeightPrevious) {
+ isKeyboardShown = true;
+ layoutHeightMin = layoutHeight;
+ } else {
+ isKeyboardShown = layoutHeight == layoutHeightMin;
+ }
+
+ if (layoutHeight != layoutHeightPrevious) {
+ FrameLayout.LayoutParams rootViewLayout = (FrameLayout.LayoutParams) view.getLayoutParams();
+ rootViewLayout.height = layoutHeight;
+ view.requestLayout();
+ layoutHeightPrevious = layoutHeight;
+ }
+
+ view.post(new Runnable() {
+ @Override
+ public void run() {
+ view.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayout);
+ }
+ });
+
+ }
+ };
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ view.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayout);
+ }
+
+ return view;
+ }
+
+ private void setupToolbar(Toolbar toolbar) {
+ ImageView navigationIcon = toolbar.findViewById(R.id.close_button);
+ navigationIcon.setImageResource(R.drawable.ic_action_close);
+ navigationIcon.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showExitDialog();
+ }
+ });
+ TextView title = toolbar.findViewById(R.id.toolbar_title);
+ toolbar.findViewById(R.id.toolbar_subtitle).setVisibility(View.GONE);
+ View actionBtn = toolbar.findViewById(R.id.action_button);
+ if (isEditingMode()) {
+ title.setText(getString(R.string.edit_online_routing_engine));
+ ImageView ivBtn = toolbar.findViewById(R.id.action_button_icon);
+ ivBtn.setImageDrawable(
+ getIcon(R.drawable.ic_action_delete_dark, R.color.color_osm_edit_delete));
+ actionBtn.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onDeleteEngine();
+ dismiss();
+ }
+ });
+ } else {
+ title.setText(getString(R.string.add_online_routing_engine));
+ actionBtn.setVisibility(View.GONE);
+ }
+ }
+
+ private void setupNameCard() {
+ nameCard = new OnlineRoutingCard(mapActivity, isNightMode(), appMode);
+ nameCard.build(mapActivity);
+ nameCard.setDescription(getString(R.string.select_nav_profile_dialog_message));
+ nameCard.setEditedText(engine.getName(app));
+ nameCard.setFieldBoxLabelText(getString(R.string.shared_string_name));
+ nameCard.setOnTextChangedListener(new OnTextChangedListener() {
+ @Override
+ public void onTextChanged(boolean changedByUser, @NonNull String text) {
+ if (changedByUser) {
+ engine.put(EngineParameter.CUSTOM_NAME, text);
+ checkCustomNameUnique(engine);
+ }
+ }
+ });
+ nameCard.showDivider();
+ segmentsContainer.addView(nameCard.getView());
+ }
+
+ private void setupTypeCard() {
+ typeCard = new OnlineRoutingCard(mapActivity, isNightMode(), appMode);
+ typeCard.build(mapActivity);
+ typeCard.setHeaderTitle(getString(R.string.shared_string_type));
+ List serverItems = new ArrayList<>();
+ for (EngineType server : EngineType.values()) {
+ serverItems.add(new HorizontalSelectionItem(server.getTitle(), server));
+ }
+ typeCard.setSelectionMenu(serverItems, engine.getType().getTitle(),
+ new CallbackWithObject() {
+ @Override
+ public boolean processResult(HorizontalSelectionItem result) {
+ EngineType type = (EngineType) result.getObject();
+ if (engine.getType() != type) {
+ changeEngineType(type);
+ return true;
+ }
+ return false;
+ }
+ });
+ typeCard.setOnTextChangedListener(new OnTextChangedListener() {
+ @Override
+ public void onTextChanged(boolean editedByUser, @NonNull String text) {
+ if (editedByUser) {
+ engine.put(EngineParameter.CUSTOM_URL, text);
+ updateCardViews(exampleCard);
+ }
+ }
+ });
+ typeCard.setFieldBoxLabelText(getString(R.string.shared_string_server_url));
+ typeCard.showDivider();
+ segmentsContainer.addView(typeCard.getView());
+ }
+
+ private void setupVehicleCard() {
+ vehicleCard = new OnlineRoutingCard(mapActivity, isNightMode(), appMode);
+ vehicleCard.build(mapActivity);
+ vehicleCard.setHeaderTitle(getString(R.string.shared_string_vehicle));
+ vehicleCard.setFieldBoxLabelText(getString(R.string.shared_string_custom));
+ vehicleCard.setOnTextChangedListener(new OnTextChangedListener() {
+ @Override
+ public void onTextChanged(boolean editedByUser, @NonNull String text) {
+ if (editedByUser) {
+ customVehicleKey = text;
+ engine.put(EngineParameter.VEHICLE_KEY, customVehicleKey);
+ updateCardViews(nameCard, exampleCard);
+ }
+ }
+ });
+ vehicleCard.setEditedText(customVehicleKey);
+ vehicleCard.setFieldBoxHelperText(getString(R.string.shared_string_enter_param));
+ vehicleCard.showDivider();
+ segmentsContainer.addView(vehicleCard.getView());
+ setupVehicleTypes();
+ }
+
+ private void setupVehicleTypes() {
+ List vehicleItems = new ArrayList<>();
+ for (VehicleType vehicle : engine.getAllowedVehicles()) {
+ vehicleItems.add(new HorizontalSelectionItem(vehicle.getTitle(app), vehicle));
+ }
+ vehicleCard.setSelectionMenu(vehicleItems, engine.getSelectedVehicleType().getTitle(app),
+ new CallbackWithObject() {
+ @Override
+ public boolean processResult(HorizontalSelectionItem result) {
+ VehicleType vehicle = (VehicleType) result.getObject();
+ if (!Algorithms.objectEquals(engine.getSelectedVehicleType(), vehicle)) {
+ String vehicleKey = vehicle.equals(CUSTOM_VEHICLE) ? customVehicleKey : vehicle.getKey();
+ engine.put(EngineParameter.VEHICLE_KEY, vehicleKey);
+ generateUniqueNameIfNeeded();
+ updateCardViews(nameCard, vehicleCard, exampleCard);
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ private void setupApiKeyCard() {
+ apiKeyCard = new OnlineRoutingCard(mapActivity, isNightMode(), appMode);
+ apiKeyCard.build(mapActivity);
+ apiKeyCard.setHeaderTitle(getString(R.string.shared_string_api_key));
+ apiKeyCard.setFieldBoxLabelText(getString(R.string.keep_it_empty_if_not));
+ String apiKey = engine.get(EngineParameter.API_KEY);
+ if (apiKey != null) {
+ apiKeyCard.setEditedText(apiKey);
+ }
+ apiKeyCard.showDivider();
+ apiKeyCard.setOnTextChangedListener(new OnTextChangedListener() {
+ @Override
+ public void onTextChanged(boolean editedByUser, @NonNull String text) {
+ if (Algorithms.isBlank(text)) {
+ engine.remove(EngineParameter.API_KEY);
+ } else {
+ engine.put(EngineParameter.API_KEY, text);
+ }
+ updateCardViews(exampleCard);
+ }
+ });
+ segmentsContainer.addView(apiKeyCard.getView());
+ }
+
+ private void setupExampleCard() {
+ exampleCard = new OnlineRoutingCard(mapActivity, isNightMode(), appMode);
+ exampleCard.build(mapActivity);
+ exampleCard.setHeaderTitle(getString(R.string.shared_string_example));
+ List locationItems = new ArrayList<>();
+ for (ExampleLocation location : ExampleLocation.values()) {
+ locationItems.add(new HorizontalSelectionItem(location.getName(), location));
+ }
+ exampleCard.setSelectionMenu(locationItems, selectedLocation.getName(),
+ new CallbackWithObject() {
+ @Override
+ public boolean processResult(HorizontalSelectionItem result) {
+ ExampleLocation location = (ExampleLocation) result.getObject();
+ if (selectedLocation != location) {
+ selectedLocation = location;
+ updateCardViews(exampleCard);
+ return true;
+ }
+ return false;
+ }
+ });
+ exampleCard.setDescription(getString(R.string.online_routing_example_hint));
+ exampleCard.showFieldBox();
+ exampleCard.setButton(getString(R.string.test_route_calculation), new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ testEngineWork();
+ }
+ });
+ segmentsContainer.addView(exampleCard.getView());
+ }
+
+ private void setupResultsContainer() {
+ testResultsContainer = getInflater().inflate(
+ R.layout.bottom_sheet_item_with_descr_64dp, segmentsContainer, false);
+ testResultsContainer.setVisibility(View.INVISIBLE);
+ segmentsContainer.addView(testResultsContainer);
+ }
+
+ private void setupButtons() {
+ boolean nightMode = isNightMode();
+ View cancelButton = view.findViewById(R.id.dismiss_button);
+ UiUtilities.setupDialogButton(nightMode, cancelButton,
+ DialogButtonType.SECONDARY, R.string.shared_string_cancel);
+ cancelButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showExitDialog();
+ }
+ });
+
+ view.findViewById(R.id.buttons_divider).setVisibility(View.VISIBLE);
+
+ saveButton = view.findViewById(R.id.right_bottom_button);
+ UiUtilities.setupDialogButton(nightMode, saveButton,
+ UiUtilities.DialogButtonType.PRIMARY, R.string.shared_string_save);
+ saveButton.setVisibility(View.VISIBLE);
+ saveButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onSaveEngine();
+ dismiss();
+ }
+ });
+ }
+
+ private void changeEngineType(EngineType type) {
+ OnlineRoutingEngine tmp = (OnlineRoutingEngine) engine.clone();
+ engine = OnlineRoutingFactory.createEngine(type, tmp.getParams());
+
+ // after changing the type, select the vehicle
+ // with the same name that was selected before
+ VehicleType previous = tmp.getSelectedVehicleType();
+ VehicleType next = null;
+ for (VehicleType vt : engine.getAllowedVehicles()) {
+ if (Algorithms.objectEquals(previous.getTitle(app), vt.getTitle(app))) {
+ next = vt;
+ break;
+ }
+ }
+ String vehicleKey;
+ if (next != null) {
+ vehicleKey = next.equals(CUSTOM_VEHICLE) ? customVehicleKey : next.getKey();
+ } else {
+ vehicleKey = engine.getAllowedVehicles().get(0).getKey();
+ }
+ engine.put(EngineParameter.VEHICLE_KEY, vehicleKey);
+
+ setupVehicleTypes();
+ generateUniqueNameIfNeeded();
+ updateCardViews(nameCard, typeCard, vehicleCard, exampleCard);
+ }
+
+ private void generateUniqueNameIfNeeded() {
+ if (engine.get(EngineParameter.CUSTOM_NAME) == null) {
+ engine.remove(EngineParameter.NAME_INDEX);
+ if (hasNameDuplicate(engine.getName(app))) {
+ int index = 0;
+ do {
+ engine.put(EngineParameter.NAME_INDEX, String.valueOf(++index));
+ } while (hasNameDuplicate(engine.getName(app)));
+ }
+ }
+ }
+
+ private void checkCustomNameUnique(@NonNull OnlineRoutingEngine engine) {
+ if (hasNameDuplicate(engine.getName(app))) {
+ nameCard.showFieldBoxError(getString(R.string.message_name_is_already_exists));
+ saveButton.setEnabled(false);
+ } else {
+ nameCard.hideFieldBoxError();
+ saveButton.setEnabled(true);
+ }
+ }
+
+ private boolean hasNameDuplicate(@NonNull String name) {
+ for (OnlineRoutingEngine engine : helper.getEnginesExceptMentioned(editedEngineKey)) {
+ if (Algorithms.objectEquals(engine.getName(app), name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void onSaveEngine() {
+ if (engine != null) {
+ helper.saveEngine(engine);
+ }
+ }
+
+ private void onDeleteEngine() {
+ helper.deleteEngine(engine);
+ }
+
+ private boolean isEditingMode() {
+ return editedEngineKey != null;
+ }
+
+ private String getTestUrl() {
+ List path = new ArrayList<>();
+ path.add(selectedLocation.getCityCenterLatLon());
+ path.add(selectedLocation.getCityAirportLatLon());
+ return engine.getFullUrl(path);
+ }
+
+ private void testEngineWork() {
+ final OnlineRoutingEngine requestedEngine = (OnlineRoutingEngine) engine.clone();
+ final ExampleLocation location = selectedLocation;
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ StringBuilder message = new StringBuilder();
+ boolean resultOk = false;
+ try {
+ String response = helper.makeRequest(exampleCard.getEditedText());
+ resultOk = requestedEngine.parseServerMessage(message, response);
+ } catch (IOException | JSONException e) {
+ message.append(e.toString());
+ }
+ showTestResults(resultOk, message.toString(), location);
+ }
+ }).start();
+ }
+
+ private void showTestResults(final boolean resultOk,
+ final @NonNull String message,
+ final @NonNull ExampleLocation location) {
+ app.runInUIThread(new Runnable() {
+ @Override
+ public void run() {
+ testResultsContainer.setVisibility(View.VISIBLE);
+ ImageView ivImage = testResultsContainer.findViewById(R.id.icon);
+ TextView tvTitle = testResultsContainer.findViewById(R.id.title);
+ TextView tvDescription = testResultsContainer.findViewById(R.id.description);
+ if (resultOk) {
+ ivImage.setImageDrawable(getContentIcon(R.drawable.ic_action_gdirections_dark));
+ tvTitle.setText(getString(R.string.shared_string_ok));
+ } else {
+ ivImage.setImageDrawable(getContentIcon(R.drawable.ic_action_alert));
+ tvTitle.setText(String.format(getString(R.string.message_server_error), message));
+ }
+ tvDescription.setText(location.getName());
+ }
+ });
+ }
+
+ private void updateCardViews(@NonNull BaseCard... cardsToUpdate) {
+ for (BaseCard card : cardsToUpdate) {
+ if (nameCard.equals(card)) {
+ if (Algorithms.isEmpty(engine.get(EngineParameter.CUSTOM_NAME))) {
+ nameCard.setEditedText(engine.getName(app));
+ }
+
+ } else if (typeCard.equals(card)) {
+ typeCard.setHeaderSubtitle(engine.getType().getTitle());
+ typeCard.setEditedText(engine.getBaseUrl());
+ if (engine.isParameterAllowed(EngineParameter.API_KEY)) {
+ apiKeyCard.show();
+ } else {
+ apiKeyCard.hide();
+ }
+
+ } else if (vehicleCard.equals(card)) {
+ VehicleType vt = engine.getSelectedVehicleType();
+ vehicleCard.setHeaderSubtitle(vt.getTitle(app));
+ if (vt.equals(CUSTOM_VEHICLE)) {
+ vehicleCard.showFieldBox();
+ vehicleCard.setEditedText(customVehicleKey);
+ } else {
+ vehicleCard.hideFieldBox();
+ }
+
+ } else if (exampleCard.equals(card)) {
+ exampleCard.setEditedText(getTestUrl());
+ }
+ }
+ }
+
+ public void showExitDialog() {
+ View focus = view.findFocus();
+ AndroidUtils.hideSoftKeyboard(mapActivity, focus);
+ if (!engine.equals(initEngine)) {
+ AlertDialog.Builder dismissDialog = createWarningDialog(mapActivity,
+ R.string.shared_string_dismiss, R.string.exit_without_saving, R.string.shared_string_cancel);
+ dismissDialog.setPositiveButton(R.string.shared_string_exit, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ dismissDialog.show();
+ } else {
+ dismiss();
+ }
+ }
+
+ private AlertDialog.Builder createWarningDialog(Activity activity, int title, int message, int negButton) {
+ Context themedContext = UiUtilities.getThemedContext(activity, isNightMode());
+ AlertDialog.Builder warningDialog = new AlertDialog.Builder(themedContext);
+ warningDialog.setTitle(getString(title));
+ warningDialog.setMessage(getString(message));
+ warningDialog.setNegativeButton(negButton, null);
+ return warningDialog;
+ }
+
+ private void dismiss() {
+ FragmentActivity activity = getActivity();
+ if (activity != null) {
+ FragmentManager fragmentManager = activity.getSupportFragmentManager();
+ if (!fragmentManager.isStateSaved()) {
+ fragmentManager.popBackStack();
+ }
+ }
+ }
+
+ private boolean isNightMode() {
+ return !app.getSettings().isLightContentForMode(getAppMode());
+ }
+
+ @NonNull
+ private ApplicationMode getAppMode() {
+ return appMode != null ? appMode : app.getSettings().getApplicationMode();
+ }
+
+ @Nullable
+ private MapActivity getMapActivity() {
+ FragmentActivity activity = getActivity();
+ if (activity instanceof MapActivity) {
+ return (MapActivity) activity;
+ } else {
+ return null;
+ }
+ }
+
+ private LayoutInflater getInflater() {
+ return UiUtilities.getInflater(mapActivity, isNightMode());
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ view.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayout);
+ } else {
+ view.getViewTreeObserver().removeGlobalOnLayoutListener(onGlobalLayout);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ saveState(outState);
+ }
+
+ private void saveState(@NonNull Bundle outState) {
+ outState.putString(ENGINE_TYPE_KEY, engine.getType().name());
+ for (EngineParameter key : EngineParameter.values()) {
+ String value = engine.get(key);
+ if (value != null) {
+ outState.putString(key.name(), value);
+ }
+ }
+ outState.putString(ENGINE_CUSTOM_VEHICLE_KEY, customVehicleKey);
+ outState.putString(EXAMPLE_LOCATION_KEY, selectedLocation.name());
+ outState.putString(APP_MODE_KEY, getAppMode().getStringKey());
+ outState.putString(EDITED_ENGINE_KEY, editedEngineKey);
+ }
+
+ private void restoreState(@NonNull Bundle savedState) {
+ editedEngineKey = savedState.getString(EngineParameter.KEY.name());
+ initEngine = createInitStateEngine();
+ String typeKey = savedState.getString(ENGINE_TYPE_KEY);
+ EngineType type = EngineType.getTypeByName(typeKey);
+ engine = OnlineRoutingFactory.createEngine(type);
+ for (EngineParameter key : EngineParameter.values()) {
+ String value = savedState.getString(key.name());
+ if (value != null) {
+ engine.put(key, value);
+ }
+ }
+ customVehicleKey = savedState.getString(ENGINE_CUSTOM_VEHICLE_KEY);
+ selectedLocation = ExampleLocation.valueOf(savedState.getString(EXAMPLE_LOCATION_KEY));
+ appMode = ApplicationMode.valueOfStringKey(savedState.getString(APP_MODE_KEY), null);
+ }
+
+ private void initState() {
+ initEngine = createInitStateEngine();
+ selectedLocation = ExampleLocation.values()[0];
+ engine = (OnlineRoutingEngine) initEngine.clone();
+ if (Algorithms.objectEquals(engine.getSelectedVehicleType(), CUSTOM_VEHICLE)) {
+ customVehicleKey = engine.get(EngineParameter.VEHICLE_KEY);
+ } else {
+ customVehicleKey = "";
+ }
+ }
+
+ private OnlineRoutingEngine createInitStateEngine() {
+ OnlineRoutingEngine engine;
+ OnlineRoutingEngine editedEngine = helper.getEngineByKey(editedEngineKey);
+ if (editedEngine != null) {
+ engine = (OnlineRoutingEngine) editedEngine.clone();
+ } else {
+ engine = OnlineRoutingFactory.createEngine(EngineType.values()[0]);
+ String vehicle = engine.getAllowedVehicles().get(0).getKey();
+ engine.put(EngineParameter.VEHICLE_KEY, vehicle);
+ if (editedEngineKey != null) {
+ engine.put(EngineParameter.KEY, editedEngineKey);
+ }
+ }
+ return engine;
+ }
+
+ public static void showInstance(@NonNull FragmentActivity activity,
+ @NonNull ApplicationMode appMode,
+ @Nullable String editedEngineKey) {
+ FragmentManager fm = activity.getSupportFragmentManager();
+ if (!fm.isStateSaved() && fm.findFragmentByTag(OnlineRoutingEngineFragment.TAG) == null) {
+ OnlineRoutingEngineFragment fragment = new OnlineRoutingEngineFragment();
+ fragment.appMode = appMode;
+ fragment.editedEngineKey = editedEngineKey;
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment, TAG)
+ .addToBackStack(TAG).commitAllowingStateLoss();
+ }
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/profiles/OnlineRoutingEngineDataObject.java b/OsmAnd/src/net/osmand/plus/profiles/OnlineRoutingEngineDataObject.java
index 46a35ea67e..fd0f593462 100644
--- a/OsmAnd/src/net/osmand/plus/profiles/OnlineRoutingEngineDataObject.java
+++ b/OsmAnd/src/net/osmand/plus/profiles/OnlineRoutingEngineDataObject.java
@@ -1,12 +1,27 @@
package net.osmand.plus.profiles;
+import androidx.annotation.NonNull;
+
import net.osmand.plus.R;
public class OnlineRoutingEngineDataObject extends ProfileDataObject {
+ private int order;
+
public OnlineRoutingEngineDataObject(String name,
String description,
- String stringKey) {
+ String stringKey,
+ int order) {
super(name, description, stringKey, R.drawable.ic_world_globe_dark, false, null);
+ this.order = order;
+ }
+
+ @Override
+ public int compareTo(@NonNull ProfileDataObject profileDataObject) {
+ if (profileDataObject instanceof OnlineRoutingEngineDataObject) {
+ OnlineRoutingEngineDataObject another = (OnlineRoutingEngineDataObject) profileDataObject;
+ return (this.order < another.order) ? -1 : ((this.order == another.order) ? 0 : 1);
+ }
+ return 0;
}
}
diff --git a/OsmAnd/src/net/osmand/plus/profiles/ProfileDataUtils.java b/OsmAnd/src/net/osmand/plus/profiles/ProfileDataUtils.java
index 28967ba2f3..6306dee9cc 100644
--- a/OsmAnd/src/net/osmand/plus/profiles/ProfileDataUtils.java
+++ b/OsmAnd/src/net/osmand/plus/profiles/ProfileDataUtils.java
@@ -5,7 +5,7 @@ import android.content.Context;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.R;
-import net.osmand.plus.onlinerouting.OnlineRoutingEngine;
+import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.profiles.RoutingProfileDataObject.RoutingProfilesResources;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.router.GeneralRouter;
@@ -56,7 +56,22 @@ public class ProfileDataUtils {
Collections.sort(fileNames, new Comparator() {
@Override
public int compare(String s, String t1) {
- return s.equals(OSMAND_NAVIGATION) ? -1 : t1.equals(OSMAND_NAVIGATION) ? 1 : s.compareToIgnoreCase(t1);
+ // OsmAnd navigation should be at the top of the list
+ if (s.equals(OSMAND_NAVIGATION)) {
+ return -1;
+ } else if (t1.equals(OSMAND_NAVIGATION)) {
+ return 1;
+
+ // Online navigation should be at the bottom of the list
+ } else if (s.equals(ONLINE_NAVIGATION)) {
+ return 1;
+ } else if (t1.equals(ONLINE_NAVIGATION)) {
+ return -1;
+
+ // Other sorted by file names
+ } else {
+ return s.compareToIgnoreCase(t1);
+ }
}
});
for (String fileName : fileNames) {
@@ -71,9 +86,11 @@ public class ProfileDataUtils {
public static List getOnlineRoutingProfiles(OsmandApplication app) {
List objects = new ArrayList<>();
- for (OnlineRoutingEngine engine : app.getOnlineRoutingHelper().getEngines()) {
+ List engines = app.getOnlineRoutingHelper().getEngines();
+ for (int i = 0; i < engines.size(); i++) {
+ OnlineRoutingEngine engine = engines.get(i);
objects.add(new OnlineRoutingEngineDataObject(
- engine.getName(app), engine.getBaseUrl(), engine.getStringKey()));
+ engine.getName(app), engine.getBaseUrl(), engine.getStringKey(), i));
}
return objects;
}
diff --git a/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheet.java b/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheet.java
index 98ca4771cc..eb97a8ae85 100644
--- a/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheet.java
+++ b/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheet.java
@@ -39,8 +39,9 @@ import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import net.osmand.plus.helpers.FontCache;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.bottomsheets.BasePreferenceBottomSheet;
-import net.osmand.plus.onlinerouting.OnlineRoutingEngineFragment;
+import net.osmand.plus.onlinerouting.ui.OnlineRoutingEngineFragment;
import net.osmand.router.RoutingConfiguration;
+import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
@@ -250,7 +251,7 @@ public class SelectProfileBottomSheet extends BasePreferenceBottomSheet {
tvTitle.setText(profile.getName());
tvDescription.setText(profile.getDescription());
- boolean isSelected = setupSelected && profile.getStringKey().equals(selectedItemKey);
+ boolean isSelected = setupSelected && Algorithms.objectEquals(profile.getStringKey(), selectedItemKey);
int iconColor;
if (dialogMode == DialogMode.NAVIGATION_PROFILE) {
iconColor = isSelected ? activeColorResId : iconDefaultColorResId;
diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
index 89c17f1c2b..96017bc96c 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
@@ -2,7 +2,6 @@ package net.osmand.plus.routing;
import android.content.Context;
-import android.os.Build;
import android.os.Bundle;
import android.util.Base64;
@@ -20,16 +19,14 @@ import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.LatLon;
import net.osmand.data.LocationPoint;
import net.osmand.data.WptLocationPoint;
-import net.osmand.osm.io.NetworkUtils;
import net.osmand.plus.OsmandApplication;
-import net.osmand.plus.onlinerouting.OnlineRoutingEngine;
import net.osmand.plus.onlinerouting.OnlineRoutingHelper;
+import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.R;
import net.osmand.plus.TargetPointsHelper;
import net.osmand.plus.TargetPointsHelper.TargetPoint;
-import net.osmand.plus.Version;
import net.osmand.plus.render.NativeOsmandLibrary;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.router.GeneralRouter;
@@ -48,21 +45,16 @@ import net.osmand.router.RoutingConfiguration.Builder;
import net.osmand.router.RoutingContext;
import net.osmand.router.TurnType;
import net.osmand.util.Algorithms;
-import net.osmand.util.GeoPolylineParserUtil;
import net.osmand.util.MapUtils;
import org.json.JSONException;
-import org.json.JSONObject;
import org.xml.sax.SAXException;
-import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.net.MalformedURLException;
-import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1210,8 +1202,12 @@ public class RouteProvider {
private RouteCalculationResult findOnlineRoute(RouteCalculationParams params) throws IOException, JSONException {
OnlineRoutingHelper helper = params.ctx.getOnlineRoutingHelper();
String stringKey = params.mode.getRoutingProfile();
- List route = helper.calculateRouteOnline(helper.getEngineByKey(stringKey), getFullPathFromParams(params));
- if (!route.isEmpty()) {
+ OnlineRoutingEngine engine = helper.getEngineByKey(stringKey);
+ List route = null;
+ if (engine != null) {
+ route = helper.calculateRouteOnline(engine, getFullPathFromParams(params));
+ }
+ if (!Algorithms.isEmpty(route)) {
List res = new ArrayList<>();
for (LatLon pt : route) {
WptPt wpt = new WptPt();
diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java
index 03af123cc8..1addb2f50a 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java
@@ -194,7 +194,7 @@ public class RoutingHelper {
}
public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, List intermediatePoints, Location currentLocation) {
- RoutingHelperUtils.checkAndUpdateStartLocation(app, currentLocation);
+ RoutingHelperUtils.checkAndUpdateStartLocation(app, currentLocation, false);
RouteCalculationResult previousRoute = route;
clearCurrentRoute(finalLocation, intermediatePoints);
// to update route
diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelperUtils.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelperUtils.java
index b756238eca..6803a2920f 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelperUtils.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelperUtils.java
@@ -8,9 +8,6 @@ import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.TargetPointsHelper;
-import net.osmand.plus.helpers.enums.MetricsConstants;
-import net.osmand.plus.settings.backend.ApplicationMode;
-import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.util.MapUtils;
import java.util.List;
@@ -164,19 +161,20 @@ public class RoutingHelperUtils {
}
- public static void checkAndUpdateStartLocation(@NonNull OsmandApplication app, LatLon newStartLocation) {
+ public static void checkAndUpdateStartLocation(@NonNull OsmandApplication app, LatLon newStartLocation, boolean force) {
if (newStartLocation != null) {
LatLon lastStartLocation = app.getSettings().getLastStartPoint();
- if (lastStartLocation == null || MapUtils.getDistance(newStartLocation, lastStartLocation) > CACHE_RADIUS) {
+ if (lastStartLocation == null || MapUtils.getDistance(newStartLocation, lastStartLocation) > CACHE_RADIUS || force) {
app.getMapViewTrackingUtilities().detectDrivingRegion(newStartLocation);
app.getSettings().setLastStartPoint(newStartLocation);
}
}
}
- public static void checkAndUpdateStartLocation(@NonNull OsmandApplication app, Location nextStartLocation) {
+ public static void checkAndUpdateStartLocation(@NonNull OsmandApplication app, Location nextStartLocation, boolean force) {
if (nextStartLocation != null) {
- checkAndUpdateStartLocation(app, new LatLon(nextStartLocation.getLatitude(), nextStartLocation.getLongitude()));
+ LatLon newStartLocation = new LatLon(nextStartLocation.getLatitude(), nextStartLocation.getLongitude());
+ checkAndUpdateStartLocation(app, newStartLocation, force);
}
}
}
diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/OnlineRoutingSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OnlineRoutingSettingsItem.java
index 779a97d1df..cf08142393 100644
--- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/OnlineRoutingSettingsItem.java
+++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OnlineRoutingSettingsItem.java
@@ -7,8 +7,9 @@ import androidx.annotation.Nullable;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
-import net.osmand.plus.onlinerouting.OnlineRoutingEngine;
-import net.osmand.plus.onlinerouting.OnlineRoutingEngine.EngineParameter;
+import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
+import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.onlinerouting.OnlineRoutingHelper;
import org.json.JSONException;
@@ -93,8 +94,8 @@ public class OnlineRoutingSettingsItem extends CollectionSettingsItem points = article.getGpxFile().getPoints();
WptPt gpxPoint = null;
String coordinates = url.replace(PREFIX_GEO, "");
double lat;
double lon;
try {
- lat = Double.valueOf(coordinates.substring(0, coordinates.indexOf(",")));
- lon = Double.valueOf(coordinates.substring(coordinates.indexOf(",") + 1,
- coordinates.length()));
+ lat = Double.parseDouble(coordinates.substring(0, coordinates.indexOf(",")));
+ lon = Double.parseDouble(coordinates.substring(coordinates.indexOf(",") + 1));
} catch (NumberFormatException e) {
Log.w(TAG, e.getMessage(), e);
return true;
}
for (WptPt point : points) {
- if (point.getLatitude() == lat && point.getLongitude() == lon) {
+ if (MapUtils.getDistance(point.getLatitude(), point.getLongitude(), lat, lon) < ROUNDING_ERROR) {
gpxPoint = point;
break;
}
}
if (gpxPoint != null) {
final OsmandSettings settings = app.getSettings();
- settings.setMapLocationToShow(lat, lon, settings.getLastKnownMapZoom(),
+ settings.setMapLocationToShow(gpxPoint.getLatitude(), gpxPoint.getLongitude(),
+ settings.getLastKnownMapZoom(),
new PointDescription(PointDescription.POINT_TYPE_WPT, gpxPoint.name),
false,
gpxPoint);