diff --git a/.gitignore b/.gitignore index 5798e73f27..33e746a3d6 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,10 @@ OsmAndCore_*.aar .project out/ +# Huawei +agconnect-services.json +OsmAndHms.jks + # Android Studio /.idea *.iml diff --git a/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl b/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl index 3edd8c94da..72a3035b60 100644 --- a/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl +++ b/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl @@ -20,6 +20,8 @@ import net.osmand.aidlapi.mapmarker.UpdateMapMarkerParams; import net.osmand.aidlapi.calculateroute.CalculateRouteParams; +import net.osmand.aidlapi.profile.ExportProfileParams; + import net.osmand.aidlapi.gpx.ImportGpxParams; import net.osmand.aidlapi.gpx.ShowGpxParams; import net.osmand.aidlapi.gpx.StartGpxRecordingParams; @@ -103,6 +105,8 @@ import net.osmand.aidlapi.events.AKeyEventsParams; import net.osmand.aidlapi.info.AppInfoParams; +import net.osmand.aidlapi.profile.ExportProfileParams; + // NOTE: Add new methods at the end of file!!! interface IOsmAndAidlInterface { @@ -867,4 +871,6 @@ interface IOsmAndAidlInterface { AppInfoParams getAppInfo(); boolean setMapMargins(in MapMarginsParams params); + + boolean exportProfile(in ExportProfileParams params); } \ No newline at end of file diff --git a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java index 2e1543321a..af48552bc4 100644 --- a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java +++ b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java @@ -4,6 +4,8 @@ public interface OsmAndCustomizationConstants { // Navigation Drawer: String DRAWER_ITEM_ID_SCHEME = "drawer.action."; + String DRAWER_SWITCH_PROFILE_ID = DRAWER_ITEM_ID_SCHEME + "switch_profile"; + String DRAWER_CONFIGURE_PROFILE_ID = DRAWER_ITEM_ID_SCHEME + "configure_profile"; String DRAWER_DASHBOARD_ID = DRAWER_ITEM_ID_SCHEME + "dashboard"; String DRAWER_MAP_MARKERS_ID = DRAWER_ITEM_ID_SCHEME + "map_markers"; String DRAWER_MY_PLACES_ID = DRAWER_ITEM_ID_SCHEME + "my_places"; diff --git a/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java b/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java index 1118a17f5c..ad122f60c6 100644 --- a/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java +++ b/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java @@ -9,12 +9,21 @@ import net.osmand.aidlapi.AidlParams; public class CopyFileParams extends AidlParams { + public static final String DESTINATION_DIR_KEY = "destinationDir"; + public static final String FILE_NAME_KEY = "fileName"; + public static final String FILE_PART_DATA_KEY = "filePartData"; + public static final String START_TIME_KEY = "startTime"; + public static final String DONE_KEY = "done"; + private String destinationDir; private String fileName; private byte[] filePartData; private long startTime; private boolean done; - public CopyFileParams(@NonNull String fileName, @NonNull byte[] filePartData, long startTime, boolean done) { + public CopyFileParams(@NonNull String destinationDir, @NonNull String fileName, @NonNull byte[] filePartData, + long startTime, boolean done) { + + this.destinationDir = destinationDir; this.fileName = fileName; this.filePartData = filePartData; this.startTime = startTime; @@ -37,6 +46,10 @@ public class CopyFileParams extends AidlParams { } }; + public String getDestinationDir() { + return destinationDir; + } + public String getFileName() { return fileName; } @@ -55,23 +68,26 @@ public class CopyFileParams extends AidlParams { @Override public void writeToBundle(Bundle bundle) { - bundle.putString("fileName", fileName); - bundle.putByteArray("filePartData", filePartData); - bundle.putLong("startTime", startTime); - bundle.putBoolean("done", done); + bundle.putString(DESTINATION_DIR_KEY, destinationDir); + bundle.putString(FILE_NAME_KEY, fileName); + bundle.putByteArray(FILE_PART_DATA_KEY, filePartData); + bundle.putLong(START_TIME_KEY, startTime); + bundle.putBoolean(DONE_KEY, done); } @Override protected void readFromBundle(Bundle bundle) { - fileName = bundle.getString("fileName"); - filePartData = bundle.getByteArray("filePartData"); - startTime = bundle.getLong("startTime"); - done = bundle.getBoolean("done"); + destinationDir = bundle.getString(DESTINATION_DIR_KEY); + fileName = bundle.getString(FILE_NAME_KEY); + filePartData = bundle.getByteArray(FILE_PART_DATA_KEY); + startTime = bundle.getLong(START_TIME_KEY); + done = bundle.getBoolean(DONE_KEY); } @Override public String toString() { return "CopyFileParams {" + + " destinationDir=" + destinationDir + " fileName=" + fileName + ", filePartData size=" + filePartData.length + ", startTime=" + startTime + diff --git a/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java b/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java index 36959ef776..00c68851e7 100644 --- a/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java +++ b/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java @@ -5,15 +5,31 @@ import android.os.Bundle; import android.os.Parcel; import net.osmand.aidlapi.AidlParams; +import net.osmand.aidlapi.profile.AExportSettingsType; + +import java.util.ArrayList; + +import static net.osmand.aidlapi.profile.ExportProfileParams.SETTINGS_TYPE_KEY; public class ProfileSettingsParams extends AidlParams { + public static final String VERSION_KEY = "version"; + public static final String REPLACE_KEY = "replace"; + public static final String LATEST_CHANGES_KEY = "latestChanges"; + public static final String PROFILE_SETTINGS_URI_KEY = "profileSettingsUri"; private Uri profileSettingsUri; private String latestChanges; private int version; + private ArrayList settingsTypeKeyList = new ArrayList<>(); + boolean replace; - public ProfileSettingsParams(Uri profileSettingsUri, String latestChanges, int version) { + public ProfileSettingsParams(Uri profileSettingsUri, ArrayList settingsTypeList, boolean replace, + String latestChanges, int version) { this.profileSettingsUri = profileSettingsUri; + for (AExportSettingsType settingsType : settingsTypeList) { + settingsTypeKeyList.add(settingsType.name()); + } + this.replace = replace; this.latestChanges = latestChanges; this.version = version; } @@ -46,17 +62,29 @@ public class ProfileSettingsParams extends AidlParams { return profileSettingsUri; } + public ArrayList getSettingsTypeKeys() { + return settingsTypeKeyList; + } + + public boolean isReplace() { + return replace; + } + @Override public void writeToBundle(Bundle bundle) { - bundle.putInt("version", version); - bundle.putString("latestChanges", latestChanges); - bundle.putParcelable("profileSettingsUri", profileSettingsUri); + bundle.putInt(VERSION_KEY, version); + bundle.putString(LATEST_CHANGES_KEY, latestChanges); + bundle.putParcelable(PROFILE_SETTINGS_URI_KEY, profileSettingsUri); + bundle.putStringArrayList(SETTINGS_TYPE_KEY, settingsTypeKeyList); + bundle.putBoolean(REPLACE_KEY, replace); } @Override protected void readFromBundle(Bundle bundle) { - version = bundle.getInt("version"); - latestChanges = bundle.getString("latestChanges"); - profileSettingsUri = bundle.getParcelable("profileSettingsUri"); + version = bundle.getInt(VERSION_KEY); + latestChanges = bundle.getString(LATEST_CHANGES_KEY); + profileSettingsUri = bundle.getParcelable(PROFILE_SETTINGS_URI_KEY); + settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY); + replace = bundle.getBoolean(REPLACE_KEY); } } \ No newline at end of file diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.aidl b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.aidl new file mode 100644 index 0000000000..99c59bba2a --- /dev/null +++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidlapi.profile; + +parcelable AExportSettingsType; \ No newline at end of file diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.java b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.java new file mode 100644 index 0000000000..23c0189615 --- /dev/null +++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.java @@ -0,0 +1,11 @@ +package net.osmand.aidlapi.profile; + +public enum AExportSettingsType { + PROFILE, + QUICK_ACTIONS, + POI_TYPES, + MAP_SOURCES, + CUSTOM_RENDER_STYLE, + CUSTOM_ROUTING, + AVOID_ROADS; +} diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.aidl b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.aidl new file mode 100644 index 0000000000..0dfefce6be --- /dev/null +++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidlapi.profile; + +parcelable ExportProfileParams; \ No newline at end of file diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.java b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.java new file mode 100644 index 0000000000..931f52eeb8 --- /dev/null +++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.java @@ -0,0 +1,61 @@ +package net.osmand.aidlapi.profile; + +import android.os.Bundle; +import android.os.Parcel; + +import net.osmand.aidlapi.AidlParams; + +import java.util.ArrayList; +import java.util.List; + +public class ExportProfileParams extends AidlParams { + + public static final String PROFILE_KEY = "profile"; + public static final String SETTINGS_TYPE_KEY = "settings_type"; + private String profile; + private ArrayList settingsTypeKeyList = new ArrayList<>(); + + public ExportProfileParams(String profile, ArrayList settingsTypeList) { + + this.profile = profile; + for (AExportSettingsType settingsType : settingsTypeList) { + settingsTypeKeyList.add(settingsType.name()); + } + } + + public ExportProfileParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new Creator() { + @Override + public ExportProfileParams createFromParcel(Parcel in) { + return new ExportProfileParams(in); + } + + @Override + public ExportProfileParams[] newArray(int size) { + return new ExportProfileParams[size]; + } + }; + + public String getProfile() { + return profile; + } + + public List getSettingsTypeKeys() { + return settingsTypeKeyList; + } + + @Override + public void writeToBundle(Bundle bundle) { + bundle.putString(PROFILE_KEY, profile); + bundle.putStringArrayList(SETTINGS_TYPE_KEY, settingsTypeKeyList); + } + + @Override + protected void readFromBundle(Bundle bundle) { + profile = bundle.getString(PROFILE_KEY); + settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY); + } +} \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/MapRenderingTypes.java b/OsmAnd-java/src/main/java/net/osmand/osm/MapRenderingTypes.java index 34e5048930..d996678e44 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/MapRenderingTypes.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/MapRenderingTypes.java @@ -27,7 +27,7 @@ public abstract class MapRenderingTypes { private static final Log log = PlatformUtil.getLog(MapRenderingTypes.class); public static final String[] langs = new String[] { "af", "als", "ar", "az", "be", "bg", "bn", "bpy", "br", "bs", "ca", "ceb", "cs", "cy", "da", "de", "el", "eo", "es", "et", "eu", "fa", "fi", "fr", "fy", "ga", "gl", "he", "hi", "hsb", - "hr", "ht", "hu", "hy", "id", "is", "it", "ja", "ka", "ko", "ku", "la", "lb", "lo", "lt", "lv", "mk", "ml", "mr", "ms", "nds", "new", "nl", "nn", "no", "nv", "os", "pl", "pms", "pt", "ro", "ru", "sc", "sh", "sk", "sl", "sq", "sr", "sv", "sw", "ta", "te", "th", "tl", "tr", "uk", "vi", "vo", "zh", "zh-hans", "zh-hant", }; + "hr", "ht", "hu", "hy", "id", "is", "it", "ja", "ka", "kn", "ko", "ku", "la", "lb", "lo", "lt", "lv", "mk", "ml", "mr", "ms", "nds", "new", "nl", "nn", "no", "nv", "os", "pl", "pms", "pt", "ro", "ru", "sc", "sh", "sk", "sl", "sq", "sr", "sv", "sw", "ta", "te", "th", "tl", "tr", "uk", "vi", "vo", "zh", "zh-hans", "zh-hant", }; public final static byte RESTRICTION_NO_RIGHT_TURN = 1; diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java index 54464afc32..bff7db3a6d 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java @@ -739,7 +739,7 @@ public class RoutePlannerFrontEnd { res = searchRouteImpl(ctx, points, routeDirection); } if (ctx.calculationProgress != null) { - ctx.calculationProgress.timeToCalculate += (System.nanoTime() - timeToCalculate); + ctx.calculationProgress.timeToCalculate = (System.nanoTime() - timeToCalculate); } BinaryRoutePlanner.printDebugMemoryInformation(ctx); if (res != null) { diff --git a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java index d890d5edea..5524827631 100644 --- a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java +++ b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java @@ -947,4 +947,20 @@ public class Algorithms { } return res; } + + public static boolean isValidMessageFormat(CharSequence sequence) { + if (!isEmpty(sequence)) { + int counter = 0; + for (int i = 0; i < sequence.length(); i++) { + char ch = sequence.charAt(i); + if (ch == '{') { + counter++; + } else if (ch == '}') { + counter--; + } + } + return counter == 0; + } + return false; + } } \ No newline at end of file diff --git a/OsmAnd-telegram/AndroidManifest.xml b/OsmAnd-telegram/AndroidManifest.xml index 7b2a96c236..73e2e856ca 100644 --- a/OsmAnd-telegram/AndroidManifest.xml +++ b/OsmAnd-telegram/AndroidManifest.xml @@ -20,7 +20,7 @@ android:screenOrientation="unspecified" android:supportsRtl="true" android:theme="@style/AppTheme"> - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml index 928ad6f319..1c5738a313 100644 --- a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml +++ b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml @@ -447,6 +447,50 @@ + + + + + + + + + + + + + + diff --git a/OsmAnd-telegram/res/layout/item_description_long.xml b/OsmAnd-telegram/res/layout/item_description_long.xml new file mode 100644 index 0000000000..6face5220f --- /dev/null +++ b/OsmAnd-telegram/res/layout/item_description_long.xml @@ -0,0 +1,18 @@ + + diff --git a/OsmAnd-telegram/res/values-ar/strings.xml b/OsmAnd-telegram/res/values-ar/strings.xml index 1fb4149e0b..45b5d99ce7 100644 --- a/OsmAnd-telegram/res/values-ar/strings.xml +++ b/OsmAnd-telegram/res/values-ar/strings.xml @@ -267,4 +267,8 @@ تتبع حالة أوسماند العودة إلى OsmAnd %1$s منذ + إرسال التقرير + تصدير + سجل الاستخدام + التحقق من السجلات التفصيلية للتطبيق ومشاركتها \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-de/strings.xml b/OsmAnd-telegram/res/values-de/strings.xml index 8c12570da5..0767c598d8 100644 --- a/OsmAnd-telegram/res/values-de/strings.xml +++ b/OsmAnd-telegram/res/values-de/strings.xml @@ -267,4 +267,8 @@ Letzte Antwort: vor %1$s vor %1$s ERR + Export + Logcat-Puffer + Protokolle der Anwendung einsehen und freigeben + Bericht senden \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-es-rUS/strings.xml b/OsmAnd-telegram/res/values-es-rUS/strings.xml index 86bfb79310..259192e6a8 100644 --- a/OsmAnd-telegram/res/values-es-rUS/strings.xml +++ b/OsmAnd-telegram/res/values-es-rUS/strings.xml @@ -268,4 +268,8 @@ Última respuesta: Hace %1$s Hace %1$s ERR + Exportar + Búfer de Logcat + Comprueba y comparte los registros detallados de la aplicación + Enviar informe \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-et/strings.xml b/OsmAnd-telegram/res/values-et/strings.xml index 126490e593..0ddb7950a7 100644 --- a/OsmAnd-telegram/res/values-et/strings.xml +++ b/OsmAnd-telegram/res/values-et/strings.xml @@ -267,4 +267,8 @@ Viimane vastus: %1$s tagasi %1$s tagasi ERR + Ekspordi + Logcati puhver + Vaata ja jaga rakenduse detailseid logisid + Saada ettekanne \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-fr/strings.xml b/OsmAnd-telegram/res/values-fr/strings.xml index 4397948508..4b9d45edfb 100644 --- a/OsmAnd-telegram/res/values-fr/strings.xml +++ b/OsmAnd-telegram/res/values-fr/strings.xml @@ -267,4 +267,8 @@ Définissez l\'heure à laquelle les contacts et groupes sélectionnés verront votre position en temps réel. OsmAnd connect depuis + Buffer Logcat + Vérifier et partager les logs détaillés de l\'application + Exporter + Envoyer le rapport \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-he/strings.xml b/OsmAnd-telegram/res/values-he/strings.xml index 99c18d4cb2..65da2f52a1 100644 --- a/OsmAnd-telegram/res/values-he/strings.xml +++ b/OsmAnd-telegram/res/values-he/strings.xml @@ -268,4 +268,8 @@ תגובה אחרונה: לפני %1$s לפני %1$s שגיאה + ייצוא + מכלא Logcat + בדיקה ושיתוף יומני תיעוד מפורטים של היישומים + שליחת דיווח \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-hu/strings.xml b/OsmAnd-telegram/res/values-hu/strings.xml index 8c257bb040..95d655b5cb 100644 --- a/OsmAnd-telegram/res/values-hu/strings.xml +++ b/OsmAnd-telegram/res/values-hu/strings.xml @@ -268,4 +268,8 @@ Utolsó válasz: %1$s Ennyivel ezelőtt: %1$s HIBA + Exportálás + Logcat-puffer (hibanapló) + Az alkalmazás részletes naplóinak ellenőrzése és megosztása + Jelentés küldése \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-pt-rBR/strings.xml b/OsmAnd-telegram/res/values-pt-rBR/strings.xml index c1aa3b8e55..d792f9748f 100644 --- a/OsmAnd-telegram/res/values-pt-rBR/strings.xml +++ b/OsmAnd-telegram/res/values-pt-rBR/strings.xml @@ -267,4 +267,8 @@ Última resposta: %1$s atrás %1$s atrás ERR + Exportar + Buffer de Logcat + Verifique e compartilhe registros detalhados do aplicativo + Enviar o relatório \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-pt/strings.xml b/OsmAnd-telegram/res/values-pt/strings.xml index 6076bfee91..9a516699e2 100644 --- a/OsmAnd-telegram/res/values-pt/strings.xml +++ b/OsmAnd-telegram/res/values-pt/strings.xml @@ -108,7 +108,7 @@ Ainda não encontrado Reenvie o local Última localização disponível - Status de compartilhamento + Estado de compartilhamento Compartilhamento: %1$s Ativado Sem conexão GPS @@ -267,4 +267,8 @@ Última resposta: %1$s Última atualização do Telegram: %1$s ERR + Enviar o relatório + Exportar + Buffer de logcat + Verifique e compartilhe registos detalhados da app \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-ru/strings.xml b/OsmAnd-telegram/res/values-ru/strings.xml index daff0e950b..e12c0245b5 100644 --- a/OsmAnd-telegram/res/values-ru/strings.xml +++ b/OsmAnd-telegram/res/values-ru/strings.xml @@ -75,7 +75,7 @@ По расстоянию По имени По группе - Сортировать + Сортировка Сортировать по Отстановить все Выход @@ -267,4 +267,8 @@ Последнее обновление от Telegram: %1$s назад Последний ответ: %1$s Последнее обновление от Telegram: %1$s + Экспорт + Буфер Logcat + Проверьте и поделитесь подробными журналами приложения + Отправить отчёт \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-sc/strings.xml b/OsmAnd-telegram/res/values-sc/strings.xml index 9846061b82..82a4e8aade 100644 --- a/OsmAnd-telegram/res/values-sc/strings.xml +++ b/OsmAnd-telegram/res/values-sc/strings.xml @@ -268,4 +268,8 @@ Ùrtima risposta: %1$s a como %1$s a como ERR + Esporta + Buffer de Logcat + Verìfica e cumpartzi sos registros de s\'aplicatzione fatos a sa minuda + Imbia resumu \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-tr/strings.xml b/OsmAnd-telegram/res/values-tr/strings.xml index 086306b609..87ba56717d 100644 --- a/OsmAnd-telegram/res/values-tr/strings.xml +++ b/OsmAnd-telegram/res/values-tr/strings.xml @@ -267,4 +267,8 @@ Son cevap: %1$s önce %1$s önce HATA + Dışa aktar + Logcat tamponu + Uygulamanın ayrıntılı günlük kayıtlarına göz atın ve paylaşın + Rapor gönder \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-uk/strings.xml b/OsmAnd-telegram/res/values-uk/strings.xml index 1ff2bebb09..e370c05360 100644 --- a/OsmAnd-telegram/res/values-uk/strings.xml +++ b/OsmAnd-telegram/res/values-uk/strings.xml @@ -118,7 +118,7 @@ Налаштування Застосунок не має дозволу до отримання даних позиціювання. Будь ласка, увімкніть «Позиціювання» у системних налаштуваннях - Фоновий режим + Режим тла OsmAnd Tracker працює у фоновому режимі з вимкненим екраном. Відстань Поділитися позицією @@ -267,4 +267,8 @@ Остання відповідь: %1$s тому %1$s тому ПМЛК + Експорт + Буфер logcat + Переглянути та надіслати докладний журнал застосунку + Надіслати звіт \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-zh-rTW/strings.xml b/OsmAnd-telegram/res/values-zh-rTW/strings.xml index f1f06a0daa..7739c65184 100644 --- a/OsmAnd-telegram/res/values-zh-rTW/strings.xml +++ b/OsmAnd-telegram/res/values-zh-rTW/strings.xml @@ -270,4 +270,8 @@ 最後回應:%1$s 前 %1$s 前 ERR + 傳送報告 + 匯出 + Logcat 緩衝 + 檢查及分享應用程式的詳細紀錄 \ No newline at end of file diff --git a/OsmAnd-telegram/res/values/dimens.xml b/OsmAnd-telegram/res/values/dimens.xml index 7f4942dc3e..60976ff822 100644 --- a/OsmAnd-telegram/res/values/dimens.xml +++ b/OsmAnd-telegram/res/values/dimens.xml @@ -27,6 +27,7 @@ 89dp 48dp + 44dp 42dp 56dp diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index 2b5eca334e..8bccf957ce 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -1,5 +1,9 @@ + Send report + Check and share detailed logs of the app + Logcat buffer + Export ERR Last update from Telegram: %1$s Last response: %1$s diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt index ee6bbb29d4..b6927dc610 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt @@ -3,16 +3,20 @@ package net.osmand.telegram import android.app.Application import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.net.ConnectivityManager import android.net.NetworkInfo import android.os.Build import android.os.Handler +import net.osmand.PlatformUtil +import net.osmand.telegram.ui.TrackerLogcatActivity import net.osmand.telegram.helpers.* import net.osmand.telegram.helpers.OsmandAidlHelper.OsmandHelperListener import net.osmand.telegram.helpers.OsmandAidlHelper.UpdatesListener import net.osmand.telegram.notifications.NotificationHelper import net.osmand.telegram.utils.AndroidUtils import net.osmand.telegram.utils.UiUtils +import java.io.File class TelegramApplication : Application() { @@ -200,4 +204,33 @@ class TelegramApplication : Application() { fun runInUIThread(action: (() -> Unit), delay: Long) { uiHandler.postDelayed(action, delay) } + + fun sendCrashLog(file: File) { + val intent = Intent(Intent.ACTION_SEND) + intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("crash@osmand.net")) + intent.putExtra(Intent.EXTRA_STREAM, AndroidUtils.getUriForFile(this, file)) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.type = "vnd.android.cursor.dir/email" + intent.putExtra(Intent.EXTRA_SUBJECT, "OsmAnd bug") + val text = StringBuilder() + text.append("\nDevice : ").append(Build.DEVICE) + text.append("\nBrand : ").append(Build.BRAND) + text.append("\nModel : ").append(Build.MODEL) + text.append("\nProduct : ").append(Build.PRODUCT) + text.append("\nBuild : ").append(Build.DISPLAY) + text.append("\nVersion : ").append(Build.VERSION.RELEASE) + text.append("\nApp : ").append(getString(R.string.app_name_short)) + try { + val info = packageManager.getPackageInfo(packageName, 0) + if (info != null) { + text.append("\nApk Version : ").append(info.versionName).append(" ").append(info.versionCode) + } + } catch (e: PackageManager.NameNotFoundException) { + PlatformUtil.getLog(TrackerLogcatActivity::class.java).error("", e) + } + intent.putExtra(Intent.EXTRA_TEXT, text.toString()) + val chooserIntent = Intent.createChooser(intent, getString(R.string.send_report)) + chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(chooserIntent) + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index d8ec28a9c0..d9ec335a0e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -110,7 +110,7 @@ private const val PROXY_ENABLED = "proxy_enabled" private const val PROXY_PREFERENCES_KEY = "proxy_preferences" private const val SHARING_INITIALIZATION_TIME = 60 * 2L // 2 minutes -private const val WAITING_TDLIB_TIME = 3 // 3 seconds +private const val WAITING_TDLIB_TIME = 7 // 7 seconds private const val GPS_UPDATE_EXPIRED_TIME = 60 * 3L // 3 minutes @@ -540,14 +540,24 @@ class TelegramSettings(private val app: TelegramApplication) { if (initTime && initSending) { initializing = true } else { + var waitingTimeError = false val maxWaitingTime = WAITING_TDLIB_TIME * MAX_MESSAGES_IN_TDLIB_PER_CHAT * max(1, chatsCount) - val textSharingError = !shareInfo.lastTextMessageHandled && currentTime - shareInfo.lastSendTextMessageTime > maxWaitingTime - val mapSharingError = !shareInfo.lastMapMessageHandled && currentTime - shareInfo.lastSendMapMessageTime > maxWaitingTime - if (shareInfo.hasSharingError - || (shareTypeValue == SHARE_TYPE_MAP_AND_TEXT && (textSharingError || mapSharingError)) - || textSharingError && (shareTypeValue == SHARE_TYPE_TEXT) - || mapSharingError && (shareTypeValue == SHARE_TYPE_MAP) - ) { + val textSharingWaitingTime = currentTime - shareInfo.lastSendTextMessageTime + val mapSharingWaitingTime = currentTime - shareInfo.lastSendMapMessageTime + val textSharingError = !shareInfo.lastTextMessageHandled && textSharingWaitingTime > maxWaitingTime + val mapSharingError = !shareInfo.lastMapMessageHandled && mapSharingWaitingTime > maxWaitingTime + if ((shareTypeValue == SHARE_TYPE_MAP_AND_TEXT && (textSharingError || mapSharingError)) + || textSharingError && (shareTypeValue == SHARE_TYPE_TEXT) + || mapSharingError && (shareTypeValue == SHARE_TYPE_MAP)) { + waitingTimeError = true + log.debug("Send chats error for share type \"$shareTypeValue\"" + + "\nMax waiting time: ${maxWaitingTime}s" + + "\nLast text message handled: ${shareInfo.lastTextMessageHandled}" + + "\nText sharing waiting time: ${textSharingWaitingTime}s" + + "\nLast map message handled: ${shareInfo.lastMapMessageHandled}" + + "\nMap sharing waiting time: ${mapSharingWaitingTime}s") + } + if (shareInfo.hasSharingError || waitingTimeError) { sendChatsErrors = true locationTime = max(shareInfo.lastTextSuccessfulSendTime, shareInfo.lastMapSuccessfulSendTime) chatsIds.add(shareInfo.chatId) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt index e4d5188a2b..704a797af2 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt @@ -776,6 +776,7 @@ class TelegramHelper private constructor() { client?.send(TdApi.CreatePrivateChat(userId, false)) { obj -> when (obj.constructor) { TdApi.Error.CONSTRUCTOR -> { + log.debug("createPrivateChatWithUser ERROR $obj") val error = obj as TdApi.Error if (error.code != IGNORED_ERROR_CODE) { shareInfo.hasSharingError = true @@ -969,7 +970,7 @@ class TelegramHelper private constructor() { val messageType = if (isBot) MESSAGE_TYPE_BOT else MESSAGE_TYPE_TEXT when (obj.constructor) { TdApi.Error.CONSTRUCTOR -> { - log.debug("handleTextLocationMessageUpdate - ERROR") + log.debug("handleTextLocationMessageUpdate - ERROR $obj") val error = obj as TdApi.Error if (error.code != IGNORED_ERROR_CODE) { shareInfo.hasSharingError = true diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt index a5bf5993e9..447275815e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt @@ -213,6 +213,12 @@ class SettingsDialogFragment : BaseDialogFragment() { DisconnectTelegramBottomSheet.showInstance(childFragmentManager) } + mainView.findViewById(R.id.logcat_row).setOnClickListener { + val intent = Intent(activity, TrackerLogcatActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + app.startActivity(intent) + } + return mainView } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/TrackerLogcatActivity.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/TrackerLogcatActivity.kt new file mode 100644 index 0000000000..ee62ab4407 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/TrackerLogcatActivity.kt @@ -0,0 +1,271 @@ +package net.osmand.telegram.ui + +import android.os.AsyncTask +import android.os.Bundle +import android.view.* +import android.widget.ProgressBar +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import net.osmand.PlatformUtil +import net.osmand.telegram.R +import net.osmand.telegram.TelegramApplication +import java.io.* +import java.lang.ref.WeakReference +import java.util.* + +class TrackerLogcatActivity : AppCompatActivity() { + private var logcatAsyncTask: LogcatAsyncTask? = null + private val logs: MutableList = ArrayList() + private var adapter: LogcatAdapter? = null + private val LEVELS = arrayOf("D", "I", "W", "E") + private var filterLevel = 1 + private lateinit var recyclerView: RecyclerView + + override fun onCreate(savedInstanceState: Bundle?) { + val app: TelegramApplication = getApplication() as TelegramApplication + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_tracker_logcat) + + val toolbar = findViewById(R.id.toolbar).apply { + navigationIcon = app.uiUtils.getThemedIcon(R.drawable.ic_arrow_back) + setNavigationOnClickListener { onBackPressed() } + } + setSupportActionBar(toolbar) + setupIntermediateProgressBar() + + adapter = LogcatAdapter() + recyclerView = findViewById(R.id.recycler_view) as RecyclerView + recyclerView!!.layoutManager = LinearLayoutManager(this) + recyclerView!!.adapter = adapter + } + + protected fun setupIntermediateProgressBar() { + val progressBar = ProgressBar(this) + progressBar.visibility = View.GONE + progressBar.isIndeterminate = true + val supportActionBar = supportActionBar + if (supportActionBar != null) { + supportActionBar.setDisplayShowCustomEnabled(true) + supportActionBar.customView = progressBar + setSupportProgressBarIndeterminateVisibility(false) + } + } + + override fun setSupportProgressBarIndeterminateVisibility(visible: Boolean) { + val supportActionBar = supportActionBar + if (supportActionBar != null) { + supportActionBar.customView.visibility = if (visible) View.VISIBLE else View.GONE + } + } + + override fun onResume() { + super.onResume() + startLogcatAsyncTask() + } + + override fun onPause() { + super.onPause() + stopLogcatAsyncTask() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val app: TelegramApplication = applicationContext as TelegramApplication + val share: MenuItem = menu.add(0, SHARE_ID, 0, R.string.shared_string_export) + share.icon = app.uiUtils.getThemedIcon(R.drawable.ic_action_share) + share.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) + val level = menu.add(0, LEVEL_ID, 0, "") + level.title = getFilterLevel() + level.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) + return super.onCreateOptionsMenu(menu) + } + + private fun getFilterLevel(): String { + return "*:" + LEVELS[filterLevel] + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val itemId = item.itemId + when (itemId) { + android.R.id.home -> { + finish() + return true + } + LEVEL_ID -> { + filterLevel++ + if (filterLevel >= LEVELS.size) { + filterLevel = 0 + } + item.title = getFilterLevel() + stopLogcatAsyncTask() + logs.clear() + adapter!!.notifyDataSetChanged() + startLogcatAsyncTask() + return true + } + SHARE_ID -> { + startSaveLogsAsyncTask() + return true + } + } + return false + } + + private fun startSaveLogsAsyncTask() { + val saveLogsAsyncTask = SaveLogsAsyncTask(this, logs) + saveLogsAsyncTask.execute() + } + + private fun startLogcatAsyncTask() { + logcatAsyncTask = LogcatAsyncTask(this, getFilterLevel()) + logcatAsyncTask!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) + } + + private fun stopLogcatAsyncTask() { + if (logcatAsyncTask != null && logcatAsyncTask!!.status == AsyncTask.Status.RUNNING) { + logcatAsyncTask!!.cancel(false) + logcatAsyncTask!!.stopLogging() + } + } + + private inner class LogcatAdapter : RecyclerView.Adapter() { + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(viewGroup.context) + val itemView = inflater.inflate(R.layout.item_description_long, viewGroup, false) as TextView + itemView.gravity = Gravity.CENTER_VERTICAL + return LogViewHolder(itemView) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is LogViewHolder) { + val log = getLog(position) + holder.logTextView.text = log + } + } + + override fun getItemCount(): Int { + return logs.size + } + + private fun getLog(position: Int): String { + return logs[position] + } + + private inner class LogViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val logTextView: TextView = itemView.findViewById(R.id.description) + } + } + + class SaveLogsAsyncTask internal constructor(logcatActivity: TrackerLogcatActivity, logs: Collection) : AsyncTask() { + private val logcatActivity: WeakReference + private val logs: Collection + + override fun onPreExecute() { + val activity = logcatActivity.get() + activity?.setSupportProgressBarIndeterminateVisibility(true) + } + + override fun doInBackground(vararg voids: Void?): File { + val app: TelegramApplication = logcatActivity.get()?.applicationContext as TelegramApplication + val file = File(app.getExternalFilesDir(null), LOGCAT_PATH) + try { + if (file.exists()) { + file.delete() + } + val stringBuilder = StringBuilder() + for (log in logs) { + stringBuilder.append(log) + stringBuilder.append("\n") + } + if (file.parentFile.canWrite()) { + val writer = BufferedWriter(FileWriter(file, true)) + writer.write(stringBuilder.toString()) + writer.close() + } + } catch (e: Exception) { + log.error(e) + } + return file + } + + override fun onPostExecute(file: File?) { + val activity = logcatActivity.get() + if (activity != null && file != null) { + val app: TelegramApplication = activity.applicationContext as TelegramApplication + activity.setSupportProgressBarIndeterminateVisibility(false) + app.sendCrashLog(file) + } + } + + init { + this.logcatActivity = WeakReference(logcatActivity) + this.logs = logs + } + } + + class LogcatAsyncTask internal constructor(logcatActivity: TrackerLogcatActivity?, filterLevel: String) : AsyncTask() { + private var processLogcat: Process? = null + private val logcatActivity: WeakReference + private val filterLevel: String + + override fun doInBackground(vararg voids: Void?): Void? { + try { + val filter = android.os.Process.myPid().toString() + val command = arrayOf("logcat", filterLevel, "--pid=$filter", "-T", MAX_BUFFER_LOG.toString()) + processLogcat = Runtime.getRuntime().exec(command) + val bufferedReader = BufferedReader(InputStreamReader(processLogcat?.inputStream)) + var line: String? + while (bufferedReader.readLine().also { line = it } != null && logcatActivity.get() != null) { + if (isCancelled) { + break + } + publishProgress(line) + } + stopLogging() + } catch (e: IOException) { // ignore + } catch (e: Exception) { + log.error(e) + } + return null + } + + override fun onProgressUpdate(vararg values: String?) { + if (values.size > 0 && !isCancelled) { + val activity = logcatActivity.get() + if (activity != null) { + val autoscroll = !activity.recyclerView!!.canScrollVertically(1) + for (s in values) { + if (s != null) { + activity.logs.add(s) + } + } + activity.adapter!!.notifyDataSetChanged() + if (autoscroll) { + activity.recyclerView!!.scrollToPosition(activity.logs.size - 1) + } + } + } + } + + fun stopLogging() { + if (processLogcat != null) { + processLogcat!!.destroy() + } + } + + init { + this.logcatActivity = WeakReference(logcatActivity) + this.filterLevel = filterLevel + } + } + + companion object { + private const val LOGCAT_PATH = "logcat.log" + private const val MAX_BUFFER_LOG = 10000 + private const val SHARE_ID = 0 + private const val LEVEL_ID = 1 + private val log = PlatformUtil.getLog(TrackerLogcatActivity::class.java) + } +} diff --git a/OsmAnd/.gitignore b/OsmAnd/.gitignore index e3071e5fbf..8bfbd5b688 100644 --- a/OsmAnd/.gitignore +++ b/OsmAnd/.gitignore @@ -13,10 +13,13 @@ libs/it.unibo.alice.tuprolog-tuprolog-3.2.1.jar libs/commons-codec-commons-codec-1.11.jar libs/OsmAndCore_android-0.1-SNAPSHOT.jar +# Huawei libs/huawei-*.jar huaweidrmlib/ HwDRM_SDK_* drm_strings.xml +agconnect-services.json +OsmAndHms.jks # copy_widget_icons.sh res/drawable-large/map_* diff --git a/OsmAnd/AndroidManifest-freehuawei.xml b/OsmAnd/AndroidManifest-freehuawei.xml index e96bb7fe47..c557d5fe2a 100644 --- a/OsmAnd/AndroidManifest-freehuawei.xml +++ b/OsmAnd/AndroidManifest-freehuawei.xml @@ -2,24 +2,31 @@ - - - - + + + + + + + + - + tools:replace="android:authorities" + android:authorities="net.osmand.huawei.fileprovider"/> - \ No newline at end of file diff --git a/OsmAnd/AndroidManifest-huawei.xml b/OsmAnd/AndroidManifest-huawei.xml deleted file mode 100644 index bc847980cd..0000000000 --- a/OsmAnd/AndroidManifest-huawei.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle index 91b0b8022f..2775f1a2c2 100644 --- a/OsmAnd/build.gradle +++ b/OsmAnd/build.gradle @@ -40,6 +40,15 @@ android { keyAlias "osmand" keyPassword System.getenv("OSMAND_APK_PASSWORD") } + + publishingHuawei { + storeFile file("/var/lib/jenkins/osmand_hw_key") + storePassword System.getenv("OSMAND_HW_APK_PASSWORD") + keyAlias "osmand" + keyPassword System.getenv("OSMAND_HW_APK_PASSWORD") + v1SigningEnabled true + v2SigningEnabled true + } } defaultConfig { @@ -107,19 +116,26 @@ android { debug { manifest.srcFile "AndroidManifest-debug.xml" } + full { + java.srcDirs = ["src-google"] + } + fulldev { + java.srcDirs = ["src-google"] + } free { + java.srcDirs = ["src-google"] manifest.srcFile "AndroidManifest-free.xml" } freedev { + java.srcDirs = ["src-google"] manifest.srcFile "AndroidManifest-freedev.xml" } freecustom { + java.srcDirs = ["src-google"] manifest.srcFile "AndroidManifest-freecustom.xml" } - huawei { - manifest.srcFile "AndroidManifest-huawei.xml" - } freehuawei { + java.srcDirs = ["src-huawei"] manifest.srcFile "AndroidManifest-freehuawei.xml" } @@ -185,21 +201,16 @@ android { dimension "version" applicationId "net.osmand.plus" } - fulldev { - dimension "version" - applicationId "net.osmand.plus" - resConfig "en" - //resConfigs "xxhdpi", "nodpi" - } - huawei { + fulldev { dimension "version" - applicationId "net.osmand.plus.huawei" + applicationId "net.osmand.plus" + resConfig "en" + // resConfigs "xxhdpi", "nodpi" } freehuawei { dimension "version" applicationId "net.osmand.huawei" } - // CoreVersion legacy { dimension "coreversion" @@ -219,7 +230,11 @@ android { signingConfig signingConfigs.development } release { - signingConfig signingConfigs.publishing + if (gradle.startParameter.taskNames.toString().contains("huawei")) { + signingConfig signingConfigs.publishingHuawei + } else { + signingConfig signingConfigs.publishing + } } } @@ -276,46 +291,6 @@ task downloadWorldMiniBasemap { } } -task downloadHuaweiDrmZip { - doLast { - ant.get(src: 'https://obs.cn-north-2.myhwclouds.com/hms-ds-wf/sdk/HwDRM_SDK_2.5.2.300_ADT.zip', dest: 'HwDRM_SDK_2.5.2.300_ADT.zip', skipexisting: 'true') - ant.unzip(src: 'HwDRM_SDK_2.5.2.300_ADT.zip', dest: 'huaweidrmlib/') - } -} - -task copyHuaweiDrmLibs(type: Copy) { - dependsOn downloadHuaweiDrmZip - from "huaweidrmlib/HwDRM_SDK_2.5.2.300_ADT/libs" - into "libs" -} - -task copyHuaweiDrmValues(type: Copy) { - dependsOn downloadHuaweiDrmZip - from "huaweidrmlib/HwDRM_SDK_2.5.2.300_ADT/res" - into "res" -} - -task downloadPrebuiltHuaweiDrm { - dependsOn copyHuaweiDrmLibs, copyHuaweiDrmValues -} - -task cleanHuaweiDrmLibs(type: Delete) { - delete "huaweidrmlib" - delete fileTree("libs").matching { - include "**/huawei-*.jar" - } -} - -task cleanHuaweiDrmValues(type: Delete) { - delete fileTree("res").matching { - include "**/drm_strings.xml" - } -} - -task cleanPrebuiltHuaweiDrm { - dependsOn cleanHuaweiDrmLibs, cleanHuaweiDrmValues -} - task collectVoiceAssets(type: Sync) { from "../../resources/voice" into "assets/voice" @@ -397,8 +372,6 @@ task copyLargePOIIcons(type: Sync) { } } - - task copyWidgetIconsXhdpi(type: Sync) { from "res/drawable-xxhdpi/" into "res/drawable-large-xhdpi/" @@ -444,15 +417,6 @@ task collectExternalResources { copyWidgetIconsXhdpi, copyPoiCategories, downloadWorldMiniBasemap - - Gradle gradle = getGradle() - String tskReqStr = gradle.getStartParameter().getTaskRequests().toString().toLowerCase() - // Use Drm SDK only for huawei build - if (tskReqStr.contains("huawei")) { - dependsOn downloadPrebuiltHuaweiDrm - } else { - dependsOn cleanPrebuiltHuaweiDrm - } } // Legacy core build @@ -503,10 +467,16 @@ task cleanupDuplicatesInCore() { file("libs/x86_64/libc++_shared.so").renameTo(file("libc++/x86_64/libc++_shared.so")) } } + afterEvaluate { android.applicationVariants.all { variant -> variant.javaCompiler.dependsOn(collectExternalResources, buildOsmAndCore, cleanupDuplicatesInCore) } + Gradle gradle = getGradle() + String tskReqStr = gradle.getStartParameter().getTaskRequests().toString().toLowerCase() + if (tskReqStr.contains("huawei")) { + apply plugin: 'com.huawei.agconnect' + } } task appStart(type: Exec) { @@ -516,7 +486,6 @@ task appStart(type: Exec) { // commandLine 'cmd', '/c', 'adb', 'shell', 'am', 'start', '-n', 'net.osmand.plus/net.osmand.plus.activities.MapActivity' } - dependencies { implementation project(path: ':OsmAnd-java', configuration: 'android') implementation project(':OsmAnd-api') @@ -565,6 +534,5 @@ dependencies { } implementation 'com.jaredrummler:colorpicker:1.1.0' - huaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar') - freehuaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar') + freehuaweiImplementation 'com.huawei.hms:iap:5.0.2.300' } diff --git a/OsmAnd/res/drawable/ic_action_direction_arrow.xml b/OsmAnd/res/drawable/ic_action_direction_arrow.xml index 5e97fe8338..49bec48f22 100644 --- a/OsmAnd/res/drawable/ic_action_direction_arrow.xml +++ b/OsmAnd/res/drawable/ic_action_direction_arrow.xml @@ -1,9 +1,9 @@ + android:viewportHeight="12"> diff --git a/OsmAnd/res/drawable/ic_action_route_part.xml b/OsmAnd/res/drawable/ic_action_route_part.xml new file mode 100644 index 0000000000..e06263ef8e --- /dev/null +++ b/OsmAnd/res/drawable/ic_action_route_part.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/OsmAnd/res/drawable/ic_logo_openplacereview.xml b/OsmAnd/res/drawable/ic_logo_openplacereview.xml new file mode 100644 index 0000000000..fc5b26cbf3 --- /dev/null +++ b/OsmAnd/res/drawable/ic_logo_openplacereview.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/OsmAnd/res/layout/bottom_buttons_vertical.xml b/OsmAnd/res/layout/bottom_buttons_vertical.xml new file mode 100644 index 0000000000..0de7d48389 --- /dev/null +++ b/OsmAnd/res/layout/bottom_buttons_vertical.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml b/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml index b5cb11cf49..f386e8c59f 100644 --- a/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml +++ b/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml @@ -6,33 +6,37 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + osmand:typeface="@string/font_roboto_regular" + tools:text="Some very long title to check overlapped texts" /> + + + osmand:typeface="@string/font_roboto_medium" + tools:text="summary" /> - + diff --git a/OsmAnd/res/layout/bottom_sheet_menu_base.xml b/OsmAnd/res/layout/bottom_sheet_menu_base.xml index 5110455a7c..d265ba938c 100644 --- a/OsmAnd/res/layout/bottom_sheet_menu_base.xml +++ b/OsmAnd/res/layout/bottom_sheet_menu_base.xml @@ -35,10 +35,8 @@ android:layout_width="match_parent" android:layout_height="10dp" android:layout_gravity="bottom" - android:visibility="gone" - android:background="@drawable/bg_contextmenu_shadow_top_light" /> + android:background="@drawable/bg_contextmenu_shadow_top_light" + android:visibility="gone" /> - - - + \ No newline at end of file diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml index f3bbf28ac6..c695a1c813 100644 --- a/OsmAnd/res/values-ar/strings.xml +++ b/OsmAnd/res/values-ar/strings.xml @@ -721,9 +721,9 @@ عكس اتجاه المسار استخدم الوجهة الحالية يمر على طول المسار باكمله - خريطة التنقل متوفرة حالياً لهذا الموقع. + خريطة التنقل متوفرة لهذا الموقع فعلها عبر \n -\nلتفعليها \'القائمة\' ← \'ضبط الخريطة\' ← \'مصدر الخريطة\' ← \'الخريطة المحملة\'. +\n\'القائمة\' ← \'ضبط الخريطة\' ← \'مصدر الخريطة\' ← \'الخريطة المحملة\'. مصدر التوجيه الصوتي اختيار قناة لتشغيل التوجيه الصوتي. صوت المكالمة الهاتفية ( كما يحاول قطع ستريو بلوتوث السيارة ) @@ -1074,7 +1074,7 @@ المسارات الرياضية وسائل المواصلات سمات أخرى للخريطة - العناصر الاخرى + العناصر الأخرى شريط المعلومات العدادات على اليمين العدادات على اليسار @@ -1645,7 +1645,7 @@ الطريق محظور تحديد اعكس نقطة الانطلاق والوصول - أيقونات POI + أيقونات نقاط الاهتمام النوع غير محدد قسم مسجل @@ -1972,7 +1972,7 @@ بطاقة الذاكرة غير متاحة. \nلن تكون قادرا على رؤية الخرائط أو العثور على أماكن. بطاقة الذاكرة في وضع القراءة فقط. -\n يمكنك فقط مشاهدة الخريطة المحملة مسبقا ولا يمكنك التحميل من الإنترنت. +\n يمكنك فقط مشاهدة الخريطة المحملة مسبقاً ولا يمكنك التحميل من الإنترنت. انعطف يميناً بشكل حاد انعطف يساراً بشكل حاد قم بالدوران وواصل @@ -3413,7 +3413,7 @@ سترى الأيقونة فقط أثناء الملاحة أو أثناء التحرك. قيم تظهر أيقونة الخريطة فقط على الخريطة ، وتتغير أثناء التنقل إلى أيقونة التنقل. - تحقق وتبادل سجلات مفصلة من التطبيق + التحقق من السجلات التفصيلية للتطبيق ومشاركتها تعذر تحليل الهدف الجغرافي \'%s\'. الإذن مطلوب لاستخدام هذا الخيار. اعراض جانبية: سيفقد المسار الخاص بك جميع الأقسام التي لم يتحقق فيها معيار الحد الأدنى للسرعة (على سبيل المثال ، حيث تدفع دراجتك أعلى تل شديد الانحدار). أيضا ، لن تكون هناك معلومات حول فترات الراحة ، مثل الاستراحات. هذا له تأثيرات على أي تحليل أو مرحلة ما بعد المعالجة ، مثل عند محاولة تحديد المدة الإجمالية لرحلتك ، أو وقت الحركة ، أو متوسط سرعتك. @@ -3891,4 +3891,14 @@ آخر تعديل الاسم: أ – ي الاسم: أ – ي + رموز البدء/الانتهاء + شكرا لشرائك \"خطوط الكنتور\" + رسوم الاشتراك ستفرض كل شهر. يمكنك إلغاء اشتراكك متى أردت عبر Google play. + سيتم تحصيل المبلغ على حساب AppGallery الخاص بك عند تأكيد الشراء. +\n +\nيتم تجديد الاشتراك تلقائيًا ما لم يتم إلغاؤه قبل تاريخ التجديد. سيتم خصم حسابك على فترة التجديد (شهر/ثلاثة أشهر/سنة) فقط في تاريخ التجديد. +\n +\nيمكنك إدارة وإلغاء الاشتراكات الخاصة بك عن طريق الانتقال إلى إعدادات AppGallery. + تجنب الممرات + تجنب الممرات \ No newline at end of file diff --git a/OsmAnd/res/values-cs/phrases.xml b/OsmAnd/res/values-cs/phrases.xml index e63732cdc6..8d64234b3a 100644 --- a/OsmAnd/res/values-cs/phrases.xml +++ b/OsmAnd/res/values-cs/phrases.xml @@ -3844,4 +3844,5 @@ Šipka Vibrace Tlak + Zkapalněný zemní plyn \ No newline at end of file diff --git a/OsmAnd/res/values-cs/strings.xml b/OsmAnd/res/values-cs/strings.xml index 64f1dc8949..59b0baf68d 100644 --- a/OsmAnd/res/values-cs/strings.xml +++ b/OsmAnd/res/values-cs/strings.xml @@ -3525,4 +3525,57 @@ Zobrazená oblast: %1$s x %2$s Smazat následující cílový bod Umožní ovládat úroveň přiblížení mapy pomocí tlačítek hlasitosti zařízení. Tlačítka hlasitosti pro přibližování + Mezní vzdálenost + Navigační profil + Vyberte soubor stopy, do níž se nový úsek přidá. + Snímky z úrovně ulice + Opravdu chcete zavřít plánovanou trasu a zahodit tak všechny změny\? + V případě opačného směru + Uložit jako nový soubor stopy + Přidat do souboru stopy + Stopy + Stopy + Stopy + "Ukládat trasu do GPX souboru" + Trasa ze stopy + Přidat soubory stop + Zjednodušená trasa + Uloží se pouze linie trasy, mezicíle budou odstraněny. + Název souboru + %s vybraných souborů stop + REC + Pozastaví záznam trasy, pokud je aplikace ukončena (přes nedávné aplikace). (Ikona režimu na pozadí zmizí z notifikační oblasti Androidu.) + Pozastavit záznam trasy + Pokračovat v záznamu trasy + Výchozí + Všechny následující úseky + Přecházející úsek + Všechny předcházející úseky + Pouze vybraný úsek bude přepočítán pomocí vybraného profilu. + Všechny následující úseky budou přepočítány pomocí vybraného profilu. + Všechny předcházející úseky budou přepočítány pomocí vybraného profilu. + Otevřít uloženou trasu + je uloženo + Přidejte prosím alespoň dva body. + Znovu + • Vylepšená funkce plánování trasy: umožňuje použít různé typy navigace pro úseky trasy a přidává stopy +\n +\n • Nové menu pro vzhled stop: zvolte barvu, tloušťku, směrové šipky, ikony startu a cíle +\n +\n • Vylepšená viditelnost bodů pro cyklistiku. +\n +\n • Stopy je nyní možné aktivovat, nabízejí kontextové menu se základními údaji. +\n +\n • Vylepšený algoritmus vyhledávání +\n +\n • Vylepšené možnosti následování stopy v navigaci +\n +\n • Opravené problémy s importem a exportem nastavení profilů +\n +\n + Naposledy změněno + Název: Z – A + Název: A – Z + Ikony startu/cíle + Děkujeme za zakoupení modulu \'Vrstevnice\' \ No newline at end of file diff --git a/OsmAnd/res/values-da/phrases.xml b/OsmAnd/res/values-da/phrases.xml index 3e1f8c0d81..9f26ab3b43 100644 --- a/OsmAnd/res/values-da/phrases.xml +++ b/OsmAnd/res/values-da/phrases.xml @@ -3842,4 +3842,14 @@ Donationsboks Pil: nej Elevator + Nøddebutik + Bikube + Tidsplan + Realtid + Forsinkelse + Ja + Afgangstavle: nej + Små elektriske apparater + Afgangstavle + Genopfyldning af drikkevand \ No newline at end of file diff --git a/OsmAnd/res/values-da/strings.xml b/OsmAnd/res/values-da/strings.xml index 058b72d771..0e05949cf0 100644 --- a/OsmAnd/res/values-da/strings.xml +++ b/OsmAnd/res/values-da/strings.xml @@ -3243,7 +3243,7 @@ Advarsler vises nederst til venstre under navigationen. Skift profil Sprog og output - Gendan standardværdi + Nulstil til standard Opret, importer, rediger profiler Administrer programprofiler… Påvirker hele programmet @@ -3756,8 +3756,8 @@ Slet adresse Tilføj adresse Angiv adresse - Trim før - Trim efter + Trimme før + Trimme efter Skift rutetype før Skift rutetype efter Forenklet spor @@ -3771,4 +3771,21 @@ er gemt Tilføj mindst to punkter. Omgøre + Navigere fra position til sporet + Punkt på sporet for at navigere + %s sporfiler valgt + Sidst ændret + Navn: Z – A + Navn: A – Z + Vælg en sporfil, der skal åbnes. + Vælg en sporfil, som det nye segment skal føjes til. + Kasser alle ændringer i den planlagte rute ved at lukke den\? + Føj til en sporfil + Vælg sporfil, der skal følges eller importeres fra enheden. + Kun rutelinjen gemmes, rutepunkter slettes. + Alle efterfølgende segmenter + Kun det valgte segment genberegnes igen ved hjælp af den valgte profil. + Alle efterfølgende segmenter genberegnes ved hjælp af den valgte profil. + Alle tidligere segmenter genberegnes ved hjælp af den valgte profil. + Start-/slutikoner \ No newline at end of file diff --git a/OsmAnd/res/values-de/phrases.xml b/OsmAnd/res/values-de/phrases.xml index d091dc7fd5..d40597ca63 100644 --- a/OsmAnd/res/values-de/phrases.xml +++ b/OsmAnd/res/values-de/phrases.xml @@ -3847,4 +3847,5 @@ Bienenstock Kleine Elektrogeräte Nussladen + Flüssigerdgas \ No newline at end of file diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml index 319e1c4f6f..fcfcca7c77 100644 --- a/OsmAnd/res/values-de/strings.xml +++ b/OsmAnd/res/values-de/strings.xml @@ -3826,7 +3826,7 @@ Wählen Sie eine Trackdatei zum Öffnen aus. Fertig Track überschreiben - Schwellenwert-Distanz + Maximaler Abstand Als neuen Track speichern Route umkehren Der gesamte Track wird mit dem ausgewählten Profil neu berechnet. @@ -3876,9 +3876,7 @@ Track Datei zum Folgen auswählen, oder vom Gerät importieren. Die GPX-Aufzeichnung wird angehalten, wenn OsmAnd beendet wird (über „zuletzt verwendete Apps“). (Die Hintergrunddienst-Anzeige verschwindet aus der Android-Benachrichtigungsleiste.) Aufzeichnungsintervall für die generelle Track-Aufzeichnung festlegen (via Schaltfläche \'GPX\' auf dem Kartenbildschirm). - Um diese Option nutzen zu können, muss OsmAnd den Track auf die Straßen der Karte einrasten. -\n -\n Wählen Sie im nächsten Schritt ein Navigationsprofil um festzulegen, welche Straßentypen verwendet werden sollen, und wählen Sie einen Wert für die maximal zulässige Entfernung zwischen Track und Straße. + Als nächstes können Sie Ihren Track mit einem Ihrer Navigationsprofile auf die nächstgelegene erlaubte Straße einrasten lassen, um diese Option zu nutzen. Track-Wegpunkt hinzufügen %s Track Dateien ausgewählt Nur die Routenlinie wird gespeichert, die Wegpunkte werden gelöscht. @@ -3910,4 +3908,14 @@ Zuletzt geändert Name: Z – A Name: A – Z + Start-/Ziel-Symbole + Vielen Dank für den Kauf von \'Höhenlinien\' + Das Abonnement wird pro ausgewähltem Zeitraum berechnet. Sie können das Abonnement jederzeit über die AppGallery kündigen. + Die Bezahlung wird Ihrem AppGallery-Konto bei der Bestätigung des Kaufs belastet. +\n +\n Das Abonnement verlängert sich automatisch, sofern es nicht vor dem Verlängerungsdatum gekündigt wird. Ihr Konto wird für den Verlängerungszeitraum (Monat / drei Monate / Jahr) nur am Verlängerungsdatum belastet. +\n +\n Sie können Ihre Abonnements verwalten und kündigen, indem Sie zu Ihren AppGallery-Einstellungen gehen. + Vermeidet Fußwege + Keine Fußwege \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index 4019b950b2..88c35ef6f9 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -3851,4 +3851,5 @@ Pequeños electrodomésticos Panal de abejas Frutos secos + Gas natural licuado \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/strings.xml b/OsmAnd/res/values-es-rAR/strings.xml index 99f789d362..c6f59f41ea 100644 --- a/OsmAnd/res/values-es-rAR/strings.xml +++ b/OsmAnd/res/values-es-rAR/strings.xml @@ -385,15 +385,15 @@ \nCualquiera de estos mapas puede usarse como el mapa predefinido que se mostrará, o como una superposición o subyacencia de otro mapa base (como los mapas estándar de OsmAnd en línea). Ciertos elementos de los mapas vectoriales de OsmAnd pueden ocultarse a través del menú «Configurar mapa» para hacer cualquier subyacencia mas visible. \n \nDescarga las teselas de los mapas directamente en línea, o prepáralo para su uso sin conexión (copiar manualmente en la carpeta de datos OsmAnd) como una base de datos SQLite que puede ser producida por una variedad de herramientas de preparación de mapas de terceros. - Este complemento activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX. -\n + Activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX. +\n \nLas trazas grabadas pueden ser compartidas con tus amigos o ser usadas para contribuir a OSM. Los atletas pueden usar las trazas grabadas para seguir sus entrenamientos. Algunos análisis básicos de trazas se pueden realizar directamente en OsmAnd, como tiempos por vuelta, velocidad media, etc., y por supuesto las trazas pueden analizarse posteriormente con herramientas de análisis de terceros. Complemento de curvas de nivel - Este complemento proporciona una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje. -\n + Proporciona una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje. +\n \nLos datos globales (entre 70° norte y 70° sur) se basan en mediciones de SRTM (Shuttle Radar Topography Mission, o en español Misión de Topografía por Radar desde Transbordador) y ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer, o en español Radiómetro Espacial Avanzado de Emisión Térmica y Reflexión), un instrumento de captura de imágenes a bordo de Terra, el satélite insignia del Sistema de Observación de la Tierra de la NASA. ASTER es un esfuerzo cooperativo entre la NASA, el Ministerio de Economía, Comercio e Industria de Japón (METI) y Sistemas Espaciales de Japón (J-spacesystems). - Este complemento proporciona tanto una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje. (Note que las curvas de nivel y/o los datos de relieve están disponibles en descargas adicionales separadas luego de activar el complemento.) -\n + Proporciona tanto una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje. (Note que las curvas de nivel y/o los datos de relieve están disponibles en descargas adicionales separadas luego de activar el complemento.) +\n \nLos datos globales (entre 70° norte y 70° sur) se basan en mediciones de SRTM (Shuttle Radar Topography Mission, o en español Misión de Topografía por Radar Shuffle) y ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer, o en español Radiómetro Espacial Avanzado de Emisión Térmica y Reflexión), un instrumento de captura de imágenes a bordo de Terra, el satélite insignia del Sistema de Observación de la Tierra de la NASA. ASTER es un esfuerzo cooperativo entre la NASA, el Ministerio de Economía, Comercio e Industria de Japón (METI) y Sistemas Espaciales de Japón (J-spacesystems). Activando esta vista cambia el estilo del mapa OsmAnd a la «Vista turística», que es una vista de alto detalle especial para viajeros y conductores profesionales. \n @@ -406,13 +406,13 @@ \nNo es necesario descargar un mapa especial, la vista es creada a partir de nuestros mapas estándar. \n \nEsta vista puede ser revertida desactivando de nuevo aquí, o cambiando el «Estilo del mapa» desde «Configurar mapa» cuando lo desees. - Este complemento enriquece el mapa y la navegación de OsmAnd al producir también mapas náuticos para el canotaje, vela y otros tipos de deportes acuáticos. -\n -\nUn mapa especial complementado para OsmAnd proporcionará toda las marcas de navegación náutica y símbolos cartográficos para el interior, así como para la navegación cerca de la costa. La descripción de cada marca de navegación proporciona los datos necesarios para su identificación y su significado (categoría, forma, color, número, referencia, etc.). -\n + Enriquece el mapa y la navegación de OsmAnd al producir también mapas náuticos para el canotaje, vela y otros tipos de deportes acuáticos. +\n +\nUn mapa especial complementado para OsmAnd proporcionará toda las marcas de navegación náutica y símbolos cartográficos para el interior, así como para la navegación cerca de la costa. La descripción de cada marca de navegación proporciona los datos necesarios para su identificación y su significado (categoría, forma, color, número, referencia, etc.). +\n \nPara volver a uno de los estilos del mapas convencionales de OsmAnd, simplemente desactiva este complemento de nuevo, o cambia el «Estilo del mapa» en «Configurar mapa» cuando lo desees. - Este complemento para OsmAnd pone a tu alcance detalles sobre pistas de esquí de descenso, de travesía, rutas de esquí alpino, teleféricos y remontes a nivel mundial. Las rutas y pistas se muestran por código de color en función de su dificultad y representados con un estilo del mapa especial «Invierno» que lo asemeja a un paisaje invernal nevado. -\n + Detalles sobre pistas de esquí de descenso, de travesía, rutas de esquí alpino, teleféricos y remontes a nivel mundial. Las rutas y pistas se muestran por código de color en función de su dificultad y representados con un estilo del mapa especial «Invierno» que lo asemeja a un paisaje invernal nevado. +\n \nActivando esta vista, cambia el estilo del mapa a «Invierno y esquí», mostrando las características del terreno en condiciones invernales. Esta vista se puede revertir desactivando de nuevo aquí o cambiando el «Estilo del mapa» en «Configurar mapa» cuando lo desees. Toma notas de audio, fotografía y/o video durante un viaje, usando un botón en el mapa o el menú contextual de la ubicación. Registra dónde se ha estacionado el automóvil, incluyendo cuánto tiempo queda. @@ -3907,4 +3907,14 @@ Último modificado Nombre: Z – A Nombre: A – Z + Iconos de inicio/fin + Gracias por comprar las «Curvas de nivel» + La suscripción se cobra por el período elegido. Puedes cancelarlo en cualquier momento en AppGallery. + El pago será cargado a la cuenta de AppGallery al confirmar la compra. +\n +\nLa suscripción se renueva automáticamente a menos que se cancele antes de la fecha de renovación. La cuenta será cargada por el período de renovación (mes/trimestre/año) sólo en la fecha de renovación. +\n +\nPuedes administrar y cancelar las suscripciones accediendo a los ajustes de AppGallery. + Evita pasar por las veredas o aceras + Evitar veredas/aceras \ No newline at end of file diff --git a/OsmAnd/res/values-es-rUS/phrases.xml b/OsmAnd/res/values-es-rUS/phrases.xml index 25786f2488..8c0dbc2414 100644 --- a/OsmAnd/res/values-es-rUS/phrases.xml +++ b/OsmAnd/res/values-es-rUS/phrases.xml @@ -3851,4 +3851,5 @@ Tablero de partidas Frutos secos Panal de abejas + Gas natural licuado \ No newline at end of file diff --git a/OsmAnd/res/values-es-rUS/strings.xml b/OsmAnd/res/values-es-rUS/strings.xml index a7750a3b93..c42f0e0c5b 100644 --- a/OsmAnd/res/values-es-rUS/strings.xml +++ b/OsmAnd/res/values-es-rUS/strings.xml @@ -3904,4 +3904,14 @@ Nombre: Z – A Nombre: A – Z Último modificado + Iconos de inicio/fin + Gracias por comprar las «Curvas de nivel» + La suscripción se cobra por el período elegido. Puedes cancelarlo en cualquier momento en AppGallery. + El pago será cargado a la cuenta de AppGallery al confirmar la compra. +\n +\nLa suscripción se renueva automáticamente a menos que se cancele antes de la fecha de renovación. La cuenta será cargada por el período de renovación (mes/trimestre/año) sólo en la fecha de renovación. +\n +\nPuedes administrar y cancelar las suscripciones accediendo a los ajustes de AppGallery. + Evita pasar por aceras o veredas + Evitar aceras \ No newline at end of file diff --git a/OsmAnd/res/values-es/phrases.xml b/OsmAnd/res/values-es/phrases.xml index d892b94f7e..cac7b4a36d 100644 --- a/OsmAnd/res/values-es/phrases.xml +++ b/OsmAnd/res/values-es/phrases.xml @@ -1588,7 +1588,7 @@ Koshinto Placa azul Jizo - Crucero (monumento) + Cruz Cantera histórica Agregado Antimonio @@ -1825,7 +1825,7 @@ Capacitación: artes marciales Capacitación: aviación Capacitación: peluquería - Monumento + Objeto monumental Tipo: industria petrolera Tipo: Área de pozos Tipo: fábrica @@ -1868,7 +1868,7 @@ 100LL (con plomo, para aviones) Autogas (Etanol libre de plomo) Jet A-1 (diésel) - AdBlue + Líquido de escape de diesel Combustible: madera Combustible: carbón vegetal Combustible: carbón @@ -3798,7 +3798,7 @@ Tubo Red de recarga de agua potable Recarga de agua potable: no - Recarga de agua potable: sí + Obstrucción Nivel de agua: por debajo del nivel medio del agua Nivel de agua: por encima del nivel medio del agua diff --git a/OsmAnd/res/values-es/strings.xml b/OsmAnd/res/values-es/strings.xml index caf2e2ec40..6041f2a95e 100644 --- a/OsmAnd/res/values-es/strings.xml +++ b/OsmAnd/res/values-es/strings.xml @@ -23,7 +23,7 @@ Borrar edición Edición asíncrona OSM: PDI/Notas de OSM guardados en el dispositivo - Muestra y gestiona PDI/notas de OSM guardadas en la base de datos del dispositivo. + Muestra y gestiona PDI/Notas de OSM en la base de datos de tu dispositivo. Indica el intervalo del seguimiento en línea. Intervalo del seguimiento en línea Indica la dirección web con sintaxis de parámetros : lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}. @@ -163,7 +163,7 @@ El idioma elegido es incompatible con el motor TTS (texto a voz) instalado en Android, se usará el idioma TTS predefinido. ¿Buscar otro motor TTS en la tienda de aplicaciones\? Faltan datos ¿Ir a la tienda de aplicaciones para descargar el idioma elegido? - Invertir la dirección GPX + Invertir la dirección de la traza Usar destino actual Pasar a lo largo de la traza completa Mapa vectorial presente para esta ubicación. @@ -265,10 +265,7 @@ Todos los datos sin conexión en la versión vieja de OsmAnd son compatibles con la nueva versión, pero los puntos de Favoritos deben exportarse desde la versión vieja y luego, importarse en la nueva. Compilación {0} instalada ({1}). Descargando compilación… - ¿Instalar OsmAnd\? -\nVersión: {0} -\nFecha: {1} -\nTamaño: {2} MB + ¿Instalar OsmAnd - {0} de {1} {2} MB\? Error al recuperar la lista de compilaciones de OsmAnd Cargando compilaciones de OsmAnd… Instalar compilación de OsmAnd @@ -441,7 +438,7 @@ Búsqueda sin conexión Búsqueda en línea Máximo zoom en línea - No buscar en las teselas de mapas en línea para niveles de zoom más allá de esto. + No busca en los mapas en línea niveles de zoom más allá de éste. Distancia total %1$s, tiempo de viaje %2$d h %3$d min. Servicios de navegación con o sin conexión. Servicio de navegación @@ -606,7 +603,7 @@ Para países donde la gente conduce por el lado izquierdo del camino. Punto de partida aún no determinado. ¿Cancelar la descarga\? - El mapa base necesario para proporcionar la funcionalidad básica, está en la cola de descarga. + El mapa base necesario para proporcionar funcionalidad básica está en la cola de descargas. Activa el complemento «Mapas en línea», para elegir diferentes fuentes de mapas Mapas en línea y teselas Usa mapas en línea (descarga y guarda teselas en la tarjeta de memoria). @@ -729,8 +726,8 @@ PM AM Lugar de aparcamiento - Este complemento, registra dónde se ha aparcado el automóvil y cuánto tiempo queda (si hay un límite de tiempo). -\nTanto la ubicación como el tiempo del aparcamiento se muestran en el menú principal y en un widget sobre el mapa. Puedes añadir una notificación al calendario, en el caso de que desees tener un recordatorio al respecto. + Te permite egistrar dónde has aparcado el automóvil y cuánto tiempo queda (si hay un límite de tiempo). +\nTanto la ubicación como el tiempo del aparcamiento se muestran en el menú principal y en un control sobre el mapa. Puedes añadir un recordatorio al calendario, de Android. Aparcamiento Marcar como aparcamiento Quitar marcador de aparcamiento @@ -890,7 +887,7 @@ Curvas de nivel Otros mapas Curvas de nivel - Este complemento, proporciona la funcionalidad para tomar notas de audio, fotografía y/o video durante un viaje, usando un botón en el mapa, o directamente en el menú contextual para cualquier ubicación en el mapa. + Tomar notas de audio/foto/video durante un viaje, usando un botón de mapa o un menú contextual de la ubicación. Notas audio/vídeo Complemento OsmAnd para curvas de nivel sin conexión Este complemento proporciona una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje. @@ -930,7 +927,7 @@ Grabando %1$s %3$s %2$s Por favor, considera pagar por el complemento «Curvas de nivel» para apoyar desarrollos adicionales. Complemento de curvas de nivel - El complemento de Dropbox, permite sincronizar trazas y notas multimedia con tu cuenta de Dropbox. + Sincroniza trazas y notas multimedia con tu cuenta de Dropbox. Complemento Dropbox Cambiar orden Mostrar @@ -1003,8 +1000,8 @@ Punto Nombre del archivo GPX Archivo GPX guardado en {0} - Este complemento proporciona un widget en el mapa, permitiendo crear caminos pulsando el mapa, usando o modificando archivos GPX existentes, para planificar un viaje y medir la distancia entre puntos. Los resultados pueden guardarse como un archivo GPX y usarse luego para la orientación. - Distancias y planificación + Crea caminos pulsando en el mapa, o usando o modificando archivos GPX existentes, para planificar un viaje y medir la distancia entre puntos. El resultado puede guardarse como un archivo GPX y usarse luego para la orientación. + Calculadora de distancias y herramienta de planificación * Pulse para marcar un punto. \n * Mantenga pulsado el mapa para quitar el punto anterior. \n * Mantenga pulsado en un punto para ver e incluir la descripción. @@ -1307,9 +1304,9 @@ Duración Distancia Grabación de viaje - Este complemento activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX. -\n -\nLas trazas grabadas pueden ser compartidas con sus amigos o ser usadas para contribuir a OSM. Los atletas pueden usar las trazas grabadas para seguir sus entrenamientos. Algunos análisis básicos de trazas se pueden realizar directamente en OsmAnd, como tiempos por vuelta, velocidad media, etc., y por supuesto las trazas pueden analizarse posteriormente con herramientas de análisis de terceros. + Este complemento activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX. +\n +\nLas trazas grabadas pueden ser compartidas con tus amigos o ser usadas para contribuir a OSM. Los atletas pueden usar las trazas grabadas para monitorizar sus entrenamientos. Algunos análisis básicos de trazas pueden realizarse directamente en OsmAnd, como tiempos por vuelta, velocidad media, etc., y por supuesto las trazas pueden analizarse posteriormente con herramientas de análisis de terceros. Rutas de autobús, trolebús y lanzadera Intervalo de registro Registra la ubicación en un archivo GPX, pudiendo des/activarlo usando el widget de grabación GPX en el mapa. @@ -1433,7 +1430,7 @@ Pista de entrenamiento Sólo caminos Compartir nota - Notas + Notas A/V Mapa en línea Exportar Audio @@ -1781,7 +1778,7 @@ Se ofrece la opción de controlar la aplicación principalmente a través del panel de control flexible o de un menú estático. Se puede cambiar esto luego, en los ajustes del panel. Usar panel de control Usar menú - El botón del menú, muestra el panel de control en lugar del menú + El botón del menú muestra el panel de control en lugar del menú Acceso desde el mapa Indica el tipo de PDI correcto u omítelo. Sin escaleras @@ -1880,7 +1877,7 @@ \nSe utiliza {3} MB temporalmente, {1} MB constantemente. (De {2} MB.) Elegir marcador del mapa Otros marcadores - Subir notas de OSM anónimas o usar el perfil de OpenStreetMap.org. + Sube tu nota de OSM de forma anónima o usando tu perfil de OpenStreetMap.org. Subir nota(s) de OSM Subir anónimamente Barra superior @@ -1910,7 +1907,7 @@ \nParte de los ingresos vuelven a la comunidad de OSM y se paga por cada contribución OSM. \nSi amas a OsmAnd, OSM y quieres apoyarlos y ser apoyado por ellos, esta es una perfecta manera de hacerlo. Mostrar barra de transparencia en el mapa - Se ha cambiado a la memoria interna, porque la carpeta de almacenamiento de datos elegida es de sólo lectura. Elige un directorio de almacenamiento válido. + Se ha cambiado a la memoria interna, porque la carpeta de almacenamiento de datos elegida está protegida de escritura. Elige un directorio de almacenamiento válido. Memoria compartida La aplicación ya permite escribir en el almacenamiento externo, pero se debe reiniciar la aplicación. Subir ↑ @@ -1918,7 +1915,7 @@ Finalizar navegación Evitar camino Informe completo - Nombre de usuario y contraseña de OpenStreetMap + Nombre de usuario y contraseña de OSM Informe Añade marcadores a través del mapa No se encontraron puntos de referencia @@ -2009,16 +2006,16 @@ Necesario para descargar mapas. Buscando la ubicación… Espacio libre - Almacenamiento de datos de OsmAnd (para mapas, archivos GPX, etc.): %1$s. + Almacenamiento de datos de OsmAnd (para mapas, trazas, etc.): %1$s. Conceder permiso Permitir el acceso a la ubicación Obtenga direcciones y descubra lugares nuevos, sin una conexión a Internet Encontrar mi ubicación Omite la búsqueda de nuevas versiones o descuentos relacionados con OsmAnd. Ocultar nuevas versiones - Suscripción mensual. Puede cancelarlo en cualquier momento en Google Play. - Donaciones a la comunidad de OpenStreetMap - Parte de tu donación se envía a usuarios que realicen cambios en OpenStreetMap. El costo de la suscripción sigue siendo la misma. + Suscripción mensual. Puedes cancelarla en cualquier momento en Google Play. + Donación a la comunidad de OSM + Parte de tu donación se envía a los contribuidores a OSM. El coste de la suscripción sigue siendo el mismo. La suscripción permite actualizaciones cada hora, día o semana y descargas ilimitadas para los mapas de todo el mundo. Obtener Obtener por %1$s @@ -2032,8 +2029,8 @@ Subir PDI Cálculo de la ruta Ciudad o región - Sin archivos GPX aún - También puedes añadir archivos GPX a la carpeta + No tienes archivos de trazas aún + También puedes añadir archivos de trazas a la carpeta Añadir más… Aspecto Notificaciones @@ -2047,7 +2044,7 @@ Usar autopistas Permite usar autopistas. Activar la grabación rápida - Muestra una notificación del sistema que permite la grabación del viaje. + Muestra una notificación del sistema que permite empezar la grabación del viaje. Viaje Grabado Grabar @@ -2121,7 +2118,7 @@ Un botón que añade una nota fotográfica en el centro de la pantalla. Un botón que añade una nota de OSM en el centro de la pantalla. Un botón que añade un PDI en el centro de la pantalla. - Un botón que des/activa las indicaciones por voz durante la navegación. + Un botón que activa o desactiva las indicaciones por voz durante la navegación. Un botón que añade la ubicación del aparcamiento en el centro de la pantalla. Mostrar un diálogo temporal " guardado como " @@ -2188,7 +2185,7 @@ \nProporciona un código completo OLC completo y válido. \nÁrea representada: %1$s x %2$s - Un botón que muestra la siguiente lista. + Un botón para paginar a través de la lista de abajo. Mapa superpuesto cambiado a «%s». Mapa subyacente cambiado a «%s». Pendiente @@ -2286,15 +2283,15 @@ \n ¡Más países alrededor del globo están disponibles para descargar! \n Obtén un navegador confiable en tu país - ya sea Francia, Alemania, México, Reino Unido, España, Países bajos, Estados Unidos, Rusia, Brasil o cualquier otro. Alternar zoom automático del mapa - Un botón que des/activa el zoom automático del mapa de acuerdo a la velocidad. - Activar zoom automático del mapa - Desactivar zoom automático del mapa + Botón para activar o desactivar el zoom automático controlado por la velocidad. + Activar zoom automático + Desactivar zoom automático Definir destino Reemplazar destino Añadir primer destino intermedio - Un botón que añade el destino de la ruta en el centro de la pantalla, cualquier destino previamente elegido se convierte en el último destino intermedio. - Este botón de acción, añade un nuevo destino de ruta en el centro de la pantalla, reemplazando el anterior destino (si existe). - Un botón que añade el primer destino intermedio en el centro de la pantalla. + Un botón que añade el centro de la pantalla como destino de la ruta, cualquier destino previamente elegido se convierte en el último destino intermedio. + Un botón que añade el centro de la pantalla como destino de la nueva ruta, reemplazando el anterior destino (si existe). + Un botón que añade el centro de la pantalla como el primer destino intermedio. Sin superposición Sin subyacencia Error @@ -2327,13 +2324,14 @@ Ubicación propia animada Activa el desplazamiento animado del mapa para «Mi ubicación» durante la navegación. Resumen - Navegación GPS + Navegación \n • Funciona en línea (rápido) o sin conexión (sin cargos de roaming al viajar al extranjero) \n • Guía por voz giro-a-giro (voces grabadas y sintetizadas) -\n • (Opcional) Guía de carriles, nombres de calles y tiempo estimado al destino -\n • Soporta puntos intermedios en el itinerario +\n • (Opcional) Guía de carriles, nombres de calles y tiempo estimado de llegada +\n • Soporta puntos intermedios en tu itinerario \n • La ruta se recalcula al salirse de la misma -\n • Busca destinos por dirección, por tipo (por ejemplo: Restaurantes, hoteles, gasolineras, museos), o por coordenada geográfica +\n • Busca lugares por dirección, por tipo (por ejemplo: Restaurantes, hoteles, gasolineras, museos), o por coordenadas geográficas +\n Vista del mapa \n • Muestra tu ubicación y orientación \n • (Opcional) Ajusta el mapa a la dirección del movimiento (o la brújula) @@ -2397,7 +2395,7 @@ Mostrar u ocultar notas de OSM Mostrar notas de OSM Ocultar notas de OSM - Un botón que muestra u oculta las notas de OSM en el mapa. + Botón para mostrar u ocultar las notas de OSM en el mapa. Ordenados por distancia Buscar en Favoritos Ocultar desde el nivel de zoom @@ -2419,7 +2417,7 @@ Abrir Mapillary Instalar Mejorar cobertura de fotos con Mapillary - Instala Mapillary para añadir una o más fotos a esta ubicación del mapa. + Instala Mapillary para añadir fotos a esta ubicación del mapa. Fotos en línea Imagen de Mapillary Permisos @@ -2449,10 +2447,10 @@ Min/Máx Rosa translúcido Pausar/reanudar navegación - Este botón de acción, pausa o reanuda la navegación. + Botón para pausar o reanudar navegación. Mostrar diálogo «Navegación finalizada» Iniciar/parar navegación - Este botón de acción, inicia o para la navegación. + Botón para iniciar o terminar la navegación. Tiempo del búfer para el seguimiento en línea Indica el tiempo que el búfer mantendrá los lugares para enviar sin conexión Añadir al menos un punto. @@ -2468,7 +2466,7 @@ Punto de ruta 1 Punto de referencia 1 Sin animaciones - Desactiva las animaciones en la aplicación. + Desactiva las animaciones de los mapas. Mantener en el mapa ¿Salir sin guardar? Línea @@ -2492,9 +2490,9 @@ Mover todo al historial Indicación de distancia Ordenar por - Elige cómo se indica la distancia y dirección a los marcadores del mapa en la pantalla del mapa: + Elige cómo se indica la distancia y dirección a los marcadores del mapa en el mapa: Umbral de orientación del mapa - Velocidad a partir de la cual la orientación del mapa cambia de «Dirección del movimiento» a «Dirección de la brújula». + Selecciona la velocidad a partir de la que la orientación del mapa cambia de «Dirección del movimiento» a «Dirección de la brújula». Todos los marcadores del mapa movidos al historial Marcador del mapa movido al historial Marcador del mapa movido a los activos @@ -2603,8 +2601,8 @@ Pulsa un marcador en el mapa para moverlo al primer lugar de los marcadores activos, sin abrir el menú contextual. Activar «Una pulsación» ¡Toma notas! - Añade una nota de audio, vídeo o foto para cada punto del mapa, utilizando el widget o el menú contextual. - Notas por fecha + Añade una nota de audio, vídeo o foto a cualquier punto del mapa, utilizando el control o el menú contextual. + Notas A/V por fecha Por fecha Por tipo Aquí hay: @@ -2852,7 +2850,7 @@ Guaraní Está utilizando el mapa «{0}» que funciona con OsmAnd. ¿Quiere ejecutar la versión completa de OsmAnd\? ¿Ejecutar OsmAnd\? - Un botón que alterna entre el modo diurno y nocturno para OsmAnd. + Un botón que alterna entre los modos diurno y nocturno para OsmAnd. Modo diurno Modo nocturno Alternar modos diurno/nocturno @@ -2956,7 +2954,7 @@ Autopista Carretera/ruta estatal Carretera principal - Calle residencial + Calle Vía de servicio Acera Camino rural @@ -3103,7 +3101,7 @@ Elegir el tipo de navegación Automóvil, camión, motocicleta Bicicleta de montaña, ciclomotor, caballo - Caminata, senderismo, correr + Caminata, senderismo, carrera Tipos de transporte público Barco, remo, vela Avión, ala delta @@ -3139,7 +3137,7 @@ Dificultad preferida Preferir rutas de esta dificultad, aunque el trazado sobre pistas más duras o más fáciles sigue siendo posible si son más cortas. Fuera de pista - Los senderos libres y fuera de pista son rutas y pasajes no oficiales. Típicamente descuidados, no mantenidos por los oficiales y no controlados por la noche. Entrar bajo su propio riesgo. + Los senderos libres y fuera de pista son rutas y pasajes no oficiales. Típicamente descuidados, no mantenidos y no controlados por la noche. Entra bajo tu propio riesgo. Geocodificación Error Todo terreno @@ -3361,7 +3359,7 @@ El nombre de archivo está vacío Traza guardada Revertir - Un botón para centrar en la pantalla el punto de partida y calcular la ruta hacia el destino o abre un cuadro de diálogo para elegir el destino si el marcador no está en el mapa. + Un botón que añade el centro de la pantalla como punto de partida. Pedirá luego que se fije el destino o iniciará el cálculo de la ruta. Mostrar nodo de la red de rutas ciclistas ¿Borrar %1$s\? Diálogo de descarga del mapa @@ -3394,7 +3392,7 @@ Elegir el color Los perfiles predefinidos de OsmAnd no pueden borrarse , pero sí desactivarse (en la pantalla anterior), o moverse a la parte inferior. Editar perfiles - El \'tipo de navegación\' domina como se calculan las ruta. + El \'Tipo de navegación\' determina cómo se calculan las rutas. Apariencia del perfil Icono, color y nombre Editar lista de perfiles @@ -3679,7 +3677,7 @@ Buscar tipos de PDI Acción %1$s no admitida Mapa general del mundo (detallado) - Tipo no admitido + Tipo no soportado Proporciona la anchura de tu vehículo, algunas restricciones de ruta pueden aplicarse para vehículos anchos. Proporciona la altura de tu vehículo, algunas restricciones de ruta pueden aplicarse para vehículos altos. Proporciona el peso de tu vehículo, algunas restricciones de ruta pueden aplicarse para vehículos pesados. @@ -3715,7 +3713,7 @@ Elige cómo se guardarán las teselas descargadas. Puede exportar o importar acciones rápidas con perfiles de aplicación. ¿Eliminar todo\? - ¿Estás seguro de que deseas eliminar irrevocablemente% d acciones rápidas\? + ¿Estás seguro deseas eliminar de forma irreversible %d acciones rápidas\? Tiempo de apagado de la pantalla Si \"%1$s\" está encendido, el tiempo de actividad dependerá de ello. metros @@ -3740,7 +3738,7 @@ Urdu Tayiko Bávaro - Rastreador OsmAnd + Trazador OsmAnd La guía para la simbología del mapa. Posiciones de estacionamiento Deshabilitado. Requiere \'Mantener la pantalla encendida\' dentro de \'Tiempo de espera después de la activación\'. @@ -3849,7 +3847,7 @@ Distancia umbral Perfil de navegación Selecciona un archivo de traza al que agregar el nuevo segmento. - Imágenes a pie de calle + Fotos a pie de calle ¿Estás seguro de que quieres descartar todos los cambios en la ruta planeada cerrándola\? En caso de dirección contraria Guardar como nuevo archivo de traza @@ -3897,4 +3895,4 @@ Último modificado Nombre: Z – A Nombre: A - Z - + \ No newline at end of file diff --git a/OsmAnd/res/values-et/phrases.xml b/OsmAnd/res/values-et/phrases.xml index 6d72d50576..4cb42eee67 100644 --- a/OsmAnd/res/values-et/phrases.xml +++ b/OsmAnd/res/values-et/phrases.xml @@ -524,7 +524,7 @@ Oktaan 95 Oktaan 98 Oktaan 100 - CNG + Surugaas 1:25 kütus 1:50 kütus Etanool @@ -3826,4 +3826,5 @@ Väikesed elektriseadmed Mesitaru Pähklipood + Veeldatud maagaas \ No newline at end of file diff --git a/OsmAnd/res/values-et/strings.xml b/OsmAnd/res/values-et/strings.xml index e219936c08..fe5cbbfa20 100644 --- a/OsmAnd/res/values-et/strings.xml +++ b/OsmAnd/res/values-et/strings.xml @@ -40,7 +40,7 @@ Start Stop Import - Eksport + Ekspordi Veel… Veel tegevusi Ära enam näita @@ -910,7 +910,7 @@ Seadista teekonna parameetrid Teekonna parameetrid Rakenduse profiiliks muudetud \"%s\" - Logcat puhver + Logcati puhver Laienduse seaded Vaikimisi Selle ala vaatlemiseks lae alla üksikasjalik %s kaart. @@ -3364,7 +3364,7 @@ OSM Ikooni kuvatakse vaid navigeerimise või liikumise ajal. Peatumisel näidatav ikoon. - Kontrolli ja jaga rakenduse detailseid logisid + Vaata ja jaga rakenduse detailseid logisid Geokavatsuse väärtusest \'%s\' ei saanud aru. Selle valiku kasutamine vajab luba. See on madala kiirusega väljalülitusfilter, et mitte salvestada punkte, mis jäävad alla teatud kiiruse. See võib muuta salvestatud rajad kaardil vaadates sujuvamaks. @@ -3497,7 +3497,7 @@ Kohandatud värv Jätkamiseks vali tööpäevad Teekond punktide vahel - Kavanda teekonda + Kavanda teekond Lisa rajale Näita alguse ja lõpu ikoone Vali laius @@ -3762,4 +3762,15 @@ Viimati muudetud Nimi: Z – A Nimi: A – Z + Ekraani väljalülitamine + Ratastool edasi + Väldi jalgteid + Väldi jalgteid + Täname „Kõrgusjoonte“ ostu eest + Ostukinnituse saabumisel arveldame tellimuse eest sinu AppGallery konto alusel. +\n +\nKui sa ei tühista tellimust enne uue perioodi algust, siis tellimus pikeneb automaatselt ning arveldame järgmise ajavahemiku eest (kuu/kvartal/aasta) selle alguses. +\n +\nTellimust saad hallata ja tühistada AppGallery seadistustest. + Arveldame tellimuse eest valitud ajavahemiku alusel. Seda saad sa vabalt valitud ajal tühistada AppGallery\'s. \ No newline at end of file diff --git a/OsmAnd/res/values-fa/phrases.xml b/OsmAnd/res/values-fa/phrases.xml index eba643a4f9..dd6c374db3 100644 --- a/OsmAnd/res/values-fa/phrases.xml +++ b/OsmAnd/res/values-fa/phrases.xml @@ -9,7 +9,7 @@ فروشگاه گوشت بقالی فروشگاه محصولات دامی - سبزی فروشی + میوه و سبزی‌فروشی فروشگاه غذاهای دریایی شیرینی و آجیل فروشی بستنی فروشی diff --git a/OsmAnd/res/values-fa/strings.xml b/OsmAnd/res/values-fa/strings.xml index 4d8d47c875..68b4a4511d 100644 --- a/OsmAnd/res/values-fa/strings.xml +++ b/OsmAnd/res/values-fa/strings.xml @@ -3278,7 +3278,7 @@ تور اسکی مسیرها برای تور اسکی. کمپر - ون کمپر + ون کمپر (RV) واگن کامیون پیک‌آپ تحلیل‌ها @@ -3553,7 +3553,7 @@ پروفایل سفارشی زاویه: ‎%s° زاویه - تا مسیریابی مجدد انجام شود، از موقعیت من تا مسیر محاسبه‌شده پاره‌خط مستقیمی نمایش داده می‌شود + تا مسیریابی مجدد انجام نشده، از موقعیت من تا مسیر محاسبه‌شده پاره‌خط مستقیمی نمایش داده می‌شود کمترین زاویه میان موقعیت من و مسیر آماده‌سازی چیزی انتخاب نشده @@ -3873,7 +3873,7 @@ ذخیره به‌عنوان رد جدید برعکس‌کردن مسیر تمام رد با استفاده از پروفایل انتخابی بازمحاسبه خواهد شد. - با استفاده از پروفایل انتخابی فقط پارهٔ بعدی بازمحاسبه خواهد شد. + فقط پارهٔ بعدی با استفاده از پروفایل انتخابی بازمحاسبه خواهد شد. همهٔ پاره‌های بعدی پارهٔ قبلی همهٔ پاره‌های قبلی @@ -3928,4 +3928,9 @@ بازهٔ زمانی برای ضبط رد را انتخاب کنید (که از طریق ابزار ضبط سفر روی نقشه فعال می‌شود). نگه‌داشتن ضبط سفر ازسرگیری ضبط سفر + اسکیت این‌لاین + موتور پرشی + اسکوتر موتوری + ویلچر رو به جلو + فاصله آستانه \ No newline at end of file diff --git a/OsmAnd/res/values-fr/phrases.xml b/OsmAnd/res/values-fr/phrases.xml index 638995c1f2..91dc180070 100644 --- a/OsmAnd/res/values-fr/phrases.xml +++ b/OsmAnd/res/values-fr/phrases.xml @@ -1762,7 +1762,7 @@ Fournitures de plomberie Fournitures de bois Ancres pour vélo - Râtelier pour vélo + Arceaux pour vélo Terminal d\'informations Carte tactile Tableau d\'affichage @@ -3380,7 +3380,7 @@ Parc animalier Enceinte Parc safari - Râtelier pour vélo + Arceaux pour vélo Vélo de sport Hachoir Hors route diff --git a/OsmAnd/res/values-fr/strings.xml b/OsmAnd/res/values-fr/strings.xml index 91daebce7f..401664b66e 100644 --- a/OsmAnd/res/values-fr/strings.xml +++ b/OsmAnd/res/values-fr/strings.xml @@ -3412,7 +3412,7 @@ OSM Icône affiché pendant la navigation ou en déplacement. Icône affiché à l\'arrêt. - Consultez et partagez les journaux de l\'application (pour debug) + Vérifier et partager les logs détaillés de l\'application Vous n\'êtes pas autorisés à utiliser cette option. Intervalle de suivi Adresse web @@ -3883,4 +3883,9 @@ Dernière modification Nom : Z – A Nom : A – Z + Icônes de départ / arrivée + Merci pour votre achat de \'Courbes de niveaux\' + Abonnement facturé pour chaque période sélectionnée. Annulation possible à tout moment sur AppGallery. + Éviter les trottoirs + Éviter les trottoirs \ No newline at end of file diff --git a/OsmAnd/res/values-hu/phrases.xml b/OsmAnd/res/values-hu/phrases.xml index 4e0cce87d3..0234cd76a2 100644 --- a/OsmAnd/res/values-hu/phrases.xml +++ b/OsmAnd/res/values-hu/phrases.xml @@ -294,7 +294,7 @@ Forgalomlassító útszűkület Gázolaj Biodízel - Cseppfolyós gáz (LPG) + LPG (cseppfolyósított PB-gáz) 80-as oktánszámú 91-es oktánszámú 92-es oktánszámú @@ -3835,4 +3835,5 @@ Ivóvíz-utántöltés Mag- és aszaltgyümölcsbolt Méhkaptár + LNG (cseppfolyósított földgáz) \ No newline at end of file diff --git a/OsmAnd/res/values-hu/strings.xml b/OsmAnd/res/values-hu/strings.xml index 97f76c0623..b903ccb8da 100644 --- a/OsmAnd/res/values-hu/strings.xml +++ b/OsmAnd/res/values-hu/strings.xml @@ -3277,7 +3277,7 @@ Térképnézet Alapértelmezés visszaállítása Másolás egy másik profilból - Logcat-puffer + Logcat-puffer (hibanapló) Alapértelmezés szerint Hópark Lovas szán @@ -3424,8 +3424,8 @@ OSM-szerkesztés Az összes még fel nem töltött szerkesztés vagy OSM-hiba megtalálható a %1$s helyen. A már feltöltött pontok nem láthatók az OsmAndban. OSM - Az ikon navigáció vagy haladás közben jelenik meg. - Az ikon álló helyzetben jelenik meg. + Navigáció vagy haladás közben megjelenő ikon. + Álló helyzetben megjelenő ikon. Az alkalmazás részletes naplóinak ellenőrzése és megosztása A beállítás használatához engedélyre van szükség. Kategóriák átrendezése @@ -3896,4 +3896,6 @@ Utolsó módosítás Név: Z–A Név: A–Z + Kiindulás/érkezés ikonjai + Köszönjük, hogy megvásárolta a szintvonalbővítményt (Contour lines) \ No newline at end of file diff --git a/OsmAnd/res/values-it/strings.xml b/OsmAnd/res/values-it/strings.xml index e806eff554..919b6123a8 100644 --- a/OsmAnd/res/values-it/strings.xml +++ b/OsmAnd/res/values-it/strings.xml @@ -1954,7 +1954,7 @@ Navigazione OsmAnd Live Livello della batteria Ungherese (formale) - Tracciato attuale + Traccia attuale Cambia posizione del marcatore Spagnolo americano Asturiano @@ -3620,7 +3620,7 @@ Mappa mondiale generale (dettagliata) Mappe extra Azione non supportata %1$s - OsmAnd tracker + Tracker OsmAnd OsmAnd + Mapillary Azione veloce Righello radiale @@ -3812,7 +3812,7 @@ Aggiungi ad una Traccia Seleziona l\'intervallo a cui i segnaposti con distanza o orario sulla traccia verranno mostrati. Seleziona l\'opzione desiderata per la divisione: per tempo o per distanza. - Frecce delle direzioni + Frecce della direzione Seleziona larghezza Ultima modificata Importa una traccia @@ -3900,7 +3900,9 @@ \n \n Traccia semplificata - Ultimo modificato + Cronologico Nome: Z – A Nome: A – Z + Icona Partenza/Arrivo + Grazie per l\'acquisto del \'Plugin delle curve di livello\' \ No newline at end of file diff --git a/OsmAnd/res/values-iw/phrases.xml b/OsmAnd/res/values-iw/phrases.xml index c625ca9fdd..d593ffb9d0 100644 --- a/OsmAnd/res/values-iw/phrases.xml +++ b/OsmAnd/res/values-iw/phrases.xml @@ -1390,7 +1390,7 @@ שיתוף רכב שיתוף סירות מעגן - בית שימוש + בית שימוש;אסלה;טואלט מקלחת סאונה בית בושת @@ -2043,7 +2043,7 @@ אגרה למשאיות כן כן - לא + שמע: לא רק כאשר מותר לחצות תחנת הצלה אזור שירות @@ -2119,7 +2119,7 @@ דלק 100LL גפ״מ דלק מטוסים A-1 - תוסף AdBlue + נוזל מפלט לדיזל כספומט מקום בנסיעה שיתופית וסליאן @@ -2159,4 +2159,19 @@ כן מצוק טיפוס קטגוריית קושי + צינור + רשת מילוי מי שתייה + מילוי מי שתייה: אין + כן + תיבת תרומה + חץ: אין + כן + כן + מעלית + זמן אמת + השהיה + כן + לוח זמנים + כוורת דבורים + חנות אגוזים \ No newline at end of file diff --git a/OsmAnd/res/values-iw/strings.xml b/OsmAnd/res/values-iw/strings.xml index 7f556c9926..6b405f881a 100644 --- a/OsmAnd/res/values-iw/strings.xml +++ b/OsmAnd/res/values-iw/strings.xml @@ -2103,7 +2103,7 @@ ביטול הבחירה למחוק הכול שיתוף - יצוא + ייצוא עוד… שמירת הבחירה שגיאה בלתי צפויה @@ -3248,7 +3248,7 @@ התצורה הנבחרת תחול בכל רחבי היישומון. הגדרה זו נבחרת כבררת מחדל על הפרופילים: %s החלפת הגדרה - זיכרון זמני של Logcat + מכלא Logcat כבררת מחדל הגדרות תוסף הורדת המפה המפורטת %s, כדי לצפות באזור זה. @@ -3428,7 +3428,7 @@ סמל שמופיע רק בעת ניווט או תזוזה. סמל שמופיע במנוחה. דירוג - לבדוק ולשתף תיעוד מפורט של יומני היישומון + בדיקה ושיתוף יומני תיעוד מפורטים של היישומים נדרשת הרשאה כדי להשתמש באפשרות הזו. סידור הקטגוריות מחדש ניתן להחליף את סדר הרשימה ולהסתיר קטגוריות בלתי נחוצות. אפשר לייבא או לייצא את כל השינויים עם פרופילים. @@ -3909,4 +3909,14 @@ שינוי אחרון שם: ת – א שם: א – ת + סמלי התחלה/סיום + תודה לך על רכישת ‚קווי מתאר’ + הימנעות משבילי הולכי רגל + הימנעות משבילי הולכי רגל + המינוי חויב לתקופה הנבחרת. ניתן לבטל דרך ה־AppGallery בכל עת. + התשלום יחויב דרך חשבון ה־AppGallery שלך עם אישור הרכישה. +\n +\nהמינוי מתחדש אוטומטי אלא אם כן בוטל בטרם תאריך החידוש. החשבון שלך יחויב על תקופת החידוש (חודש/שלושה חודשים/שנה) רק בתאריך החידוש. +\n +\nניתן לנהל ולבטל את המינויים שלך דרך ההגדרות ב־AppGallery שלך. \ No newline at end of file diff --git a/OsmAnd/res/values-ja/phrases.xml b/OsmAnd/res/values-ja/phrases.xml index 680da76205..66c5cc2a31 100644 --- a/OsmAnd/res/values-ja/phrases.xml +++ b/OsmAnd/res/values-ja/phrases.xml @@ -1258,7 +1258,7 @@ 有り 掃除機:無し 掃除機 - アドブルー・尿素水還元剤 + ディーゼル排気用液(AdBlue・尿素水) ドライブスルー 有り 無し @@ -3822,4 +3822,16 @@ 吸引 加圧 地下水 + ナッツ専門店 + 養蜂箱 + リアルタイム時刻表 + 一般的な時刻表 + 大まかな時刻表 + 有り + 時刻表:無し + エレベーター + 街区 + 行政区 + ギブボックス(提供品置場) + 簡易給水栓 \ No newline at end of file diff --git a/OsmAnd/res/values-ja/strings.xml b/OsmAnd/res/values-ja/strings.xml index 80f0b62286..35f9146fc7 100644 --- a/OsmAnd/res/values-ja/strings.xml +++ b/OsmAnd/res/values-ja/strings.xml @@ -1190,7 +1190,7 @@ POIの更新は利用できません "出発時刻: %1$tF, %1$tT " "到着時刻: %1$tF, %1$tT " "平均速度: %1$s " - "最高速度: %1$s " + 最高速度: %1$s 平均標高: %1$s 標高差: %1$s 上り/下り: %1$s @@ -1350,7 +1350,7 @@ POIの更新は利用できません 画面の電源オン設定 方向転換地点に近づいたらデバイスの画面を(オフの場合指定時間)オンにします しない - 除外する道の指定… + 避ける道の指定… 電車でのルート 路面電車でのルート タクシーのルート共有 @@ -1625,7 +1625,7 @@ POIの更新は利用できません 所属ネットワークに応じたカラー変更 OSMCのハイキングシンボルカラー 被災域 - 太線 + 輪郭強調 更新はありません ライブ更新 ユーザーからの意見やフィードバックを大切にしています。 @@ -1816,7 +1816,7 @@ POIの更新は利用できません 上に移動 下に移動 ナビゲーションの終了 - 使用しない道路として指定 + 避ける道を指定 選択したデータ保存フォルダーが書き込み保護されているため、内部メモリに切り替えました。書き込み可能な保存用ディレクトリを選択してください。 共有記憶域 より詳細なレポートは以下サイトにて @@ -2496,7 +2496,7 @@ POIの更新は利用できません 今年 全て履歴に移動 距離表示 - 並び順の変更 + 並び順: マップ上に表示し続ける 保存せずに終了しますか? @@ -3010,9 +3010,9 @@ POIの更新は利用できません アイコン選択 基本プロファイル アイコン - 最低速度 - 最高速度 - 標準移動速度 + 予想最低速度 + 予想最高速度 + 予想標準速度 すべての道路の移動速度を制限し、種別や制限速度が不明な道路が多い場合の到着時間予測に役立ちます(ルート計算に影響します) オフロード プロファイルの個別設定 @@ -3590,7 +3590,7 @@ POIの更新は利用できません POIの作成/編集 駐車位置 お気に入りの追加/編集 - アイテム順序をデフォルトに戻す + アイテム順序を初期状態に戻します 編集に戻る 選択したプロファイルを切り替えるボタンです。 プロファイルの追加 @@ -3667,12 +3667,12 @@ POIの更新は利用できません コンテキストメニュー 項目の並べ替えや非表示するものを指定できます。 分割 - 分割線で区切られた部分より下にある項目が適用されます。 + ここで指定された項目は、区切り線より下に配置されます。 非表示 これらの項目はメニューに表示されなくなりますが、オプションやプラグインはそのまま機能します。 項目 設定を非表示にすると、元の状態にリセットされます。 - ボタンは4つしかありません。 + ボタンの数は4つ固定で変更できません。 主要機能 “%1$s”ボタンをタップすると、これらの機能にアクセスできます。 アイテムはこのカテゴリ内でのみ移動できます。 @@ -3794,7 +3794,7 @@ POIの更新は利用できません \n \n国の法律に基づいて、使用を望むかどうかを決定する必要があります。 \n -\n%1$sを選択すると、スピードカメラに関するアラートと警告が表示されます。 +\n%1$sを選択すると、スピードカメラに関するアラートと警告機能を使用できます。 \n \n%2$sを選択すると、スピードカメラに関するすべてのデータ(警告、通知、POI)が、OsmAndの再インストールを行うまで削除されます。 機能を維持 diff --git a/OsmAnd/res/values-nb/strings.xml b/OsmAnd/res/values-nb/strings.xml index 32c34dd961..9c24c2aa7c 100644 --- a/OsmAnd/res/values-nb/strings.xml +++ b/OsmAnd/res/values-nb/strings.xml @@ -751,7 +751,7 @@ Sikker modus Programmet kjører i sikker modus (skru det av i \'Innstillinger\'). Talemeldinger stopper midlertidig musikkavspilling. - Avbryt musikk + Sett musikk på pause Offentlig Optimaliser kart for Vis fra zoom-nivå (krever kotedata): @@ -1650,7 +1650,7 @@ Valgt valgte FJERN MERKELAPPEN - Last ned nattlige utviklerversjoner. + Last ned aktuelle utviklingsversjoner. Byggversjoner Spesifiser en mellomtjener. Innlogget som %1$s @@ -2142,7 +2142,7 @@ Vis/skjul OSM-notater Vis OSM-notater Skjul OSM-notater - Å trykke denne handlingsknappen viser eller skjuler OSM-notater på kartet. + Knapp til å vise eller skjule OSM-notater på kartet. Takk for at du kjøpte \'Havdybdekonturer\' Havdybdekonturer Havdybdekonturer @@ -2257,17 +2257,17 @@ Rediger handling Slett handling Navneforvalg - Trykk på denne handlingsknappen legger til en kartmarkør i skjermsenteret. - Trykking på denne handlingsknappen legger til et GPX-rutepunkt i midten av skjermen. - Trykking på denne handlingsknappen legger til et lydnotat i midten av skjermen. - Trykking på denne handlingsknappen legger til et videonotat i midten av skjermen. - Trykking på denne handlingsknappen legger til et bildenotat i midten av skjermen. - Trykking på denne handlingsknappen legger til et OSM-notat i midten av skjermen. - Trykking på denne handlingsknappen slår av eller på taleveiledning under navigering. - Trykking på denne handlingsknappen legger til en parkeringsplass i midten av skjermen. + En knapp for å legge til en kartmarkør i skjermsenteret. + En knapp for å legge til et GPX-rutepunkt i midten av skjermen. + En knapp for å legge til et lydnotat i midten av skjermen. + En knapp for å legge til et videonotat i midten av skjermen. + En knapp for å legge til et bildenotat i midten av skjermen. + En knapp for å legge til et OSM-notat i midten av skjermen. + En knapp til å slå av eller på taleveiledning under navigering. + En knapp for å legge til en parkeringsplass i midten av skjermen. Vis en midlertidig dialog " lagret i " - Trykking på denne handlingsknappen viser eller skjuler favorittpunktene på kartet. + En knapp til å vise eller skjule favorittpunktene på kartet. Opprett elementer La stå tomt for å bruke adressen eller stedsnavnet. Denne meldingen inkluderes i kommentarfeltet. @@ -2341,10 +2341,10 @@ Et trykk på kartet skjuler/viser kontrollknappene og miniprogrammene. Marker som passert Kunne ikke endre notatet. - Trykking på denne handlingsknappen slår av/på automatisk kartforstørrelse i henhold til hastigheten din. - Trykking på denne handlingsknappen gjør skjermsenteret til rutemål, ethvert tidligere valgt reisemål blir det siste mellomliggende målet. - Trykking på denne handlingsknappen gjør skjermsenteret det nye rutemålet, og erstatter det tidligere valgte reisemålet (hvis noe). - Trykking på denne handlingsknappen gjør skjermsenteret til det første mellomliggende reisemålet. + Knapp til å slå av eller på hastighetsbasert auto-zoom. + En knapp for å gjøre skjermsenteret til rutemålet, et tidligere valgt reisemål blir det siste mellomliggende målet. + En knapp for å gjøre skjermsenteret til det nye rutemålet, erstatter det tidligere valgte reisemålet (hvis noe). + En knapp for å gjøre skjermsenteret til det første mellomliggende reisemålet. Abonner på vår e-postliste om programrabatter og få tre kartnedlastinger til! Havdybdepunkter for sørlige halvkule Havdybdepunkter for nordlige halvkule @@ -2382,7 +2382,7 @@ Aktiver \"sjøkartvisning\" -tillegget Navnet inneholder for mange store bokstaver. Fortsett\? Legg til interessepunkt - Trykking på denne handlingsknappen viser eller skjuler interessepunkter på kartet. + En knapp til å vise eller skjule interessepunkter på kartet. En knapp til å bla gjennom listen nedenfor. Fyll ut alle parametere Ved å trykke lenge og dra knappen endres dens plassering på skjermen. @@ -2408,7 +2408,7 @@ Alle punkter i gruppen GPX-fil med de valgte notatenes koordinater og data. GPX-fil med alle notaters koordinater og data. - Trykking på denne handlingsknappen legger til et interessepunkt i midten av skjermen. + En knapp for å legge til et interessepunkt i midten av skjermen. Åpen fra Åpen til Stenger @@ -2859,10 +2859,10 @@ Du bruker {0} kart levert av OsmAnd. Vil du starte fullversjonen av OsmAnd\? Kjør OsmAnd\? Guaraní - Vekselvender mellom dag- og nattmodus i OsmAnd + En knapp til å skifte mellom dag- og nattmodus i OsmAnd. Dagmodus Nattmodus - Veksle dag-/nattmodus + Bytt dag/natt-modus Offentlig transport Sett reisemål Legg til mellomliggende @@ -3256,7 +3256,7 @@ OsmAnd-profil: %1$s Profil-import Hvit - Brukes til å estimere ankomsttid for ukjente veityper, og for å begrense farten på alle veier (kan endre rute) + Estimerer ankomsttid for ukjente veityper og begrenser farten for alle veier (kan påvirke ruting) Spor-lagringsmappe Spor kan lagres i \'rec\'-mappen, månedlige eller daglige mapper. Ta opp spor til \'rec\'-mappen @@ -3286,13 +3286,13 @@ \n \n Du kan bruke denne endringen på alle eller bare på den valgte profilen. - En bryter for å vise eller skjule koter på kartet. + Knapp som viser eller skjuler koter på kartet. Eksporter profil \"%1$s\" finnes allerede. Overskriv\? Kunne ikke eksportere profil. Legg til en profil ved å åpne profilfilen med OsmAnd. - %1$s feil under import: %2$s - %1$s ble importert. + %1$s importfeil: %2$s + %1$s importert. Bytt %1$s og %2$s Startpunkt Isvei @@ -3345,20 +3345,20 @@ Åpen plasseringskode (OLC) Valgt format vil benyttes i programmet. Denne innstillingen er valgt som forvalg for profiler: %s - Bruk kun for «%1$s» - Bruk for alle profiler + Bruk kun for \"%1$s\" + Bruk på alle profiler Analyseinstrumenter Vis kart på låseskjermen under navigering. - Innstillinger for ruting i valgt profil «%1$s». + Innstillinger for ruting i den valgte profilen \"%1$s\". Tidsavbrudd etter oppvåkning Varsler vises nede til venstre under navigering. Kart under navigasjon Kart under navigasjon Stemmekunngjøringer finner kun sted under navigasjon. Navigasjonsinstruks og kunngjøringer - Stemmekunngjøringer + Talemeldinger Sett opp ruteparameter - Ruteparameter + Ruteparametere Last ned detaljert %s-kart for å vise dette området. Slede Akebrett @@ -3398,13 +3398,13 @@ Personlig Laster ned %s Tykk - For ørkener og andre tynt befolkede områder. Høyere detaljnivå. + For ørkener og andre tynt befolkede områder. Mer detaljert. Posisjonsikon under bevegelse Posisjonsikon i hviletilstand Trykk på \'Bruk\' sletter fjernede profiler permanent. Hovedprofil Velg farge - Du kan ikke slette forvalgsprofilene, men du kan skru dem av før dette steget, eller flytte dem til bunnen. + OsmAnd-Standardprofiler kan ikke slettes, men deaktiveres (på forrige skjermbilde), eller bli sortert til bunnen. Rediger profiler Navigasjonstype har innvirkning på regler for ruteberegning. Profilutseende @@ -3418,8 +3418,8 @@ %1$s %2$s Importer profil OSM - \"%1$s\"-filen inneholder ingen ruteplanleggingsregler, velg en annen fil. - Ustøttet filtype. Du må velge en fil med %1$s-filendelse. + Ingen rutingsregler i \'%1$s\'. Velg en annen fil. + Velg en støttet fil med %1$s-endelse isteden. Importer fra fil Importer ruteplanleggingsfil Navigasjon, loggingsnøyaktighet @@ -3429,7 +3429,7 @@ Tillater deg å dele nåværende plassering ved bruk av turopptak. Nettbasert sporing Loggingsnøyaktighet - Du kan finne alle dine innspilte spor i %1$s, eller i OsmAnd-mappen. + Dine innspilte spor er i %1$s, eller i OsmAnd-mappen. Du finner alle dine OSM-notater i %1$s. Videonotater Bildenotater @@ -3601,7 +3601,7 @@ Sjekk og del detaljert loggføring fra programmet Bruk systemets skjermtidsavbrudd Programtillegg av - Ingen omregning + Ingen ny beregning Angi et navn for profilen Velg data å importere. Du kan lese mer om løyper i %1$s. @@ -3627,7 +3627,7 @@ \nSkru av ubrukte programtillegg for å skjule alle deres styringskontroller. %1$s. Disse elementene er skjult fra menyen, men de representerte valgene eller programtilleggene vil fortsette å virke. Velg språkene Wikipedia-artikler skal vises på i kartet. Du kan bytte mellom alle tilgjengelige språk mens du leser artikkelen. - Veiledning til kartets merking. + Veiledning til kartets symbolbruk. Ruteplanlegging Minsteavstand for å beregne rute på nytt OsmAnd har allerede elementer med samme navn som de i importen. @@ -3662,7 +3662,7 @@ Fotoboks-interessepunkter Avinstaller Du kan sette fartøyhøyde for å unngå lave broer. Hvis broen endrer høyde, brukes høyden i åpen tilstand. - Slett neste målpunkt + Slett nærmeste målpunkt Navngi punktet Vis/skjul Mapillary Skjul Mapillary @@ -3675,19 +3675,19 @@ Navigasjonsinstruks - + Avinstaller og start på nytt Rullestol Gokart Planlegg en rute Du får tilgang til disse handlingene ved å trykke på knappen “%1$s”. - Slå på for å stille inn zoomnivået på kartet med enhetens volumknapper. + Styr zoomnivået på kartet med enhetens volumknapper. Det tillagte punktet vil ikke være synlig på kartet, siden den valgte gruppen er skjult, du kan finne det i \"%s\". Scooter Rullestol framover - Du må definere arbeidsdagene for å fortsette + Still inn arbeidsdager for å fortsette Legg til i et spor - Vis ikoner for start-mål + Vis ikoner for start og mål Velg bredde Velg intervallet hvor markeringer med avstand eller tid på sporet vil vises. Velg det ønskede oppdelingsalternativet: etter tid eller etter avstand. @@ -3702,7 +3702,7 @@ Sist redigert Importer spor Åpne eksisterende spor - Velg en sporfil for åpning. + Velg en sporfil for å åpne. Snu rute Overskriv spor Hele sporet blir beregnet på nytt med den valgte profilen. @@ -3722,7 +3722,7 @@ Importer eller ta opp sporfiler Følg spor Velg sporfil å følge - Velg sporfil å følge, eller importer en. + Velg sporfil å følge eller importer fra enheten din. Velg et annet spor Starten av sporet Nærmeste punkt @@ -3747,4 +3747,15 @@ Sist endret Navn: Å - A Navn: A - Å + Start/mål-ikoner + Lagre som ny sporfil + Legg til i en sporfil + Spor + Spor + Spor + Turopptak + Lagre som sporfil + %s sporfiler valgt + Sett turopptak på pause + Gjenoppta turopptak \ No newline at end of file diff --git a/OsmAnd/res/values-pl/phrases.xml b/OsmAnd/res/values-pl/phrases.xml index 4b3efcc10c..faec609ff4 100644 --- a/OsmAnd/res/values-pl/phrases.xml +++ b/OsmAnd/res/values-pl/phrases.xml @@ -3812,7 +3812,7 @@ Skontrastowane Uzupełnianie wody pitnej: woda z sieci Uzupełnianie wody pitnej: nie - Uzupełnianie wody pitnej: tak + Tak Poziom wody: utrzymujący się na powierzchni Poziom wody: poniżej średniego poziomu wody Poziom wody: obmywający falami @@ -3834,4 +3834,7 @@ Stan pompy: brak wiązki Strzałka: nie Winda + Małogabarytowe urządzenia elektryczne + Tablica odjazdów/odlotów + Uzupełnianie wody pitnej \ No newline at end of file diff --git a/OsmAnd/res/values-pt-rBR/phrases.xml b/OsmAnd/res/values-pt-rBR/phrases.xml index dd7c249f87..07c80c478d 100644 --- a/OsmAnd/res/values-pt-rBR/phrases.xml +++ b/OsmAnd/res/values-pt-rBR/phrases.xml @@ -3844,4 +3844,5 @@ Pequenos aparelhos elétricos Colmeia Loja de nozes + GNL \ No newline at end of file diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml index a2c4186817..457106d81c 100644 --- a/OsmAnd/res/values-pt-rBR/strings.xml +++ b/OsmAnd/res/values-pt-rBR/strings.xml @@ -3427,7 +3427,7 @@ OSM Ícone mostrado ao navegar ou mover. Ícone mostrado em repouso. - Verifique e compartilhe logs detalhados do aplicativo + Verifique e compartilhe registros detalhados do aplicativo Não foi possível analisar a intenção geográfica \'%s\'. É necessária permissão para usar esta opção. Este é um filtro de corte de baixa velocidade para não registrar pontos abaixo de uma determinada velocidade. Isso pode fazer com que as faixas gravadas pareçam mais suaves quando visualizadas no mapa. @@ -3899,4 +3899,14 @@ Última modificação Nome: Z – A Nome: A – Z + Ícones de início/término + Obrigado por adquirir \'curvas de nível\' + Assinatura cobrada por período selecionado. Cancele no AppGallery a qualquer momento. + O pagamento será cobrado em sua conta AppGallery na confirmação da compra. +\n +\nA assinatura é renovada automaticamente, a menos que seja cancelada antes da data de renovação. Sua conta será cobrada pelo período de renovação (mês/três meses/ano) apenas na data de renovação. +\n +\nVocê pode gerenciar e cancelar suas assinaturas acessando as configurações do AppGallery. + Evite passarelas + Evite passarelas \ No newline at end of file diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml index 8d22b73dbe..fc6f81cb62 100644 --- a/OsmAnd/res/values-pt/phrases.xml +++ b/OsmAnd/res/values-pt/phrases.xml @@ -436,7 +436,7 @@ Central telefônica Reciclagem Centro de reciclagem - Contêiner + Contentor Vidro Papel Roupas @@ -1775,7 +1775,7 @@ Brinquedos Sorvete Cartão SIM - Seção + Secção Memorial de guerra Placa comemorativa Estátua @@ -2400,7 +2400,7 @@ Passageiros Veículos Bicicletas - Contêineres + Contentor Veículos pesados Academia ao ar livre Hackerspace @@ -2808,9 +2808,9 @@ Tipo de bomba: gravidade Estilo de bomba: moderno Estilo de bomba: histórico - Status da bomba: ok - Status da bomba: quebrado - Status da bomba: bloqueado + Estado da bomba: ok + Estado da bomba: quebrado + Estado da bomba: bloqueado Troika Cartão Troika não aceito Telescópio @@ -3563,7 +3563,7 @@ 3B 3B* Explosão de gás;Queimador de gás - Objeto excluído + Objeto apagado Caixa de resgate Sim Reddit @@ -3829,4 +3829,5 @@ Pequenos aparelhos elétricos Colmeia Loja de nozes + GNL \ No newline at end of file diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index fe6c5d23b1..cae1cc8c1e 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -239,7 +239,7 @@ Meio de transporte: Por favor, define o destino primeiro Navegação - A aplicação do estado do GPS não está instalada. Pesquisar na loja de aplicações\? + A app do estado do GPS não está instalada. Pesquisar na loja de apps\? Horas de abertura Abrindo conjunto de alterações … Fechando conjunto de alterações… @@ -294,7 +294,7 @@ Adicionar aos \'Favoritos\' Escolher entre os nomes nativos e inglês. Usar nomes em inglês - Configurações da aplicação + Configurações da app Pesquisar endereço Escolher edifício Escolher rua @@ -486,7 +486,7 @@ Usar cores fluorescentes para mostrar trajetos e rotas. Edição offline Usar sempre a edição offline. - As alterações de POI dentro da aplicação não afetam os ficheiros de mapas descarregados; essas alterações são guardadas num ficheiro separado no seu aparelho. + As alterações de POI dentro da app não afetam os ficheiros de mapas descarregados; essas alterações são guardadas num ficheiro separado no seu aparelho. A enviar… {0} POI/anotações enviados Enviar todos @@ -523,7 +523,7 @@ Já existe um ficheiro de favoritos exportados anteriormente. Quer substitui-lo\? Configurações específicas de Perfil Configurações Globais - Configurações globais da aplicação + Configurações globais da app Espaço livre insuficiente, precisa de %1$s MB (só tem: %2$s disponíveis). Descarregar {0} ficheiro(s)\? \n {1} MB (de {2} MB) será utilizado. @@ -554,7 +554,7 @@ O ficheiro POI \'%1$s\' é redundante e pode ser eliminado. Não foi encontrado (e não pôde ser criado) o ficheiro local para guardar as mudanças de POI. Upgrade para OsmAnd+ - Descarregue a nova versão da aplicação para poder usar os novos ficheiros de mapas. + Descarregue a nova versão da app para poder usar os novos ficheiros de mapas. Mudar o nome Online Nomeação Procurando posição… @@ -624,7 +624,7 @@ Áudio de chamada telefónica (para interromper os aparelhos de som Bluetooth do carro) Áudio de Notificação Áudio de mídia/navegação - A aplicação não conseguiu descarregar a camada do mapa %1$s, se a tornar a instalar pode resolver o problema. + A app não conseguiu descarregar a camada do mapa %1$s, se a tornar a instalar pode resolver o problema. Ajustar a transparência da sobreposição. Transparência da Sobreposição Ajustar a transparência do mapa base. @@ -646,7 +646,7 @@ Não foi possível executar a pesquisa offline. Pesquisa por localização geográfica Sistema - Idioma de exibição da aplicação (usado após OsmAnd ser reiniciado). + Idioma de exibição da app (usado após OsmAnd ser reiniciado). Linguagem Próximo Anterior @@ -671,7 +671,7 @@ \nO serviço de navegação está temporariamente mudado para CloudMade on-line. Não foi possível encontrar a pasta especificada. Local de armazenamento - Todos os dados offline na aplicação instalada antiga serão suportados pela nova aplicação, mas os pontos Favoritos devem ser exportados da aplicação antiga e depois importados na nova aplicação. + Todos os dados offline na app instalada antiga serão suportados pela nova, mas os pontos Favoritos devem ser exportados da app antiga e depois importados na nova. Build {0} foi instalado ({1}). Descarregando construção… Instalar OsmAnd - {0} de {1} {2} MB \? @@ -787,27 +787,27 @@ Widgets transparentes Contínuo e-mail - OsmAnd (direções automatizadas de navegação OSM) -\n -\nO OsmAnd é uma aplicação de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz. -\n -\nAlgumas das características principais: -\n- Funcionalidade totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável). -\n- Mapas vetoriais compactados do mundo inteiro disponíveis. -\n- Descarregar mapas de países ou regiões diretamente na aplicação. -\n- Sobreposição de mapas diversos, como GPX ou trajetos de navegação, pontos de interesse (POI), favoritos, curvas de nível, paragens de transportes públicos, mapas adicionais com transparência personalizável. -\n- Pesquisa desligado da Internet para endereços e locais (POIs). -\n- Encaminhamento desligado da Internet para distâncias médias. -\n- Modo de carro, bicicleta e pedestre. -\n- Vista de dia/noite, com alteração automática (opcional). -\n- Ampliação do mapa dependente da velocidade. -\n- Orientação do mapa de acordo com bússola ou direção do movimento. -\n- Orientação de faixas de rodagem, aviso de limite de velocidade, vozes gravadas e vozes para a conversão de texto para voz. -\n -\nLimitações desta versão gratuita do OsmAnd: -\n- Quantidade de descarregamentos de mapas limitado. -\n- Sem acesso aos POIs da Wikipédia no modo desligado da Internet. -\n + OsmAnd (direções automatizadas de navegação OSM) +\n +\nO OsmAnd é uma app de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz. +\n +\nAlgumas das características principais: +\n- Funcionalidade totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável). +\n- Mapas vetoriais compactados do mundo inteiro disponíveis. +\n- Descarregar mapas de países ou regiões diretamente na app. +\n- Sobreposição de mapas diversos, como GPX ou trajetos de navegação, pontos de interesse (POI), favoritos, curvas de nível, paragens de transportes públicos, mapas adicionais com transparência personalizável. +\n- Pesquisa desligado da Internet para endereços e locais (POIs). +\n- Encaminhamento desligado da Internet para distâncias médias. +\n- Modo de carro, bicicleta e pedestre. +\n- Vista de dia/noite, com alteração automática (opcional). +\n- Ampliação do mapa dependente da velocidade. +\n- Orientação do mapa de acordo com bússola ou direção do movimento. +\n- Orientação de faixas de rodagem, aviso de limite de velocidade, vozes gravadas e vozes para a conversão de texto para voz. +\n +\nLimitações desta versão gratuita do OsmAnd: +\n- Quantidade de descarregamentos de mapas limitado. +\n- Sem acesso aos POIs da Wikipédia no modo desligado da Internet. +\n \nO OsmAnd está em desenvolvimento ativo, mas o nosso projeto e o seu progresso ainda depende de contribuições financeiras para o desenvolvimento e testes de novas funcionalidades. Por favor, considere comprar o OsmAnd+, ou a ajudar a financiar novas funcionalidades específicas, ou fazer um donativo no osmand.net. Selecione um esquema de cores de estrada: Esquema de cores @@ -846,9 +846,9 @@ Só Estradas Mapa padrão Mapas só de estradas - Executar a aplicação no modo de segurança (usando o código do Android mais lento em vez do nativo). + Executar a app no modo de segurança (usando o código do Android mais lento em vez do nativo). Modo seguro - A aplicação está a ser executada no modo de segurança (desligue-a em \"Definições\"). + A app está a ser executada no modo de segurança (desligue-a em \"Definições\"). O serviço de segundo plano OsmAnd ainda está em execução. Tambẽm pará-lo\? Fechar conjunto de alterações Pesquisar mais povoações/códigos postais @@ -893,7 +893,7 @@ Sob demanda\? Formato de saída de vídeo: Usar gravador do sistema para vídeo. - Utilize aplicação do sistema para fotos. + Utilize a app do sistema para fotos. Usar aplicação da câmara A tocar o áudio da gravação. \n%1$s Indisponível @@ -927,14 +927,14 @@ desmarcado Limite de Velocidade Nenhum edifício encontrado. - A aplicação do leitor de código de barras ZXing não está instalada. Procurar no Google Play\? + A app do leitor de código de barras ZXing não está instalada. Procurar no Google Play\? Faça uma doação para ver novas funcionalidades implementadas nesta aplicação. incompleto Nome da rua Número de casa Gravação de viagem - Personalizar a aparência da aplicação. - Tema da aplicação + Personalizar a aparência da app. + Tema da app Opções de acessibilidade Especifique um endereço Selecione favorito @@ -1047,14 +1047,14 @@ Mapa mundial OsmAnd+ (Direções de Navegação Automatizada do OSM) \n -\n OsmAnd+ é uma aplicação de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz. +\n OsmAnd+ é uma app de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz. \n -\n OsmAnd+ é a versão paga da aplicação, ao comprá-lo está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações. +\n OsmAnd+ é a versão paga da app, ao comprá-lo está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações. \n \n Algumas das características principais: \n - Funcionalidade totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável). \n - Mapas vetoriais compactados do mundo inteiro disponíveis. -\n - Descarregamento de mapas de países ou regiões diretamente na aplicação. +\n - Descarregamento de mapas de países ou regiões diretamente na app. \n - Recurso Wikipédia desligado da Internet (descarregamento de POIs da Wikipédia), ótimo para passeios turísticos. \n - Possibilidade de sobreposição de várias camadas de mapas, como trilhos GPX ou navegação, pontos de Interesse, favoritos, curvas de nível, paragens de transporte público, mapas adicionais com transparência personalizável. \n @@ -1086,7 +1086,7 @@ Wikipédia (off-line) Marca Marítima Escolha perfis visíveis. - Perfis da aplicação + Perfis da app Destino Rosa Castanho @@ -1341,7 +1341,7 @@ Ver Norte Leste - Memória interna da aplicação + Memória interna da app Ir Configurações de navegação Configurações gerais @@ -1521,8 +1521,8 @@ O mapa %1$s está pronto para ser usado. Mapa descarregado Mostrar mapa - Define o sinalizador que indica a primeira inicialização da aplicação, mantém todas as outras configurações inalteradas. - Simular arranque inicial da aplicação + Define o sinalizador que indica a primeira inicialização da app, mantém todas as outras configurações inalteradas. + Simular arranque inicial da app geo: Partilhar Localização Enviar @@ -1572,7 +1572,7 @@ Escolher orientação por voz Escolher ou descarregar a orientação por voz para o seu idioma. Noite - Há uma nova opção para controlar principalmente a aplicação através do painel de controlo flexível ou um menu estático. A sua escolha pode ser alterada nas configurações do painel. + Há uma nova opção para controlar principalmente a app através do painel de controlo flexível ou um menu estático. A sua escolha pode ser alterada nas configurações do painel. Painel de controlo ou menu de controlo Atualizar Apenas descarregar com Wi-Fi @@ -1661,7 +1661,7 @@ Fino Média Negrito - Agora a aplicação está autorizada a escrever no armazenamento externo, mas primeiro é necessário reiniciar a aplicação. + Agora a app está autorizada a escrever no armazenamento externo, mas primeiro é necessário reiniciar a app. Mover ↑ Mover ↓ Terminar a navegação @@ -1959,7 +1959,7 @@ Sem sobreposição Sem subposição Erro - Assine a nossa lista de e-mail sobre descontos da aplicação e ganhe mais 3 descarregamentos de mapas! + Assine a nossa lista de e-mail sobre descontos da app e ganhe mais 3 descarregamentos de mapas! Curvas de nível de profundidade marítima e seamarks. Muito obrigado por comprar \'Contornos de profundidade náutica\' Contornos de profundidade náutica @@ -1973,10 +1973,10 @@ Fontes do mapa Circulação pela direita Automático - Não envie estatísticas anónimas de utilização da aplicação - OsmAnd recolhe informação sobre as secções da aplicação que abriu. Não são enviadas: a sua localização; a informação que introduz na aplicação; detalhes de áreas que veja, procure ou descarregue. + Não envie estatísticas anónimas de utilização da app + OsmAnd recolhe informação sobre as secções da app que abriu. Não são enviadas: a sua localização; a informação que introduz na app; detalhes de áreas que veja, procure ou descarregue. Não mostrar mensagens ao iniciar - Nâo mostrar descontos da aplicação e mensagens de eventos locais especiais. + Nâo mostrar descontos da app e mensagens de eventos locais especiais. Opções de estacionamento Muito obrigado por comprar a versão paga de OsmAnd. Inclinado @@ -2208,7 +2208,7 @@ Como abrir a hiperligação\? Ler a Wikipédia desligado da Internet Descarregar tudo - Reiniciar a aplicação + Reiniciar a app Mostrar imagens Cancelou a sua assinatura do OsmAnd Live Renovar assinatura para continuar a utilizar todas as funcionalidades: @@ -2232,7 +2232,7 @@ Guias para os lugares mais interessantes do mundo dentro do OsmAnd, sem uma conexão com a Internet. Atualizações de mapa mensais Atualizações de mapa a cada hora - Compra na aplicação + Compra na app Pagamento de uma só vez Uma vez comprado, estará sempre disponível para si. Comprar - %1$s @@ -2475,7 +2475,7 @@ \nRepresenta área: %1$s x %2$s Tolerância do limite de velocidade Selecione a margem de tolerância de limite de velocidade, acima do qual receberá um aviso de voz. - O nome do Favorito foi modificado para %1$s para facilitar gravar corretamente a sequência de caracteres com emoticons para um ficheiro. + O nome do favorito foi modificado para %1$s para facilitar gravar corretamente a cadeia de caracteres com emoticons num ficheiro. Imprimir rota Nome de favorito duplicado Nome favorito especificado já está em uso, foi alterado para %1$s para evitar a duplicação. @@ -2551,8 +2551,8 @@ Renderizar caminhos de acordo com a escala de SAC. Renderizar caminhos de acordo com traços OSMC. Hora intermediária - OsmAnd (sigla em inglês de direções de navegação automatizada do OSM) é uma aplicação de mapas e navegação com acesso a dados livres, mundiais e de alta qualidade do OSM. -\n + OsmAnd (sigla em inglês de direções de navegação automatizada do OSM) é uma app de mapas e navegação com acesso a dados livres, mundiais e de alta qualidade do OSM. +\n \nPoderá usar o navegador visual e por voz, ver POIs (pontos de interesse), criar e gerir trilhos GPX, usar (através de um suplemento) curvas de nível e dados de altitude, escolher entre os modos motorista, ciclista e pedestre, editar o OpenStreetMap e muito mais. Navegação GPS \n• Escolha entre modos off-line (sem tarifa de roaming quando estiver no exterior) ou on-line (mais rápido) @@ -2595,7 +2595,7 @@ \n • Envie trilhos GPX para o OpenStretMap diretamente da aplicação \n • Adicione POIs e envie-os diretamente para o OpenStretMap (ou mais tarde se estiver desconectado da Internet) \n - OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, melhorando as traduções ou programando novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. + OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a app reportando erros, melhorando as traduções ou programando novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. \n Cobertura de mapa e qualidade aproximada: \n • Europa Ocidental: **** \n • Europa Oriental: *** @@ -2609,11 +2609,11 @@ \n • Antártida: * \n A maioria dos países ao redor do globo está disponível para descarregar! \n Obtenha um navegador confiável no seu país - seja em França, Alemanha, México, Reino Unido, Espanha, Holanda, EUA, Rússia, Brasil ou qualquer outro. - OsmAnd+ (direções de navegação automatizada do OSM) é uma aplicação de mapas e navegação com acesso a dados livres do OSM, de todo o mundo e de alta qualidade. -\nDesfrute da navegação visual ou por voz, vendo POIs (pontos de interesse), criando e gerindo trilhos GPX, usando informação de altitude e curvas de nível, escolher entre modos dirigir, andar de bicicleta e pedestre, editar o OpenStreetMap e muito mais. -\n -\nOsmAnd+ é a versão paga da aplicação. Ao comprá-lo, está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações. -\n + OsmAnd+ (direções de navegação automatizada do OSM) é uma app de mapas e navegação com acesso a dados livres do OSM, de todo o mundo e de alta qualidade. +\nDesfrute da navegação visual ou por voz, ver POIs (pontos de interesse), criando e gerindo trilhos GPX, usando informação de altitude e curvas de nível, escolher entre modos dirigir, andar de bicicleta e pedestre, editar o OpenStreetMap e muito mais. +\n +\nOsmAnd+ é a versão paga da app. Ao comprá-lo, está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações. +\n \nAlgumas das características principais: Navegação \n• Funciona on-line (rápido) ou off-line (sem custos de roaming quando estiver no estrangeiro) @@ -2654,10 +2654,10 @@ \n• Visualização de curvas de nível e sombreamento de relevo (via suplemento adicional) Contribua diretamente para o OpenStreetMap \n • Envie relatórios de erros. -\n • Envie trilhos GPX para o OpenStretMap diretamente da aplicação. +\n • Envie trilhos GPX para o OpenStretMap diretamente da app. \n • Adicione POIs e envie-os diretamente para o OpenStretMap (ou mais tarde se estiver desconectado da Internet). \n • Gravação de viagem opcional também em plano de fundo (enquanto o aparelho está no modo adormecido). -\n OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, melhorando as traduções ou programando novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. +\n OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a app por reportar erros, a melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. \n Cobertura de mapa e qualidade aproximada: \n• Europa Ocidental: **** @@ -3358,7 +3358,7 @@ Tocar em \'Aplicar\' apagará os perfis removidos permanentemente. Perfil principal Selecione a cor - Perfis padrão do OsmAnd não podem ser apagados, mas desativados (na tela anterior) ou classificados na parte inferior. + Perfis predefinidos do OsmAnd não podem ser apagados, mas desativados (no ecrã anterior) ou classificados na parte inferior. Editar perfis O \'Tipo de navegação\' controla como as rotas são calculadas. Aspeto do perfil @@ -3887,7 +3887,7 @@ Apenas a linha da rota será gravada, os pontos de passagem serão apagados. Nome do ficheiro %s ficheiros de faixa selecionados - Vai pausar o registo de faixas quando a aplicação for morta (através de aplicações recentes). (indicação de fundo de OsmAnd desaparece da barra de notificação do Android.) + Vai pausar o registo de faixas quando a app for morta (através de apps recentes). (indicação de fundo de OsmAnd desaparece da barra de notificação do Android.) - Função atualizada de Planear uma rota: permite utilizar diferentes tipos de navegação por segmento e a inclusão de faixas \n \n - Novo menu Aparência para trilhos: selecionar cor, espessura, setas de direção de visualização, ícones de início/fim @@ -3903,4 +3903,17 @@ \n - Problemas com as configurações de importação/exportação de perfis resolvidos \n \n + Última modificação + Nome: Z – A + Nome: A – Z + Ícones de início/fim + Obrigado por comprar \'Curvas de nível\' + Assinatura cobrada por período selecionado. Cancele-a na AppGallery a qualquer momento. + O pagamento será debitado na sua conta AppGallery no momento da confirmação da compra. +\n +\nA assinatura é renovada automaticamente, a menos que seja cancelada antes da data de renovação. A sua conta será cobrada pelo período de renovação (mês/três meses/ano) apenas na data de renovação. +\n +\nPode gerir e cancelar as suas subscrições a ir às definições da sua AppGallery. + Evitar passeios + Evitar passeios \ No newline at end of file diff --git a/OsmAnd/res/values-ru/phrases.xml b/OsmAnd/res/values-ru/phrases.xml index f67e31fb94..d87b1f7c05 100644 --- a/OsmAnd/res/values-ru/phrases.xml +++ b/OsmAnd/res/values-ru/phrases.xml @@ -3832,4 +3832,5 @@ Малые электроприборы Магазин орехов Улей + СПГ \ No newline at end of file diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index 701641a64d..968f4e898e 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -52,7 +52,7 @@ Плагин Приобретите и установите плагин «Контурные линии» для отображения градуированных вертикальных областей. Цветовая схема - Показывать, начиная с уровня масштабирования + Показывать начиная с масштаба Анимация моего положения Включить анимацию прокрутки карты с моим положением во время навигации. Масштаб: %1$s @@ -60,7 +60,7 @@ Выбрать цвет Задать имя Для больших расстояний: добавьте промежуточные пункты, если маршрут не построен в течение 10 минут. - Разрешить частный доступ + Разрешить частные зоны Разрешить доступ на частную территорию. Обзор Выберите улицу @@ -71,9 +71,9 @@ Ближайшие города Выберите город Поиск почтового индекса - Аудио⁣заметка + Запись аудио⁣ Записать видео - Фотозаметка + Сделать фото OSM-заметка Функции парковки Благодарим вас за покупку платной версии OsmAnd. @@ -86,7 +86,7 @@ Видимые Восстановить покупки Шрифты карты - Посмотреть на карте + Анализ на карте Морские карты Контуры морских глубин Контуры морских глубин @@ -105,7 +105,7 @@ Добавление нового пункта назначения в центре экрана. Ранее выбранный пункт назначения станет последним промежуточным пунктом. Кнопка для установки центра экрана пунктом отправления. Затем нужно будет выбрать пункт назначения или запустить расчёт маршрута. Кнопка для установки центра экрана пунктом назначения с заменой предыдущего (если был задан). - Установка центра экрана первой промежуточной точкой маршрута. + Установка центра экрана местом первой остановки на маршруте. Нет покрытия Нет подложки Гористый @@ -115,7 +115,7 @@ Сбалансированный Предпочитать переулки Выберите предпочтительный рельеф. - Склон + Уклон Добавить новую папку Точки удалены. Вы уверены, что хотите удалить %1$d точки\? @@ -171,7 +171,7 @@ Добавить источник карты Источник карты изменён на «%s». Удерживайте кнопку для перемещения её по экрану. - Показывать контуры и точки глубины. + Показывать контуры и точки глубин. Контуры морских глубин Частота горизонталей Частота горизонталей @@ -202,7 +202,7 @@ Продолжить Пауза Поездка - Записано + Записано в трек Запись Нет данных Минимальная скорость для записи @@ -233,7 +233,7 @@ Сохранить фильтр Удалить фильтр Новый фильтр - Изменить позицию + Изменение позиции Текущий путь Навигация OsmAnd Live Уровень заряда батареи @@ -317,18 +317,18 @@ Открыть внешний проигрыватель Удалить эту запись? недоступно - Аудиозаметка - Видеозаметка - Слой аудиозаписей - Запись не может быть воспроизведена. + Запись аудио + Запись видео + Слой медиазаписей + Не удаётся воспроизвести запись. Удалить запись Проиграть Запись %1$s %3$s %2$s Запись - Аудиозаметки + Медиазаметки OsmAnd-плагин для линий высот Измерение расстояний - Нажмите «Использовать местоположение…» чтобы добавить заметку к данному местоположению. + Нажмите «Использовать местоположение…» для добавления заметки к месту. Аудиозаметки Создавайте аудио-, видео- и фотозаметки в поездке, используя виджет или контекстное меню. Аудио/видеозаметки @@ -368,9 +368,9 @@ Использовать онлайн-карты (загрузка и кеширование на SD-карте). Онлайн-карты Выберите источник онлайн или кешированных растровых карт. - Доступ ко множеству онлайн-карт (т. н. тайловых или растровых): от встроенных OSM (как Mapnik), до спутниковых снимков и слоёв специального назначения, таких как карты погоды, климатические, геологические карты, затенения рельефа и др. + Доступ ко множеству онлайн-карт (т. н. тайловых или растровых): от встроенных OSM (как Mapnik) до спутниковых снимков и слоёв специального назначения, таких как карты погоды, климатические, геологические карты, затенения рельефа и др. \n -\n Любая из этих карт может быть использована в качестве базовой либо как наложение или подложка к другой базовой карте (например стандартной локальной карте OsmAnd). Некоторые элементы векторной карты OsmAnd можно скрыть в меню «Настройки карты». +\n Любая из этих карт может быть использована как основная или в качестве подложки к другой карте (например стандартной локальной карте OsmAnd). Некоторые элементы векторной карты OsmAnd можно скрыть в меню «Настройки карты». \n \n Карты можно загрузить непосредственно из интернета или подготовить для использования в автономном режиме (и вручную скопировать в папку данных OsmAnd) в виде базы данных sqlite, которая может быть создана с помощью различных инструментов подготовки карт сторонних производителей. Показывает настройки для включения фонового отслеживания и навигации путём периодического пробуждения устройства GPS (с выключенным экраном). @@ -505,7 +505,7 @@ Голосовые подсказки (TTS) Голосовые подсказки (записанные) Данные POI - Голос TTS + TTS Новый поиск Размер текста для названий на карте: Размер текста @@ -518,23 +518,23 @@ Обратное направление трека Использовать текущий пункт назначения Пройти весь путь - Для этого региона доступны локальные векторные карты. -\n\t -\n\tДля их использования выберите в \"Меню\" → \"Настройка карты\" → \"Источник карты…\" → \"Векторные карты\". - Голосовые инструкции + Для этого региона есть локальные векторные карты. +\n\t +\n\tДля использования выберите их в качестве источника (Меню → Настройка карты → Источник карты → Локальные векторные карты). + Аудиоканал голосовых инструкций Выберите канал вывода голосовых подсказок. - Канал голосовых звонков (прерывает автомобильную Bluetooth стереосистему) - Канал уведомлений - Канал медиа/навигации + Голосовые звонки (для прерывания автомобильной стереосистемы Bluetooth) + Уведомления + Мультимедиа, навигация Приложение не может загрузить слой карты %1$s, переустановка может решить проблему. Отрегулируйте прозрачность наложения. Прозрачность наложения Отрегулируйте прозрачность основной карты. Прозрачность основной карты - Карта подложки + Карта подложки… Карта подложки Выберите карту подложки - Карта наложения + Карта наложения… Карта наложения Выберите слой наложения поверх основной карты Карта уже установлена, настройки будут обновлены. @@ -605,8 +605,8 @@ Чтение кешированных растровых карт… Недостаточно памяти для локальной карты «{0}» Версия локальной карты «{0}» не поддерживается - Локальная навигация OsmAnd является экспериментальной функцией и не работает на длинные расстояния более 20 километров. -\n + Офлайн-навигация OsmAnd — это экспериментальная функция, и она не работает на дистанциях больше 20 км. +\n \nНавигация временно переключена на онлайн-сервис CloudMade. Невозможно найти указанную папку. Папка хранилища данных @@ -616,7 +616,7 @@ Установить OsmAnd — {0} из {1} {2} МБ\? Не удалось получить список сборок OsmAnd Загружаются сборки OsmAnd… - Выберите сборку OsmAnd для установки + Выберите сборку для установки Голосовая навигация недоступна. Перейдите в «Настройки» → «Настройки навигации», выберите профиль → «Голосовые данные» и выберите или загрузите пакет голосовых подсказок. Выберите пакет голосовых подсказок Показывать производительность отрисовки. @@ -638,7 +638,7 @@ Самый быстрый маршрут Расчёт скоростного маршрута вместо кратчайшего. На масштабе {0} загрузить {1} тайлов ({2} МБ) - Загрузить карту + Скачать карту Наибольший масштаб для предварительной загрузки Выбранная карта не может быть загружена Непрерывная отрисовка @@ -669,9 +669,9 @@ Локальные векторные карты Редактировать POI Удалить POI - По направлению компаса - По направлению движения - Не вращать (север сверху) + по направлению компаса + по направлению движения + не вращать (север сверху) Выравнивание карты: Ориентация карты Детали маршрута @@ -689,7 +689,7 @@ Маршрут OSM-заметки (онлайн) POI… - Источник карты + Источник карты… Слои Поиск POI Использовать трекбол для перемещения по карте. @@ -715,10 +715,10 @@ Фильтр Звук Без звука - Выберите язык голосовых инструкции для навигации. + Выберите голосовое сопровождение для навигации. Голосовые данные Инициализируются голосовые данные… - Голосовые данные не поддерживаются текущей версией приложения + Неподдерживаемая версия голосовых данных Выбранные голосовые данные не правильного формата Выбранный пакет голосовых подсказок не доступен SD-карта недоступна. @@ -726,13 +726,13 @@ Карта памяти доступна только для чтения. \nТеперь можно только просматривать предварительно загруженную карту, а не загружать новые области. Файл распаковывается… - Направо и прямо - Резко направо и прямо - Плавно направо и прямо - Налево и прямо - Резко налево и прямо - Плавно налево и прямо - Выполните разворот, затем прямо + Направо + Резко направо + Плавно направо + Налево + Резко налево + Плавно налево + Выполните разворот Двигайтесь прямо Продолжить Загрузить детальные карты регионов @@ -844,7 +844,7 @@ Сохранить текущий трек Укажите интервал фиксирования точек для записи трека во время навигации Интервал записи во время навигации - Во время навигации GPX треки будут автоматически сохранены в папку с треками. + Во время навигации GPX-треки будут автоматически сохранены в папку с треками. Автозапись трека во время навигации Обновить карту Обновить часть карты @@ -864,7 +864,7 @@ 3D вид Показать последние использованные POI на карте. Показывать POI - Выберите источник онлайн или кешированных тайлов карты + Выберите источник онлайн- или кешированных тайлов карт. Растровые карты Источник карты Использовать интернет @@ -901,7 +901,7 @@ Дом Пересечение улиц Обновить карту - Создать POI + Добавление POI Да Отмена Нет @@ -1160,10 +1160,10 @@ \n — подсказки полосы движения, отображение ограничения скорости, предварительно записанные и синтезированные голосовые подсказки \n Без автомагистралей - Привязываться к дорогам во время навигации. + Привязывать позицию к дороге во время навигации. Привязка к дороге Промежуточный пункт %1$s слишком далеко от ближайшей дороги. - Достигнут промежуточный пункт + Вы прибыли в промежуточный пункт Промежуточный пункт Промежуточный пункт Конец маршрута слишком далеко от ближайшей дороги. @@ -1177,11 +1177,11 @@ Рестораны Достопримечательности Последний промежуточный пункт - Первый промежуточный пункт - Добавить последним промежуточным пунктом - Добавить первым промежуточным пунктом + Сделать начальной остановкой + Сделать последней остановкой + Первый промежуточный пункт Заменить пункт назначения - Пункт назначения уже задан: + Пункт назначения уже задан Пункт %1$s Точки маршрута Промежуточный пункт %1$s @@ -1201,12 +1201,12 @@ Настройки аудио и видео Изменить порядок Просмотр - Сделать фото + Снимок Сделать фото Синхронизация треков и медиазаметок с вашим аккаунтом Dropbox. Плагин Dropbox - Плагин обеспечивает наложение контурных линии и (рельефа) затемняющего слоя, которые будут отображаться поверх стандартных карт OsmAnd. Эта функция высоко оценится спортсменами, туристами, путешественниками и всеми, кто заинтересован в рельефной структуре ландшафта. -\n + Плагин обеспечивает наложение контурных линии и затемняющего слоя (рельефа), которые будут отображаться поверх стандартных карт OsmAnd. Эту функцию оценят спортсмены, туристы, путешественники и все, для кого рельеф местности имеет значение. +\n \nГлобальные данные (между 70° на севере и 70° на юге) основываются на измерениях SRTM (Shuttle Radar Topography Mission) и ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), инструментом визуализации Terra, флагманского спутника Земли системы наблюдения NASA. ASTER является результатом совместных усилий NASA, министерства экономики Японии, торговли и промышленности (METI), космических систем Японии (J-spacesystems). Фото %1$s %2$s Медиаданные @@ -1227,7 +1227,7 @@ Время прибытия Информация GPS нет - Слой рельефа местности + Слой затенения рельефа Название улицы Номер дома Нет соединения по Wi-Fi. Использовать текущее интернет-соединение для загрузки\? @@ -1239,7 +1239,7 @@ Укажите адрес Выбор избранной Модификации OSM - Выбирать + Выбрать OsmAnd карты и навигация OsmAnd+ карты и навигация Уменьшает «шум» компаса, но добавляет инерцию. @@ -1258,7 +1258,7 @@ Отменить маршрут Очистить пункт назначения Искать улицу в ближайших населённых пунктах - В порядке следования домов + Расположить в оптимальном порядке Доступно %1$d файлов для скачивания осталось %1$d файлов Подождите, пока завершится текущая операция @@ -1272,10 +1272,10 @@ Резервное копирование как правка OSM высота OsmChange-файл создан за %1$s - * Нажмите, чтобы отметить точку. -\n* Удерживайте нажатие на карте, чтобы удалить предыдущую точку. -\n* Удерживайте нажатие на точке, чтобы просмотреть и добавить описание. -\n* Нажмите на виджет измерения, чтобы увидеть больше действий. + * Нажмите, чтобы отметить точку. +\n* Нажмите и удерживайте карту, чтобы удалить предыдущую точку. +\n* Удерживайте точку для просмотра и добавления описания. +\n* Нажмите на виджет измерения для других действий. Использовать магнитный датчик вместо датчика ориентации. Другие Контурные линии @@ -1301,9 +1301,9 @@ Дорожные предупреждения Очистить промежуточные пункты Оставить промежуточные пункты - К: + Назначение: Через: - От: + Отправление: Промежуточные пункты уже заданы. Названия улиц (TTS) Объявлять… @@ -1345,9 +1345,9 @@ Симуляция использования рассчитанного маршрута Симуляция использования трека GPX Без автомасштаба - Ближний план - Средний план - Дальний план + К ближнему плану + К среднему плану + К дальнему плану Избегать автомагистралей Без автомагистралей Предпочитать автомагистрали @@ -1358,7 +1358,7 @@ Избегать грунтовых дорог Без паромов Исключить паромные переправы - Максимальная масса + Предельная масса Укажите допустимый предел массы автомобиля для учёта при построении маршрута. Отображение карты Навигационные знаки (водоёмы) @@ -1386,7 +1386,7 @@ Задать пункт назначения Предпочтения маршрута Информация про маршрут - Добавить как новый пункт назначения + Добавить пункт назначения Использовать показанный путь для навигации? Рассчитать сегмент маршрута OsmAnd без интернета Рассчитать маршрут OsmAnd для первого и последнего сегмента маршрута @@ -1445,15 +1445,15 @@ Голос Разное Локализация - Голосовые подсказки приостанавливают воспроизведение музыки. - Приостановить музыку + Приостановка воспроизведения во время подсказок. + Прерывать музыку Поделиться маршрутом используя файл GPX Неправильный формат: %s Маршрут предоставленный через OsmAnd - Только вручную (нажатием «стрелочки») + При нажатии на стрелку (вручную) Повторять навигационные инструкции с регулярными интервалами. Повторять навигационные инструкции - Объявление прибытия + Объявление о прибытии Как скоро следует сообщать о прибытии? Места, отправленные в OsmAnd Рассчитать маршрут между точками @@ -1492,7 +1492,7 @@ %1$s точек Точка %1$s %1$s \nМаршрутных точек %2$s - Показывать кнопки изменения масштаба во время навигации. + Показывать кнопки масштаба во время навигации. Кнопки масштаба Сортировать по расстоянию Сортировать по имени @@ -1536,14 +1536,14 @@ Сербский (кириллица) Китайский (упрощённый) Китайский (традиционный) - Маршруты метро + Линии метро Продолжить навигацию Приостановить навигацию - Визуализация пути по шкале SAC. - Визуализация пути согласно трассам OSMC. - Пораньше - Как обычно - Попозже + Отрисовка дорог cогласно шкале SAC. + Отрисовка дорог согласно трассам OSMC. + Раннее + По умолчанию + Позднее На последних метрах Пеший горный туризм по шкале (SAC) Наложение туристических меток @@ -1581,7 +1581,7 @@ Снизьте скорость Камера скорости Дорожные предупреждения - Выберите допустимое значение превышения скорости выше которого вы получите голосовое предупреждение. + Выберите значение скорости, при превышении которого вы получите голосовое предупреждение. Допустимое превышение скорости Избранная точка переименована на «%1$s», чтобы сохранить строку, содержащую эмотикон в файл. Печать маршрута @@ -1739,7 +1739,7 @@ Отменить выбор всех Поделиться Мои места - Точки + Избранные Треки Текущий трек Поделиться заметкой @@ -1780,7 +1780,7 @@ \n \nВ случае активации этого вида, стиль карты меняется на «Зимний/лыжный», показывая все детали пейзажа так, как они выглядят зимой. Такой (зимний) вид может быть отменён либо путём деактивации здесь, либо если вы поменяете «Стиль карты» в меню «Настройки карты» на желаемый вид. Текущий маршрут - Скачать карты + Загрузка карт Для правильного отображения дорожных знаков и правил выберите свой регион вождения: Добро пожаловать Отметить для удаления @@ -1815,8 +1815,8 @@ Переместить файлы данных Osmand в новое место назначения\? Напечатайте для поиска Номера домов - Избегать перехода границы - Максимальная высота + Избегать пересечения границ + Предельная высота Укажите высоту транспортного средства для учёта при построении маршрута. Умный пересчёт маршрута Для больших маршрутов пересчитывать только начало. @@ -1905,8 +1905,8 @@ Пропустить OsmAnd Плагины - Локальные карты -\nи Навигация + Офлайн-карты +\nи навигация Номер дома Тип лыжной трассы Автообновления @@ -2030,13 +2030,13 @@ Изменить путевую точку GPX Без лестниц Избегать ступеней и лестниц - Без пересечений границ + Без перехода границы Обновлять Загружать только по Wi-Fi Обновить сейчас Приложение не имеет разрешения на использование SD-карты Доступные карты - Начальный пункт + Пункт отправления Не выбрано Размер хранилища Звук @@ -2065,7 +2065,7 @@ Разбиение на клипы Использовать разбиение на клипы Циклическая перезапись клипов при превышении заданного объёма хранилища. - Поменять местами пункты отправления и назначения + Поменять местами пункты отправления и назначения Удалить Подземные объекты Данные недоступны @@ -2110,7 +2110,7 @@ Запись удалена элементы удалены Автообновления - Выберите или скачайте голосовые подсказки для вашего языка. + Выберите или скачайте голосовое сопровождение для вашего языка. Полный отчёт Пересчёт маршрута Имя пользователя и пароль OSM @@ -2235,7 +2235,7 @@ Получить Получайте неограниченное количество загрузок карт, вдобавок к еженедельным, ежедневным и даже почасовым обновлениям. Неограниченный доступ к картам, обновлениям и плагину «Википедия». - Голосовое сопровождение + Голосовые инструкции Абонентская плата взимается за выбранный период. Отменить подписку можно в Google Play в любой момент. Пожертвование для сообщества OSM Часть вашего пожертвования будет отправлена участникам OSM. Стоимость подписки при этом остаётся прежней. @@ -2281,16 +2281,16 @@ Изменить положение кнопки Название действия Сербский (латиница) - Голос вкл/выкл - Включить голос - Выключить голос + Голосовая навигация + Включить подсказки + Выключить подсказки Не удалось переместить файл. Благодарим вас за покупку контуров морских глубин Добавить фото Разрешения Онлайн-фото - Здесь нет фотографий. - Поделитесь вашим просмотром улиц через Mapillary. + Здесь нет фото. + Поделитесь своими уличными видами через Mapillary. Виджет Mapillary Позволяет быстро внести свой вклад в Mapillary. Фото с улиц онлайн для каждого. Открывайте места, взаимодействуйте, запечатлейте весь мир. @@ -2337,7 +2337,7 @@ Измерить расстояние Добавьте хотя бы одну точку. Фотография Mapillary - Улучшить фотопокрытие через Mapillary + Улучшить фотопокрытие в Mapillary Скрыть, начиная с уровня масштабирования Прозрачно-розовый Берберский @@ -2380,7 +2380,7 @@ Избегать ледовых дорог и бродов. Моё местоположение Финиш - Сортировать + Сортировка Экспорт маркеров в следующий файл GPX: Маркеры Изменить заметку @@ -2392,7 +2392,7 @@ Критерий сортировки: Выберите способ указания расстояния и направления до маркеров на карте: Смена ориентации карты - Выберите скорость, при которой переключается ориентация карты с «По направлению движения» на «По направлению компаса». + Выберите скорость, при которой ориентация по направлению движения переключится на ориентацию по компасу. Все маркеры перемещены в историю Маркер перемещён в историю Маркер перемещён в действующие @@ -2440,7 +2440,7 @@ Кнопка для добавления фотозаметки в центре экрана. Добавление в центре экрана OSM-заметки. Добавление POI в центре экрана. - Переключатель, чтобы включить или выключить голосовые подсказки во время навигации. + Включение/отключение голосового сопровождения при навигации. Добавление в центре экрана места парковки. Показывать промежуточный диалог Фотографии Mapillary доступны только онлайн. @@ -2470,7 +2470,7 @@ Полноэкранный режим Отметить пройденным Файл %1$s не содержит путевых точек, импортировать его как трек? - Выберите трек, чтобы добавить в маркеры его точки. + Выберите трек, чтобы добавить в маркеры его точки. Трек путевых точек Направо Налево @@ -2520,14 +2520,14 @@ \n• Поддержка промежуточных точек маршрута \n• Запись собственного или отправка GPX трека и следование ему \n - Карта -\n• Отображает POI (точки интереса) около вас -\n• Адаптирует карту в направлении вашего движения (или компаса) -\n• Показывает, где вы находитесь и куда вы смотрите -\n• Делитесь своим расположением, чтобы друзья смогли найти вас -\n• Сохраняет ваши самые важные места в избранных -\n• Позволяет вам выбрать как отображать названия на карте: на английском, местным или с фонетическим написанием -\n• Отображает специальные онлайн-тайлы, спутниковые снимки (с Bing), различные метки, как туристические/навигационные треки GPX и дополнительные слои с настраиваемой прозрачностью + Карта +\n• Отображает POI (точки интереса) вокруг вас +\n• Поворачивает карту по направлению движения (или компаса) +\n• Показывает вашу позицию и направление взгляда +\n• Делитесь вашим местоположением, чтобы вас могли найти друзья +\n• Сохраняет важные для вас места в избранных +\n• Позволяет выбрать способ отображения названий на карте: на английском, местное или фонетическое написание. +\n• Отображает специальные онлайн-тайлы, спутниковые снимки (Bing), различные метки, как туристические/навигационные треки GPX и дополнительные слои с настраиваемой прозрачностью \n Катание на лыжах \n• OsmAnd-плагин лыжные карты позволяет видеть лыжные трассы с уровнем сложности и некоторой дополнительной информацией, как расположение подъёмников и других объектов. @@ -2668,9 +2668,9 @@ Копировать местоположение/название POI Место без названия Текущий - Добавляет промежуточную остановку - Добавляет начальную остановку - Перемещает пункт назначения и создаёт промежуточную точку + Добавить последним промежуточным пунктом + Добавить первым промежуточным пунктом + Ранее выбранный пункт назначения станет последним промежуточным пунктом Показать закрытые заметки Показать/скрыть заметки OSM на карте. GPX — подходит для экспорта в JOSM и другие OSM редакторы. @@ -2683,7 +2683,7 @@ OSM-заметки Впереди туннель Туннели - Сделать отправной точкой + Сделать пунктом отправления Введите имя файла. Ошибка импорта карты Карта импортирована @@ -2764,7 +2764,7 @@ Начать редактирование Получить неограниченный доступ Добро пожаловать на открытое бета-тестирование - Карты горизонталей и карты с отмывкой рельефа + Карты горизонталей и затенение рельефа Скачать статьи Википедии для %1$s, чтобы читать их в автономном режиме. Загрузка данных Википедии Открыть статью в интернете @@ -2788,7 +2788,7 @@ Для катания на лыжах. Выделяет горнолыжные трассы, подъёмники, трассы для беговых лыж и прочее. Меньше отвлекающих второстепенных объектов на карте. Скачать все Простой стиль для вождения. Мягкий ночной режим, контурные линии, контрастные дороги в оранжевом стиле, тусклые второстепенные объекты карты. - Для пеших походов, трекинга и велосипедных прогулок на природе. Читабельный на открытом воздухе и при сложном освещении. Контрастные дороги и природные объекты, различные типы маршрутов, контурные линии с расширенными настройками, дополнительные детали. Функция «Качество дорожного покрытия» позволяет различать дороги с различным качеством поверхности. Нет ночного режима. + Для пеших походов, трекинга и велопоездок на природе. Хорошо читается при сложном освещении. Контрастные дороги и природные объекты, различные типы маршрутов, контурные линии с расширенными настройками, дополнительные детали. Параметр «Дорожное покрытие» позволяет различать поверхность и качество дорог. Ночной режим отсутствует. Старый стиль по умолчанию «Mapnik». Похожие цвета на «Mapnik». Стиль общего назначения. Густонаселённые города показаны упрощённо. Выделяет контурные линии, маршруты, качество поверхности, ограничения доступа, дорожные щиты, визуализация пешеходных маршрутов по шкале SAC, объекты спортивных сплавов. Открыть ссылку Википедии в онлайн @@ -2796,13 +2796,13 @@ Получите подписку на OsmAnd Live, чтобы читать статьи в Википедии и Викигиде в автономном режиме. Как открыть ссылку? Читать Википедию в автономном режиме - Туристический стиль с высоким контрастом и максимальной детализацией. Включает все функции стиля OsmAnd по умолчанию, также отображая как можно больше деталей, в частности дороги, тропы и другие пути для передвижения. Чёткое различие между типами дорог, как во многих туристических атласах. Подходит для дневного, ночного и уличного использования. + Туристический стиль с высоким контрастом и максимальной детализацией. Включает все функции стиля OsmAnd по умолчанию, отображая максимальное количество деталей, в частности дороги, тропы и другие пути передвижения. Чёткое различие между типами дорог (как в туристических атласах). Подходит для использования днём, ночью и при ярком освещении. Сохранить Для езды по бездорожью, основано на топографическом стиле (англ. «Topo»), можно использовать с зелёными спутниковыми снимками в качестве подложки. Уменьшенная толщина основных дорог, увеличенная толщина путей, дорожек, велосипедных и других маршрутов. Модификация стиля по умолчанию для увеличения контраста пешеходных и велосипедных дорог. Использует старые цвета Mapnik. Получите OsmAnd Live, чтобы разблокировать все функции: ежедневные обновления карт с неограниченной загрузкой, все платные и бесплатные плагины, Википедия, Викигид и многое другое. Промежуточное время прибытия - Промежуточное время + Прибытие в промежуточный пункт Редактировать действие Пожалуйста, пришлите скриншот этого уведомления на support@osmand.net Редактировать точку @@ -2855,7 +2855,7 @@ Для продолжения дайте OsmAnd разрешение на определение местоположения. Чёрный Поиск улицы - Сначала выберите город/населённый пункт/местность + Укажите город/место/район Восстановить Маркеры, добавленные как группа избранных или путевых точек GPX и отмеченные как пройденные, останутся на карте. Если группа не активна, маркеры исчезнут с карты. Оставить пройденные маркеры на карте @@ -2891,7 +2891,7 @@ Точки интереса (POI) Расчёт маршрута… Общественный транспорт - Выберите дорогу на карте или из списка ниже, которую вы хотите избежать во время навигации: + Выберите на карте или в списке ниже дорогу, которой хотите избежать при навигации: Моделировать навигацию Выберите файл трека для следования Голосовые подсказки @@ -2908,7 +2908,7 @@ пешком Показывать вдоль маршрута Покрытие - Тип дороги + Класс дороги Крутизна Добавить дом Добавить работу @@ -2923,7 +2923,7 @@ Скрыть треки Показать треки Время суток - Поворот за поворотом + По шагам Типы дорог Переключатель, чтобы показать или скрыть выбранные треки на карте. На %1$s @@ -2987,7 +2987,7 @@ Ступеньки Тропа Велодорожка - Неопределённая + Не определено Узнайте больше о маршрутизации OsmAnd в нашем блоге. Навигация на общественном транспорте в настоящее время проходит бета-тестирование, возможны ошибки и неточности. Добавить промежуточную точку @@ -3066,7 +3066,7 @@ Метро Лошадь Вертолёт - Вы можете добавить собственную модифицированную версию routing.xml в ..osmand/routing + Вы можете добавить свою модифицированную версию файла routing.xml в ..osmand/routing Выберите значок Лыжи Тип: %s @@ -3101,20 +3101,20 @@ Использовать WunderLINQ для контроля Значок Собранные данные - Нажмите ещё раз, чтобы изменить ориентацию карты + Нажмите ещё раз для смены ориентации карты Последний запуск OsmAnd завершился ошибкой. Пожалуйста, помогите нам улучшить OsmAnd, отправив нам отчёт об ошибке. Режим: %s Режим пользователя, полученный из: %s Повторяющееся имя BRouter (локально) - Альпийские/горные лыжи + Горнолыжные спуски Склоны для катания и спуска на горных лыжах и доступ к подъёмникам. Лыжные туры Сани Склоны для катания на санях. Более сложные маршруты с крутыми участками дороги. В целом, препятствия, которых следует избегать. Сложные маршруты, с опасными препятствиями и крутыми участками. - Предпочитаемая сложность + Предпочтительная сложность Служба скачивания OsmAnd Пурпурный Оценить @@ -3158,7 +3158,7 @@ Сбой Внедорожник Выбор настроек карты для профиля - Выбор настроек экрана для профиля + Настройка элементов экрана для профиля Выбор настроек навигации для профиля Выбор верхней границы изменений Использовать бесконтактный датчик (сенсорный выключатель) @@ -3187,8 +3187,8 @@ \nРасчёт: %.1f с, %d дорог, %d тайлов) Добавьте хотя бы один элемент в список «Быстрые действия» в настройках Маршруты для горнолыжного туризма. - Офпист - Фрирайды и офписты являются неофициальными неадаптированными трассами. Обычно неухоженные, неразмеченные и неосвещённые вечером. Вход на свой страх и риск. + Вне трассы + Фрирайды и внетрассовые маршруты — это неофициальные неподготовленные трассы. Обычно неухоженные, неразмеченные и неосвещённые вечером. Вход на свой страх и риск. Открыть трек Соединить разрывы (исключить пробелы) Кемпер @@ -3233,11 +3233,11 @@ Карта во время навигации Скорость движения, размеры, масса транспортного средства Параметры транспортного средства - Голосовые оповещения происходят только во время навигации. + Голосовые инструкции работают только при навигации. Навигационные инструкции и объявления Голосовые подсказки Экранные оповещения - Настройка параметров маршрута + Настройки маршрутизации Параметры маршрута Буфер Logcat Настройки плагинов @@ -3319,7 +3319,7 @@ «%1$s» уже существует. Перезаписать\? Не удалось экспортировать профиль. Импорт профиля - Чтобы добавить профиль, откройте его с помощью OsmAnd. + Для добавления профиля откройте файл профиля с помощью OsmAnd. %1$s импортирован. Белый Невозможно запустить механизм преобразования текста в речь. @@ -3365,8 +3365,8 @@ Разрешить только маршруты для катания на коньках Маршруты, подготовленные для фристайла или катания только на коньках без классических треков. Разрешить только классические маршруты - Маршруты, подготовленные только для классического стиля без конькобежных трасс. Сюда входят маршруты, подготовленные небольшим снегоходом с более свободной лыжнёй и трассами, подготовленные вручную лыжниками. - Предпочитать маршруты заданной сложности, хотя прокладка маршрута по более сложным или лёгким трассам всё же возможна, если они короче. + Маршруты только для классического стиля, без конькобежных трасс. Сюда входят маршруты, подготовленные небольшим снегоходом с более свободной лыжнёй и трассами, подготовленные вручную лыжниками. + Предпочитаемый уровень сложности маршрутов. Более сложные или лёгкие трассы могут использоваться, если они короче. Включать на повороте Класс 1 Класс 2 @@ -3390,7 +3390,7 @@ Эксперт Фрирайд Экстрим - Неопределённо + Неопределённая Канатная дорога Соединение Симулировать свою позицию используя записанный GPX трек. @@ -3534,12 +3534,12 @@ Сохранение нового профиля Не удалось создать резервную копию профиля. Очистить записанные данные\? - Побочный эффект: в записи трека будут отсутствовать все участки, где критерий минимальной скорости не был соблюдён (например, когда вы толкаете велосипед вверх по крутому склону). Также не будет информации о периодах покоя, например, во время отдыха. Это влияет на любой анализ или последующую обработку, например, при попытке определить общую продолжительность поездки, время в движении или среднюю скорость. + Побочный эффект: в треке будут отсутствовать все участки, где не соблюдён критерий минимальной скорости (например где вы толкаете велосипед вверх по крутому склону). Также не будет информации о периодах покоя, например во время отдыха. Это влияет на любой анализ или последующую обработку, например при попытке определить общую продолжительность поездки, время в движении или среднюю скорость. Побочный эффект: в результате фильтрации по точности, точки могут быть полностью пропущены, например, под мостами, под деревьями, между высокими зданиями или при определённых погодных условиях. Примечание: при включении GPS непосредственно перед записью точность определения первой точки может быть снижена, поэтому мы рассматриваем добавление секундной задержки перед записью точки (либо записи лучшей из трёх последовательных точек и т. д.), но пока это не реализовано. Фильтр предотвращает запись точек при отсутствии фактического перемещения и улучшает вид треков без обработки. Побочные эффекты: периоды в состоянии покоя не записываются вообще или только по одной точке каждый. Небольшие (в реальности) перемещения (например, в сторону, указывающие возможное изменение направления движения) могут быть отфильтрованы. Файл содержит меньше информации для последующей обработки и имеет худшую статистику, отфильтровывая явно избыточные точки во время записи, при этом потенциально сохраняя артефакты, вызванные плохим приёмом или эффектами модуля GPS. - Рекомендация: настройка 5 метров может должна вас устроить, если нет необходимости учитывать более короткие перемещения, и вы точное не хотите записывать данные в состоянии покоя. + Рекомендация: настройка 5 метров может подойти вам, если нет необходимости учитывать более короткие перемещения, и вы точно не хотите записывать данные в состоянии покоя. Указанные %1$s уже существуют в OsmAnd. Импорт данных из %1$s Импортирование @@ -3553,9 +3553,9 @@ Отклонение, при котором маршрут будет пересчитан. Легенда Невозможно разобрать геоссылку «%s». - Для отображения затенения рельефа на карте необходимы дополнительные карты. + Для отображения затенения рельефа требуются дополнительные карты. Мин. - Отображение затенения рельефа или карты уклонов. Подробнее об этих типах карт вы можете прочитать на нашем сайте. + Способы отображения рельефа местности: посредством теней (затенение рельефа) или цветов (карта уклонов). Подробнее об этих типах карт вы можете прочитать на нашем сайте. Прозрачность Уровни масштаба Пересчитывать маршрут в случае отклонения @@ -3580,14 +3580,14 @@ Примечание: проверка скорости > 0: большинство модулей GPS сообщают значение скорости только в том случае, если алгоритм определяет, что вы движетесь, и ничего, если вы не перемещаетесь. Следовательно, использование параметра > 0 в этом фильтре в некотором смысле приводит к обнаружению факта перемещения модуля GPS. Но даже если мы не производим данную фильтрацию во время записи, то всё равно эта функция используется при анализе GPX для определения скорректированного расстояния, то есть значение, отображаемое в этом поле, является расстоянием, записанным во время движения. Разделение записи Укажите веб-адрес со следующими параметрами: lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}. - В этом случае будут записываться только точки, измеренные с минимальной точностью (в метрах/футах согласно настройкам устройства). Точность — это близость измерений к истинному местоположению и не имеет прямого отношения к точности, подразумевающейся под разбросом повторных замеров. + "Будут записываться только точки, отвечающие по показателю минимальной точности (в метрах или футах — зависит от настроек системы). Точность — это близость измерений к истинному положению, и она не связана напрямую с точностью, которая представляет собой разброс повторных измерений." Рекомендация: попробуйте сначала воспользоваться детектором движения через фильтр минимального смещения (B), что может дать лучшие результаты и вы потеряете меньше данных. Если треки остаются шумными на низких скоростях, попробуйте использовать ненулевые значения. Обратите внимание, что некоторые измерения могут вообще не указывать значения скорости (некоторые сетевые методы), и в этом случае ничего не будет записываться. - Уклон использует цвета для визуализации крутизны рельефа. + Для визуализации крутизны рельефа используются цвета. Подробнее об уклонах можно прочитать в %1$s. Затенение рельефа - Затенение рельефа использует тёмные оттенки для отображения склонов, вершин и низменностей. - Для отображения склонов на карте необходимы дополнительные карты. - Уклоны + Для отображения склонов, вершин и низменностей используются тёмные тени. + Для отображения уклонов требуются дополнительные карты. + Карта уклонов Заменить этой точкой другую. Изменения применены к профилю «%1$s». Невозможно прочитать из «%1$s». @@ -3789,7 +3789,7 @@ Изменение масштаба карты кнопками громкости. Масштабирование кнопками громкости Укажите длину автомобиля, для длинных транспортных средств могут применяться ограничения на маршруте. - Удалить следующий пункт + Удалить ближайший пункт Задайте название точки Следующая точка маршрута будет удалена. Если это конечный пункт, навигация завершится. Информация о достопримечательностях из Википедии. Ваш карманный офлайн-путеводитель — просто включите плагин Википедии и читайте об объектах вокруг вас. @@ -3803,7 +3803,7 @@ Добавить к треку Для продолжения задайте рабочие дни Маршрут между точками - Составить маршрут + Составление маршрута Выберите способ разбиения: по времени или по расстоянию. Интервал между метками расстояния или времени на треке. Своё @@ -3883,17 +3883,17 @@ сохранен Добавьте хотя бы две точки. ПОВТОРИТЬ - • Обновлённый режим планирования маршрута позволяет использовать разные типы навигации для каждого сегмента и прикрепляет любой трек к дорогам + • Обновлённая функция планирования маршрута позволяет применять к сегментам разные режимы навигации и настраивать привязку к дорогам \n -\n • Новые параметры внешнего вида для треков: можно выбрать цвет, толщину, включите стрелки направления и отметки начала/окончания +\n • Новые настройки вида треков: выбор цвета и толщины линии, указатели направления, метки начала и конца маршрута \n -\n • Улучшена видимость велосипедных узлов +\n • Повышенная видимость велосипедных узлов \n -\n • Контекстное меню для треков с основной информацией +\n • Контекстное меню с основной информацией для треков \n \n • Улучшенные алгоритмы поиска \n -\n • Улучшены параметры следования по треку в навигации +\n • Улучшенные настройки следования по треку в Навигации \n \n • Исправлены проблемы с импортом/экспортом настроек профиля \n @@ -3901,4 +3901,14 @@ Последнее изменение Имя: Я - А Имя: А - Я + Значки старта и финиша + Спасибо за покупку \'Контурных линий\' + Избегать пешеходных дорожек + Избегать пешеходных дорожек + Подписка взимается за выбранный период. Отмените её в AppGallery в любое время. + Оплата будет снята с вашей учетной записи AppGallery при подтверждении покупки. +\n +\nПодписка продлевается автоматически, если она не будет отменена до даты продления. С вашего счета будет взиматься плата за период продления (месяц/три месяца/год) только в дату продления. +\n +\nВы можете управлять своими подписками и отменять их, перейдя в настройки AppGallery. \ No newline at end of file diff --git a/OsmAnd/res/values-sc/phrases.xml b/OsmAnd/res/values-sc/phrases.xml index df52122c1e..62f4c1ece8 100644 --- a/OsmAnd/res/values-sc/phrases.xml +++ b/OsmAnd/res/values-sc/phrases.xml @@ -3840,4 +3840,5 @@ Eletrodomèsticos minores Tabellone de sas tzucadas Ricàrriga de abba potàbile + GNL (LNG) \ No newline at end of file diff --git a/OsmAnd/res/values-sc/strings.xml b/OsmAnd/res/values-sc/strings.xml index accd1282b4..ac6f4f667a 100644 --- a/OsmAnd/res/values-sc/strings.xml +++ b/OsmAnd/res/values-sc/strings.xml @@ -3887,7 +3887,7 @@ Torra a fàghere • Funtzionalidade de pranificatione de un\'àndala agiornada: permitit de impreare castas diferentes de navigatzione pro segmentu e s\'inclusione de rastas \n -\n • New Appearance menu for tracks: select color, thickness, display direction arrows, start/finish icons +\n • Menù de Aparèntzia nou pro sas rastas: ischerta su colore, grussària, visulaizatzione de sas fritzas de diretzione, iconas de incumintzu/fine \n \n • Visibilidade megiorada de sos nodos pro sas bitzicletas. \n @@ -3903,4 +3903,14 @@ Ùrtima modìfica Nùmene: Z – A Nùmene: A – Z + Iconas de incumintzu/fine + Gràtzias pro àere comporadu \'Curvas de livellu\' + Costu periòdicu de s\'abbonamentu. Lu podes anullare in AppGallery cando boles. + Su pagamentu at a èssere addebitadu a su contu tuo de AppGallery cando sa còmpora at a èssere cunfirmada. +\n +\nS\'abbonamentu si rinnovat a sa sola automaticamente, francu chi siat istadu annulladu in antis de sa die de su rinnovu. Su contu tuo at a bènnere addebitadu pro su perìodu de rinnovu (mese/tres meses/annu) in sa die de rinnovu ebbia. +\n +\nPodes amministrare e annullare sos abbonamentos tuos intrende in sas impostatziones de AppGallery tuas. + Èvita sos martzapiedis + Èvita sos martzapiedis \ No newline at end of file diff --git a/OsmAnd/res/values-sk/phrases.xml b/OsmAnd/res/values-sk/phrases.xml index 14f1a5e6be..db5f8d31a6 100644 --- a/OsmAnd/res/values-sk/phrases.xml +++ b/OsmAnd/res/values-sk/phrases.xml @@ -509,7 +509,7 @@ Matričný úrad Dôchodkový fond Imigračný úrad - Daňová kontrola + Daňový úrad Štvrť Suchá bobová dráha Kolotoč s hojdačkami @@ -3274,9 +3274,9 @@ Mahájana Zamrznutí Turistická/trasová značka - - - + Ko-Shintō + Jizō + Prasat Apoštolská cirkev Radiačná onkológia Nebezpečenstvo @@ -3567,4 +3567,90 @@ Šípka Vibrácie Tlak + Skvapalnený zemný plyn + Obchod s orechmi + Včelí úľ + Cestovný poriadok + Odjazdy v reálnom čase + Intervaly + Áno + Tabuľa odjazdov: nie + Výťah + Mestský blok + Mestský obvod + Šípka: nie + Nie + Áno + Vibrovanie: nie + Výber hotovosti: cudzie karty + Výber hotovosti: minimálny nákup + Poplatok za výber hotovosti: nie + Poplatok za výber hotovosti: áno + Výber hotovosti: nie je vyžadovaný nákup + Výber hotovosti: vyžadovaný nákup + Mena výberu hotovosti + Limit výberu hotovosti + Typ výberu hotovosti: samoobslužný výber + Typ výberu hotovosti: pri pokladni + Operátor výberu hotovosti + Výber hotovosti + Výber hotovosti: áno + Zmiešané: nie + Zmiešané: áno + Ľad: nie + Ľad: áno + Trvanlivosť vodného zdroja: núdzový + Trvanlivosť vodného zdroja: trvalý + Ordinácia pôrodnej asistentky + Opatrovateľská služba + Ordinácia psychológa + Ordinácia liečiteľa + Ordinácia terapeuta + Ordinácia lekára + Zdravotná služba: testovanie: nie + Zdravotná služba: testovanie: áno + Zdravotná služba: podpora: nie + Zdravotná služba: podpora: áno + Zdravotná služba: očkovanie: nie + Zdravotná služba: očkovanie: áno + Zdravotná služba: prevencia: nie + Zdravotná služba: prevencia: áno + Zdravotná služba: starostlivosť o deti: nie + Zdravotná služba: starostlivosť o deti: áno + Zdravotná služba: vyšetrenie: nie + Zdravotná služba: vyšetrenie: áno + Zdravotná služba: poradenstvo: nie + Zdravotná služba: poradenstvo: áno + Zdravotná služba: opatrovateľstvo: áno + Zdravotná služba: opatrovateľstvo: nie + Správanie + Športová medicína + Malé elektrické spotrebiče + Tabuľa odjazdov/cestovný poriadok + Doplnenie pitnej vody + Nasávanie + Pod tlakom + Spodná voda + Potrubie + Sieť doplnenia pitnej vody + Doplnenie pitnej vody: nie + Áno + Prekážka + Výška vody: pod strednou hladinou + Výška vody: nad strednou hladinou + Výška vody: prekrýva + Výška vody: suché + Výška vody: ponorené + Výška vody: čiastočne ponorené + Nesprávne + Primitívne + Kontrastné + Len keď je povolená chôdza + Nie + Áno + Typ búdky + Búdka + Nie + Áno + Kancelária taxislužby \ No newline at end of file diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml index a9fbf672f4..f9b8f23366 100644 --- a/OsmAnd/res/values-sk/strings.xml +++ b/OsmAnd/res/values-sk/strings.xml @@ -224,7 +224,7 @@ Aktualizovať OsmAnd+ Stiahnite novú verziu aplikácie, aby ste mohli použiť nové mapové súbory. Online filter Nominatim - Hľadám umiestnenie… + Hľadám polohu… Moja poloha (nájdená) Adresa… Obľúbené miesta… @@ -325,11 +325,11 @@ ft mph mi - Zdieľať umiestnenie cez + Zdieľať polohu cez Pozícia: %1$s\n%2$s Na zobrazenie umiestnenia nasledujte webový odkaz %1$s alebo odkaz androidovského obsahu (intent link) %2$s - Poslať umiestnenie - Zdieľať umiestnenie + Poslať polohu + Zdieľať polohu GPX bod (waypoint) \"{0}\" pridaný Pridať waypoint do nahranej GPX stopy Administratíva @@ -419,7 +419,7 @@ sem zadajte čo chcete nájsť Mapa s vysokým rozlíšením Nerozťahovať (a nerozmazať) mapové dlaždice na obrazovkách s vysokou hustotou bodov. - Umiestnenie zatiaľ nenájdené. + Poloha zatiaľ nezistená. Hľadať hromadnú dopravu Hľadá sa preprava (bez cieľa): Hľadá sa preprava ({0} ako cieľ): @@ -743,7 +743,7 @@ Pridať pripomienku do aplikácie Kalendár Časovo obmedzené parkovanie Časovo neobmedzené parkovanie - Umiestnenie vášho zaparkovaného vozidla. %1$s + Poloha vášho zaparkovaného vozidla. %1$s Vyzdvihnúť vozidlo o: popoludní dopoludnia @@ -754,7 +754,7 @@ Označiť ako parkovacie miesto Odstrániť parkovaciu značku Východzí bod je príliš ďaleko od najbližšej cesty. - Zdieľané umiestnenie + Zdieľaná poloha Pridelená pamäť %1$s MB (Android limit %2$s MB, Dalvik %3$s MB). Pridelená pamäť Celková natívna pamäť pridelená aplikácii %1$s MB (Dalvik %2$s MB, iné %3$s MB). @@ -886,7 +886,7 @@ \n \nGlobálne údaje (medzi 70° severne a 70° južne) sú založené na meraní SRTM (Shuttle Radar Topography Mission) a ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), zobrazovacieho nástroja na palube Terra, vlajkového satelitu NASA Earth Observing System. ASTER je kooperatívne úsilie medzi NASA, Japonským ministerstvom hospodárstva, obchodu a priemyslu (METI) a Japonských vesmírnych systémov (J-spacesystems). Meranie vzdialenosti - Stlačte \"Použiť umiestnenie…\" pre pridanie poznámky k polohe. + Stlačte \"Použiť polohu…\" pre pridanie poznámky k polohe. Zvukové poznámky Vytvárajte obrazové/zvukové/video poznámky počas výletu, buď tlačidlom na mape alebo v kontextovom menu polohy na mape. Audio/video poznámky @@ -1393,7 +1393,9 @@ \n \nTento pohľad môže byť vypnutý jeho deaktivovaním tu alebo zmenou hodnoty v \"Štýl vykresľovania\" v \"Nastaviť mapu\". Cestovný mapový pohľad - Umiestnenie:\n Šírka %1$s\n Dĺžka %2$s + Poloha: +\n Šírka %1$s +\n Dĺžka %2$s Zobraziť dní pozadu Premenovanie zlyhalo. @@ -2632,7 +2634,7 @@ Cestovný pohľad Námorný Kopírovať názov bodu/umiestnenia - Nepomenované umiestnenie + Nepomenované miesto Zobraziť uzavreté poznámky Zobraziť/skryť OSM poznámky na mape. GPX - vhodné na export do JOSM a iných editorov OSM. @@ -3652,8 +3654,8 @@ Lombardsky %1$s / %2$s Platba bude stiahnutá z vášho účtu Google Play po potvrdení nákupu. -\n -\n Predplatné sa automaticky obnovuje ak nie je zrušené pred dátumom jeho obnovenia. Ďalšia platba za obdobie predplatného (mesiac/štvrťrok/rok) bude stiahnutá v deň obnovenia. +\n +\n Predplatné sa automaticky obnovuje, ak nie je zrušené pred dátumom jeho obnovenia. Ďalšia platba za obdobie predplatného (mesiac/štvrťrok/rok) bude stiahnutá v deň obnovenia. \n \n Vaše predplatné môžete spravovať a zrušiť v nastaveniach Google Play. Hľadať typy bodov záujmu @@ -3890,16 +3892,26 @@ \n \n • Zlepšená viditeľnosť bodov pre bicykle. \n -\n • Stopy je teraz možné aktivovať, pre kontextové menu sú základnými údajmi. +\n • Stopy je teraz možné aktivovať a majú kontextové menu so základnými údajmi. \n \n • Zlepšený algoritmus vyhľadávania \n \n • Zlepšené možnosti nasledovania stopy v navigácii \n -\n • Opravené problému s importom a exportom nastavení profilov +\n • Opravené problémy s importom a exportom nastavení profilov \n \n Naposledy zmenené Názov: Z – A Názov: A – Z + Ikony štartu/cieľa + Ďakujeme za zakúpenie modulu \'Vrstevnice\' + Predplatné bude spoplatnené v zvolenom intervale. Predplatné zrušte kedykoľvek na AppGallery. + Platba bude stiahnutá z vášho účtu AppGallery po potvrdení nákupu. +\n +\nPredplatné sa automaticky obnovuje, ak nie je zrušené pred dátumom jeho obnovenia. Ďalšia platba za obdobie predplatného (mesiac/štvrťrok/rok) bude stiahnutá v deň obnovenia. +\n +\n Vaše predplatné môžete spravovať a zrušiť v nastaveniach AppGallery. + Vyhnúť sa chodníkom pre chodcov + Vyhnúť sa chodníkom \ No newline at end of file diff --git a/OsmAnd/res/values-tr/phrases.xml b/OsmAnd/res/values-tr/phrases.xml index ed3292f096..16005d8a23 100644 --- a/OsmAnd/res/values-tr/phrases.xml +++ b/OsmAnd/res/values-tr/phrases.xml @@ -45,7 +45,7 @@ Kullanıcı tanımlı Paleontolojik alan Fırın - Likör dükkanı + İçki dükkanı Peynir dükkanı Çikolata dükkanı Kahve dükkanı @@ -326,7 +326,7 @@ Düşük enerji Ampüller Floresan tüpler Metal - Elektrik öğeleri + Elektrik ögeleri Beyaz eşya Yemeklik yağ Motor yağı @@ -362,7 +362,7 @@ Çocuk bezi Araba aküsü Arabalar - Bisiklet + Bisikletler Depolama Çöp bertaraf Çöp tenekesi @@ -766,7 +766,7 @@ Tarımsal motorlar Demirci Bira fabrikası - Boat builder + Gemi yapımcısı Ciltçi Marangoz Halı satıcısı @@ -866,7 +866,7 @@ Gün işareti Mesafe işareti Havuz - Lezbiyen + Su seti Simgesel Yapı Deniz işareti, ışık Deniz işareti, büyük ışık @@ -1740,8 +1740,8 @@ Açıklık genişliği 0.5$ madeni para Satıcı - Onarım - Onarım yok + Tamir + Tamir yok Elektrikli araçların tamiri Motosiklet tamiri Evet @@ -2043,8 +2043,8 @@ Bisiklet kiralama: hayır Pompa Bisiklet pompası: hayır - Kendin-Yap onarım için araçlar - Kendin-Yap onarım için bisiklet araçları: hayır + Kendin-Yap tamir için araçlar + Kendin-Yap tamir için bisiklet araçları: hayır Temizleme Bisiklet temizleme: hayır Zincir aleti @@ -2148,7 +2148,7 @@ Kostüm Geleneksel Takım elbise - Hamile + Hamilelik Nostalji Büyük beden Okul @@ -2201,8 +2201,8 @@ Karavan: hayır Hazırlıksız: evet Hazırlıksız: hayır - Tuvalet atığı istasyonu: evet - Tuvalet atığı istasyonu: hayır + Sıhhi atık boşaltma istasyonu: evet + Sıhhi atık boşaltma istasyonu: hayır Evet Güç kaynağı: hayır Güç kaynağı (soket): CEE 17 mavi @@ -2443,4 +2443,476 @@ Tarihi tren istasyonu Tarihi çiftlik Pa (müstahkem maori yerleşimi) + Futsal (salon futbolu) + ATM: hayır + ATM: evet + Boks + Lakros + Disk iteleme oyunu + Squash (duvar tenisi) + Uzaktan kumandalı araba yarışı + Disk golf + Judo + Badminton + Karting + Netbol + Koşma + Gal oyunları + Dojo + Resmi adı + Araç park çatısı + Garaj bölmeleri + Tür: yüzey + Su ısıtıcısı: hayır + Su ısıtıcısı: evet + Mikrodalga fırın: hayır + Mikrodalga fırın: evet + Bilardo + Yüzme: hayır + Yüzme: evet + Tekne depolama + Referans numarası + Tünel numarası + Köprü numarası + Taşıma: evet + Uzunluk + Havai fişek mağazası + Elektronik tamiri + Hackerspace + Fitness istasyonu + Bina türü: piramit + Palyatif tıp + Davranış + Derinlik psikolojisi + Naturopati + Kayropraktik + Bitkisel tıp + Reiki + Geleneksel Çin tıbbı + Homeopati + Akupunktur + Yetişkin psikiyatrisi + Podoloji + Spor hekimliği + Manuel terapi + Konuşma terapisi + Klinik patoloji + Optometri + Bağımlılık tedavisi + Sağlık uzmanlığı: obstetrik (sezaryen): hayır + Obstetrik (sezaryen) + Sağlık uzmanlığı: sosyal pediatri: hayır + Sosyal pediatri + Sağlık uzmanlığı: obstetrik (doğum öncesi): hayır + Obstetrik (doğum öncesi) + Sağlık uzmanlığı: obstetrik (doğum sonrası): hayır + Obstetrik (doğum sonrası) + Sağlık uzmanlığı: tropikal tıp: hayır + Tropikal tıp + Onkoloji + Patolojik anatomi + Nükleer tıp + Endokrinoloji + Nöropsikiyatri + Beyin ve sinir cerrahisi + Nefroloji (böbrek hastalıkları) + Diş hekimliği + Gastroenteroloji + Tıbbi görüntüleme + Çene-yüz cerrahisi + Fiziksel tıp ve rehabilitasyon + Çocuk psikiyatrisi + İş terapisi + Biyokimya + Fizyoterapi + Ortodonti + Plastik cerrahi + Sağlık uzmanlığı: kaza ve acil tıp: hayır + Kaza ve acil tıp + Hamilelik + Diş, ağız ve çene-yüz cerrahisi + Göğüs hastalıkları + Anesteziyoloji + Osteopati + Klinik biyoloji + Travmatoloji + Kardiyoloji + Dermato-venereoloji + Nöroloji + Psikiyatri + Radyoterapi + Radyoloji + Genel cerrahi + Üroloji + Dermatoloji + Sağlık uzmanlığı: pediatri: hayır + Pediatri + Kulak burun boğaz + Ortopedi + İç hastalıkları + Jinekoloji + Oftalmoloji + Pratisyen doktor + Ağır yük araçları + Bisikletler + Konteynerler + Araçlar + Yolcular + Apartman + Ok: hayır + Evet + Evet + Titreşim: hayır + Evet + Evet + Hayır + Evet + Göl + Irmak + Hamam + Konum + Feribot + Tramvay + Otobüs + Tren + Evet + Çocuk + Akademik + Din + Kan bağışı + Toptancı + Yalnızca + Evet + Ayakkabı tamiri + Kaya + Tekerlek + Araba tamiri + Tür: tarım + Posta kodu + Ev numarası + Posta kutusu + Park ücreti + Park ücreti: hayır + Park ücreti: evet + Tır: hayır + Tır: evet + Bisiklet: hayır + Bisiklet: evet + Araba: hayır + Araba: evet + Kısıtlı + Kapalı + Açık + Görünürlük: sokak + Görünürlük: ev + Konum: duvar + Konum: yerüstü + Konum: sualtı + Konum: yeraltı + Metal ızgara + Yapay çimen + Kil + Hazine + Kamu hizmeti + Bakanlık + Arşiv + + Yatak + Yön: her + Yön: çıkış + Yön: giriş + Yön: aşağı + Yön: yukarı + Yön: saat yönünün tersi + Yön: saat yönü + Yön: geri + Yön: ileri + Yön: kuzey-kuzeybatı + Yön: kuzeybatı + Yön: batı-kuzeybatı + Yön: batı + Yön: batı-güneybatı + Yön: güneybatı + Yön: güney-güneybatı + Gün: güney + Yön güney-güneydoğu + Yön: güneydoğu + Yön: doğu-güneydoğu + Yön: doğu + Yön: doğu-kuzeydoğu + Yön: kuzeydoğu + Yön: kuzey-kuzeydoğu + Yön: kuzey + Sürüngen + Kuşhane + Kuşlar + Elektronik tamir: televizyon + Elektronik tamir: telefon + Elektronik tamir: bilgisayar + Şarap: perakende + Şarap: evet + Evet + Çilingir + Elektrik mağazası + Tibet + Bulgar + Yahudi + Senegal + Mısır + Avustralya + Suriye + Hollanda + Tayvan + Pakistan + İngiliz + Hawaii + Ermeni + Jamaika + İsveç + Kanton + İsviçre + Bask + Belçika + Afgan + Ukrayna + Orta Doğu + Moğol + Nepal + Latin Amerika + Britanya + Küba + Çek + Özbek + Avrupa + Lao + Macar + Etiyopya + İrlanda + Malezya + Avusturya + Fas + Madagaskar + Fars + Bolivya + Hırvat + Peru + Balkan + Arjantin + Karayip + Afrika + Endonez + Dan + Arap + Brezilya + Leh + Gürcü + Portekiz + Filipin + Rus + Lübnan + Bavyera + Akdeniz + Kore + Vietnam + İspanyol + Türk + Uluslararası + Tay + Yunan + Fransız + Asya + Amerikan + Hint + Alman + Japon + Meksika + Çin + İtalyan + Bölgesel + Patates + Şarap + Çikolata + Kanat + Et + Yoğurt + Tatlı + Salata + Çorba + Çay + Makarna + Biftek + Kahvaltı + Mangal + Suşi + Dondurma + Tavuk + Döner + Kebap + Sandviç + Pizza + Hamburger + Kahve + Noel ağacı + Noel + Vikipedi + Uzunluk + Su deposu + Delegasyon + Başkonsolosluk + Konsolosluk + Yalnızca + Evet + Su kültürü: midye + Su kültürü: balık + Su kültürü: karides + Su kültürü + Termometre: hayır + Termometre + Barometre: hayır + Barometre + Teleskop + Özel + Askeri/kamusal + Askeri + Kamusal + Bölgesel + Uluslararası + Yaz kampı + Tuz: hayır + Tuz + Derinlik + Otizm: hayır + Otizm: evet + Ebola: hayır + Ebola: evet + Sıtma: hayır + Sıtma: evet + Evet + Acil: hayır + Acil: evet + Danışma: hayır + Danışma: evet + Kapasite (yatak) + Danışma (evsiz): hayır + Danışma (evsiz): evet + Danışma (aile): hayır + Danışma (aile): evet + Danışma (uyuşturucu): hayır + Danışma (uyuşturucu): evet + Danışma (bağımlılık): evet + Danışma (bağımlılık): evet + Sağlık çalışanının işi: psikolog + İlk yardım çantası + Duvar + Yeraltı + Sağlık merkezi türü: sahra hastanesi + Sağlık merkezi türü: laboratuvar + Sağlık hizmeti: aşılama: hayır + Sağlık hizmeti: aşılama: evet + Sağlık hizmeti: danışma: hayır + Sağlık hizmeti: danışma: evet + Sağlık hizmeti: hasta bakıcılık: hayır + Sağlık hizmeti: hasta bakıcılık: evet + Geleneksel Tibet + Geleneksel Moğol + Geleneksel Çin + Batılı + Solaryum + Çeşme + Elektrikli süpürtge: hayır + Elektrikli süpürge + Evet + Evet + Evet + Ev adı + Patlama zamanı (UTC) + Patlama tarihi (UTC) + Patlama türü: yeraltı + Patlama türü: yeraltı, tünel + Patlama: ülke + Korunan nesne: su + Korunan nesne: yaşam alanı + Korunan nesne: doğa + Korunan nesne: tarihi + Koruma alanı + Yapım aşamasında + Fast food + Pankek + Pasta + Izgara + Sosis + Sosisli sandviç + Köri + Krep + Erişte (ramen) + Çörek (donut) + Erişte (noodle) + Deniz ürünleri + Balık ve patates kızartması + Noel: web sitesi + Noel: konum + Noel: çalışma saatleri + Noel: not + Noel: etkinlik dönemi + Ağaç dükkanı + Noel dükkanı + Noel piramidi + Noel marketi + Noel etkinliği + Yolcu bilgilendirme ekranı: hayır + Yolcu bilgilendirme ekranı: evet + Higrometre: hayır + Higrometre + Kullanım: casusluk + Kullanım: araştırma + Kullanım: casusluk + Kullanım: eğitim + Ev ziyareti: hayır + Yunani + Sidda + Kampo + Ayurveda + Sıhhi atık boşaltma istasyonu + Yalnızca adil ticaret ürünleri + Adil ticaret: hayır + Adil ticaret: evet + Basınçlı hava: hayır + Bitki fidanlığı + Motorlu tekneler: hayır + Motorlu tekneler: evet + Tekne kiralama + Konum: giriş + Yön tabelası: orman tahsisi + Yön tabelası: orman bölmesi + Motosiklet giysileri: hayır + Motosiklet giysileri + Lastik: hayır + Lastik + Yedek parça: hayır + Yedek parça + Tamir: hayır + Tamir + Kiralama: hayır + Kiralama + Müzik okulu + Şarap: servis + Parti malzemeleri + Cajun + Tex-mex + Baget + Waffle + Krep + Falafel + Taco + Kantin + Tuzlu krep + Kızarmış tavuk + Kuskus + Fırın + Bistro + Kızarmış yiyecekler + Dondurulmuş yoğurt + Şarküteri + Turta + Çay dükkanı \ No newline at end of file diff --git a/OsmAnd/res/values-tr/strings.xml b/OsmAnd/res/values-tr/strings.xml index 9713a69b10..071ecab230 100644 --- a/OsmAnd/res/values-tr/strings.xml +++ b/OsmAnd/res/values-tr/strings.xml @@ -892,7 +892,7 @@ Güzergahı göster Yönlendirmeyi başlatın Lütfen önce hedefi ayarlayın - Açılış saatleri + Çalışma saatleri Yetkilendirme başarısız Sokaklar/binalar yükleniyor… Sokaklar yükleniyor… @@ -1374,7 +1374,7 @@ Açılış Kapanış İletişim Bilgileri - Açılış saatleri ekle + Çalışma saatleri ekle POI Türü Dash %1$s satır sayısı POI türü belirtiniz. @@ -3859,4 +3859,14 @@ Son değiştirme İsim: Z – A İsim: A – Z + Başlangıç/bitiş simgeleri + \'Eş yükselti eğrileri\'ni satın aldığınız için teşekkürler + Abonelik seçilen dönem başına ücretlendirilir. İstediğiniz zaman AppGallery\'den iptal edin. + Ödeme, satın alma onaylandığında AppGallery hesabınızdan alınacaktır. +\n +\nYenileme tarihinden önce iptal edilmedikçe abonelik otomatik olarak yenilenir. Hesabınızdan yenileme süresi (ay/üç ay/yıl) için yalnızca yenileme tarihinde ücret alınacaktır. +\n +\n AppGallery ayarlarınıza giderek aboneliklerinizi yönetebilir ve iptal edebilirsiniz. + Yaya yollarından kaçın + Yaya yollarından kaçın \ No newline at end of file diff --git a/OsmAnd/res/values-uk/phrases.xml b/OsmAnd/res/values-uk/phrases.xml index 4375b43720..493e631c02 100644 --- a/OsmAnd/res/values-uk/phrases.xml +++ b/OsmAnd/res/values-uk/phrases.xml @@ -3832,4 +3832,5 @@ Невеликі електроприлади Вулик Насіннєвий магазин + СПГ \ No newline at end of file diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml index 905af29b00..fda15847c5 100644 --- a/OsmAnd/res/values-uk/strings.xml +++ b/OsmAnd/res/values-uk/strings.xml @@ -42,7 +42,7 @@ \nБудь яка з цих мап може використовуватись як основна мапа в OsmAnd, або як покриття чи підкладка до іншої основної мапи (наприклад усталена безмережева мапа OsmAnd). Для того, щоб зробити більш помітною будь-яку мапу-підкладку, певні елементи векторної мапи OsmAnd можна легко сховати через меню „Налаштування мапи“ за бажанням, щоб зробити будь-яку підкладку мапи помітнішою.. \n \nКвадрати мап можна отримувати безпосередньо з мережевих джерел або підготувати їх для безмережевого використання (та вручну скопіювати в теку даних OsmAnd) у вигляді бази даних SQLite, яку можна створити за допомогою різноманітних сторонніх знаряддь підготовки мап. - Показує налаштування для увімкнення фонового трекінгу та навігації шляхом періодичного пробудження GPS-передавача (з вимкненим екраном). + Показує налаштування для увімкнення відстеження у тлі та навігації шляхом періодичного пробудження GPS-передавача (з вимкненим екраном). Робить спеціальні можливості пристрою доступними безпосередньо в OsmAnd. Це полегшує, наприклад, налаштування частоти мовлення для голосу синтезу мовлення, настроювання D-Pad навігації, за допомогою трекбола для контролю масштабу або зворотного зв\'язку синтезу мовлення, наприклад, щоб автоматично оголосити свою позицію. Налаштування функцій розробки та налагодження, як-от навігаційне моделювання, дієвість відмальовування чи голосові підказки. Призначений для розробників, не потрібний для звичайного використання застосунку. Втулки @@ -158,7 +158,7 @@ Файл з раніше імпортованими Закладками вже існує. Замінити його? Налаштування профілю Усталений профіль - Вид мапи й налаштування навігації зберігаються для кожного окремого профілю. Встановіть ваш типовий профіль. + Вид мапи й налаштування навігації зберігаються для кожного окремого профілю. Встановіть ваш усталений профіль. Навігація Визначити налаштування навігації. Загальні налаштування @@ -169,8 +169,8 @@ Ім\'я користувача OSM Потрібно для входу на openstreetmap.org. Пароль - Фоновий режим - OsmAnd працює у фоновому режимі з вимкненим екраном. + Режим тла + OsmAnd працює у режимі тла з вимкненим екраном. Не вистачає місця на диску для завантаження %1$s MB (вільно: %2$s). Завантажити {0} файл(ів)\? \n{1} МБ (із {2} МБ) буде використано. @@ -281,8 +281,8 @@ Прозорість Змінити прозорість основної мапи. Прозорість мапи - Фонова мапа… - Фонова мапа + Мапа підкладки… + Мапа підкладки Виберіть тлову мапу Верхній шар… Нема @@ -428,7 +428,7 @@ Відсутні точки Закладок для збереження Імпортувати Не вдалося завантажити GPX. - Відправити звіт + Надіслати звіт Не знайдено завантаженої мапи на карті пам\'яті. Почніть вводити текст для пошуку POI Всі @@ -456,7 +456,7 @@ Виберіть метод позиціонування, що використовується тловою службою: Джерело позиціювання Відслідковує Ваше місцерозташування, поки екран вимкнено. - Запустити OsmAnd у фоні + Запустити OsmAnd у тлі Службі навігації у тлі необхідно, аби постачальник позиціювання був активним. Сховати фільтр Показати фільтр @@ -759,8 +759,9 @@ Торкніться значка блокування, щоб розблокувати Розблокувати Запустити -\n застосунок у фоновому режимі - Вимкнути\nфоновий режим +\n застосунок у режимі тла + Вимкнути +\nрежим тла Час прибуття не відмічено Мапа @@ -846,7 +847,7 @@ Безпечний режим Програму запущено в безпечному режимі (вимкніть його в \'Налаштуваннях\'). Оберіть \"Використати місцезнаходження...\" для прив\'язки нотатки до даного місцезнаходження. - Фоновий режим OsmAnd досі запущений. Зупинити його роботу також? + Службу OsmAnd у тлі досі запущено. Зупинити її роботу також\? Закрити набір змін Програма \'ZXing Barcode Scanner\' не встановлена. Шукати в Google Play? Виберіть кольорову схему доріг: @@ -1078,9 +1079,9 @@ Колір Продовжувати Зупинити - Ввімкнути фоновий режим GPS + Увімкнути режим GPS у тлі Інтервал вмикання GPS - Зупинити фоновий режим GPS? + Зупинити режим GPS у тлі\? Бажана мова для підписів на мапі (якщо вона недоступна, будуть використані англійська чи місцева мови). Бажана мова мапи Назви місцевою мовою @@ -1686,7 +1687,7 @@ м/с Запис подорожі Навігація - Працює у фоновому режимі + Працює у тлі Відомості про закладки Зупинити симуляцію Вашої позиції. Пошук адреси @@ -2390,12 +2391,12 @@ \n• Опціональний запис подорожі в локальний GPX-файл чи онлайн-сервіс \n• Опціональне відображення швидкості та висотного розташування \n• Відображення горизонталей та рельєфу (через додатковий втулок) - Безпосередній вклад у OSM -\n• Звітуйте про помилки в даних -\n• Вивантажуйте GPX-треки в OSM безпосередньо з програми -\n• Додавайте POI (цікаві точки) та безпосередньо вивантажуйте їх в OSM (чи пізніше, якщо зараз Ви в офлайні) -\n• Опція запису подорожі також і у фоновому режимі (в той час як пристрій знаходиться в сплячому режимі) -\nOsmAnd — вільне й відкрите програмне забезпечення, що активно розвивається. Кожен може внести свій вклад, звітуючи про помилки, поліпшуючи переклад чи розробляючи нові функції. Також проєкт покладається на фінансові внески для оплати розробки та тестування нових функціональних можливостей. + Безпосередній вклад у OSM +\n• Звітуйте про помилки в даних +\n• Вивантажуйте GPX-треки в OSM безпосередньо з програми +\n• Додавайте POI (цікаві точки) та безпосередньо вивантажуйте їх в OSM (чи пізніше, якщо зараз Ви в офлайні) +\n• Опція запису подорожі також і в режимі тла (в той час як пристрій знаходиться в сплячому режимі) +\nOsmAnd — вільне й відкрите програмне забезпечення, що активно розвивається. Кожен може внести свій вклад, звітуючи про помилки, поліпшуючи переклад чи розробляючи нові функції. Також проєкт покладається на фінансові внески для оплати розробки та тестування нових функціональних можливостей. \n Приблизне охоплення мап та якість: \n • Західна Європа: **** @@ -3043,7 +3044,7 @@ Лижі Показати компас-лінійку Приховати компас-лінійку - Оберіть піктограму + Оберіть значок Режим: %s Користувацький режим, похідний від: %s Лижі @@ -3068,31 +3069,31 @@ Натисніть ще раз для зміни орієнтації мапи Мінімальна швидкість Максимальна швидкість - Типова швидкість + Усталена швидкість Змінити налаштування усталеної швидкості Встановити мінімальну/максимальну швидкість Новий профіль Збій Під час останнього запуску OsmAnd сталася помилка. Допоможіть нам покращити OsmAnd - надішліть повідомлення про помилку. НЛО - • Профілі застосунку: створюйте власний профіль з довільною піктограмою та кольором для ваших особистих потреб -\n -\n• Налаштуйте типову мінімальну/максимальну швидкості профілю -\n -\n• Додано віджет з поточними координатами -\n + • Профілі застосунку: створюйте власний профіль з довільним значком та кольором для ваших особистих потреб +\n +\n• Налаштуйте усталену найменшу/найбільшу швидкості профілю +\n +\n• Додано віджет з поточними координатами +\n \n• Додано можливість показувати на мапі компас і радіус-лінійку -\n +\n \n• Виправлено записування шляху у тлі -\n -\n• Покращено завантаження мап у тлі +\n +\n• Покращено завантаження мап у тлі \n \n• Повернено параметр \'Увімкнути екран\' \n -\n• Виправлено вибір мови Wikipedia -\n +\n• Виправлено вибір мови Wikipedia +\n \n• Виправлено поведінку кнопки компаса під час навігації -\n +\n \n• Інші виправлення помилок \n \n @@ -3430,7 +3431,7 @@ OSM Значок відображається під час навігації чи переміщення. Значок показано в спокої. - Перевіряти та обмінюватися докладними журналами застосунку + Переглянути та надіслати докладний журнал застосунку Не вдалося розібрати метод \'%s\'. Для використання цього параметра потрібен дозвіл. Це низькошвидкісний відсічний фільтр, щоб не записувати точки нижче певної швидкості. Це може призвести до плавнішого вигляду записаних треків при перегляді на мапі. @@ -3670,7 +3671,7 @@ Профілі навігації • Нові автономні мапи схилів \n -\n• Налаштування вибраних та GPX шляхових точок - спеціальні кольори, піктограми, форми +\n• Налаштування вибраних та GPX шляхових точок - спеціальні кольори, значки, форми \n \n• Налаштування порядку елементів меню \"Контекстне меню\", \"Налаштувати мапу\" та \"Скринька\" \n @@ -3694,7 +3695,7 @@ Кнопка показу або приховування громадського транспорту на мапі. Створити / змінити POI Додати / правити вибране - Відновити типовий порядок елементів + Відновити усталене впорядкування Повернутися до редагування Ви можете отримати доступ до цих дій, торкнувшись кнопки “%1$s”. Продовжити @@ -3741,7 +3742,7 @@ \nОдин тиждень - 10 080 хвилин. \nОдин місяць - 43 829 хвилин. Виберіть спосіб зберігання завантажених плиток. - Типовий час до вимкнення екрану + Усталений час до вимкнення екрану Ви можете експортувати або імпортувати швидкі дії з профілями застосунку. Видалити все\? Ви дійсно бажаєте безповоротно видалити %d швидких дій\? @@ -3805,7 +3806,7 @@ Маршрут між точками Планування маршруту Додати до треку - Показувати піктограми старт та фініш + Показувати значки початку та завершення Встановити ширину Виберіть інтервал показу міток часу або відстані для показу поверх треку. Виберіть власний варіант поділу: за часом чи відстанню. @@ -3863,7 +3864,7 @@ Змінити вид маршруту раніше Змінити вид маршруту після Вкажіть інтервал для загального запису поїздки (включається через віджет запису подорожі на мапі). - Типова системи + Усталена системна Всі наступні сегменти Попередній сегмент Усі попередні сегменти @@ -3876,14 +3877,14 @@ Відкрити збережений трек збережено Додайте принаймні дві точки. - Запис треку буде зупинено після припинення роботи застосунку через меню з переліком нещодавно запущених застосунків. (Індикатор, який інформує про роботу OsmAnd у фоні зникне з панелі сповіщень Android) + Запис треку буде зупинено після припинення роботи застосунку через меню з переліком нещодавно запущених застосунків. (Індикатор, що повідомляє про роботу OsmAnd у тлі зникне з панелі сповіщень Android) Спрощений трек Буде збережено лише лінію маршруту, а проміжні точки буде видалено. Назва файлу Повторити • Оновлено функції планування маршруту: дозволено застосувати різні типи переходів для кожного сегмента і прив\'язати будь-який трек до доріг \n -\n• Нове меню вигляду треків: вибір кольору, товщина, вигляд стрілки напрямку, піктограми початку/завершення +\n• Нове меню вигляду треків: вибір кольору, товщина, вигляд стрілки напрямку, значки початку/завершення \n \n• Покращено оглядовість велосипедних вузлів \n @@ -3899,4 +3900,6 @@ Востаннє змінено За назвою: Я — А За назвою: А — Я + Значки початку/завершення + Дякуємо за придбання «Горизонталей» \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rTW/phrases.xml b/OsmAnd/res/values-zh-rTW/phrases.xml index 25766f928e..e36aa4414d 100644 --- a/OsmAnd/res/values-zh-rTW/phrases.xml +++ b/OsmAnd/res/values-zh-rTW/phrases.xml @@ -3843,4 +3843,5 @@ 小電器 蜂箱 堅果店 + LNG \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rTW/strings.xml b/OsmAnd/res/values-zh-rTW/strings.xml index b12cc12a51..1d6615754e 100644 --- a/OsmAnd/res/values-zh-rTW/strings.xml +++ b/OsmAnd/res/values-zh-rTW/strings.xml @@ -3425,7 +3425,7 @@ OSM 在導航或移動時顯示的圖示。 靜止時顯示的圖示。 - 檢視及分享應用程式的紀錄檔 + 檢查及分享應用程式的詳細紀錄 無法解析地理含義「%s」。 您已紀錄的軌跡位於 %1$s,或是 OsmAnd 資料夾。 您的 OSM 註記位於 %1$s。 @@ -3899,4 +3899,14 @@ 最後修改時間 名稱:Z – A 名稱:A – Z + 開始/結束圖示 + 感謝您購買 \'Contour lines\' + 按選定週期收取訂閱費用。隨時在 AppGallery 上取消。 + 確認購買後將會從您的 AppGallery 帳號中付款。 +\n +\n除非在續訂日期前取消,否則就會自動續訂。您的帳號將只會在續訂日期收取訂閱週期(一個月/三個月/一年)的費用。 +\n +\n您可以在您的 AppGallery 設定中管理與取消您的訂閱。 + 避免人行道 + 避免人行道 \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 086a7470d5..e46bf65ab5 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,19 @@ Thx - Hardy --> + Switch to Java (safe) Public Transport routing calculation + Native Public Transport development + Recalculates only the initial part of the route. Can be used for long trips. + Two-phase routing for car navigation. + Complex routing + OsmAnd Live data + OsmAnd Live data + Development + Avoid footways + Avoid footways + Payment will be charged to your AppGallery account at the confirmation of purchase.\n\nSubscription automatically renews unless it is canceled before the renewal date. Your account will be charged for renewal period(month/three month/year) only on the renewal date.\n\nYou can manage and cancel your subscriptions by going to your AppGallery settings. + Subscription charged per selected period. Cancel it on AppGallery at any time. + Thank you for purchasing \'Contour lines\' Start/finish icons Name: A – Z Name: Z – A @@ -3657,7 +3670,6 @@ You need to be online to install this plugin. Get Smart route recalculation - For long trips, only recalculate the initial part of the route. Do you like OsmAnd? Your opinion and feedback is valued. Rate this app diff --git a/OsmAnd/res/xml/development_settings.xml b/OsmAnd/res/xml/development_settings.xml index d01a1f169d..231bd469bc 100644 --- a/OsmAnd/res/xml/development_settings.xml +++ b/OsmAnd/res/xml/development_settings.xml @@ -25,46 +25,11 @@ android:summaryOn="@string/shared_string_enabled" android:title="@string/safe_mode" /> - - - - - - - - - - skuDetailsList; + + /* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY + * (that you got from the Google Play developer console). This is not your + * developer public key, it's the *app-specific* public key. + * + * Instead of just storing the entire literal string here embedded in the + * program, construct the key at runtime from pieces or + * use bit manipulation (for example, XOR with some other string) to hide + * the actual key. The key itself is not secret information, but we don't + * want to make it easy for an attacker to replace the public key with one + * of their own and then fake messages from the server. + */ + private static final String BASE64_ENCODED_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgk8cEx" + + "UO4mfEwWFLkQnX1Tkzehr4SnXLXcm2Osxs5FTJPEgyTckTh0POKVMrxeGLn0KoTY2NTgp1U/inp" + + "wccWisPhVPEmw9bAVvWsOkzlyg1kv03fJdnAXRBSqDDPV6X8Z3MtkPVqZkupBsxyIllEILKHK06" + + "OCw49JLTsMR3oTRifGzma79I71X0spw0fM+cIRlkS2tsXN8GPbdkJwHofZKPOXS51pgC1zU8uWX" + + "I+ftJO46a1XkNh1dO2anUiQ8P/H4yOTqnMsXF7biyYuiwjXPOcy0OMhEHi54Dq6Mr3u5ZALOAkc" + + "YTjh1H/ZgqIHy5ZluahINuDE76qdLYMXrDMQIDAQAB"; + + public InAppPurchaseHelperImpl(OsmandApplication ctx) { + super(ctx); + purchases = new InAppPurchasesImpl(ctx); + } + + @Override + public void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback) { + if (callback != null) { + callback.onSuccess(); + } + } + + private BillingManager getBillingManager() { + return billingManager; + } + + protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand runnable) { + billingManager = new BillingManager(ctx, BASE64_ENCODED_PUBLIC_KEY, new BillingManager.BillingUpdatesListener() { + + @Override + public void onBillingClientSetupFinished() { + logDebug("Setup finished."); + + BillingManager billingManager = getBillingManager(); + // Have we been disposed of in the meantime? If so, quit. + if (billingManager == null) { + stop(true); + return; + } + + if (!billingManager.isServiceConnected()) { + // Oh noes, there was a problem. + //complain("Problem setting up in-app billing: " + result); + notifyError(taskType, billingManager.getBillingClientResponseMessage()); + stop(true); + return; + } + + runnable.run(InAppPurchaseHelperImpl.this); + } + + @Override + public void onConsumeFinished(String token, BillingResult billingResult) { + } + + @Override + public void onPurchasesUpdated(final List purchases) { + + BillingManager billingManager = getBillingManager(); + // Have we been disposed of in the meantime? If so, quit. + if (billingManager == null) { + stop(true); + return; + } + + if (activeTask == InAppPurchaseTaskType.REQUEST_INVENTORY) { + List skuInApps = new ArrayList<>(); + for (InAppPurchase purchase : getInAppPurchases().getAllInAppPurchases(false)) { + skuInApps.add(purchase.getSku()); + } + for (Purchase p : purchases) { + skuInApps.add(p.getSku()); + } + billingManager.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuInApps, new SkuDetailsResponseListener() { + @Override + public void onSkuDetailsResponse(BillingResult billingResult, final List skuDetailsListInApps) { + // Is it a failure? + if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) { + logError("Failed to query inapps sku details: " + billingResult.getResponseCode()); + notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage()); + stop(true); + return; + } + + List skuSubscriptions = new ArrayList<>(); + for (InAppSubscription subscription : getInAppPurchases().getAllInAppSubscriptions()) { + skuSubscriptions.add(subscription.getSku()); + } + for (Purchase p : purchases) { + skuSubscriptions.add(p.getSku()); + } + + BillingManager billingManager = getBillingManager(); + // Have we been disposed of in the meantime? If so, quit. + if (billingManager == null) { + stop(true); + return; + } + + billingManager.querySkuDetailsAsync(BillingClient.SkuType.SUBS, skuSubscriptions, new SkuDetailsResponseListener() { + @Override + public void onSkuDetailsResponse(BillingResult billingResult, final List skuDetailsListSubscriptions) { + // Is it a failure? + if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) { + logError("Failed to query subscriptipons sku details: " + billingResult.getResponseCode()); + notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage()); + stop(true); + return; + } + + List skuDetailsList = new ArrayList<>(skuDetailsListInApps); + skuDetailsList.addAll(skuDetailsListSubscriptions); + InAppPurchaseHelperImpl.this.skuDetailsList = skuDetailsList; + + mSkuDetailsResponseListener.onSkuDetailsResponse(billingResult, skuDetailsList); + } + }); + } + }); + } + for (Purchase purchase : purchases) { + if (!purchase.isAcknowledged()) { + onPurchaseFinished(purchase); + } + } + } + + @Override + public void onPurchaseCanceled() { + stop(true); + } + }); + } + + @Override + public void purchaseFullVersion(@NonNull final Activity activity) { + notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION); + exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, new InAppCommand() { + @Override + public void run(InAppPurchaseHelper helper) { + try { + SkuDetails skuDetails = getSkuDetails(getFullVersion().getSku()); + if (skuDetails == null) { + throw new IllegalArgumentException("Cannot find sku details"); + } + BillingManager billingManager = getBillingManager(); + if (billingManager != null) { + billingManager.initiatePurchaseFlow(activity, skuDetails); + } else { + throw new IllegalStateException("BillingManager disposed"); + } + commandDone(); + } catch (Exception e) { + complain("Cannot launch full version purchase!"); + logError("purchaseFullVersion Error", e); + stop(true); + } + } + }); + } + + @Override + public void purchaseDepthContours(@NonNull final Activity activity) { + notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS); + exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, new InAppCommand() { + @Override + public void run(InAppPurchaseHelper helper) { + try { + SkuDetails skuDetails = getSkuDetails(getDepthContours().getSku()); + if (skuDetails == null) { + throw new IllegalArgumentException("Cannot find sku details"); + } + BillingManager billingManager = getBillingManager(); + if (billingManager != null) { + billingManager.initiatePurchaseFlow(activity, skuDetails); + } else { + throw new IllegalStateException("BillingManager disposed"); + } + commandDone(); + } catch (Exception e) { + complain("Cannot launch depth contours purchase!"); + logError("purchaseDepthContours Error", e); + stop(true); + } + } + }); + } + + @Override + public void purchaseContourLines(@NonNull Activity activity) throws UnsupportedOperationException { + OsmandPlugin plugin = OsmandPlugin.getPlugin(SRTMPlugin.class); + if(plugin == null || plugin.getInstallURL() == null) { + Toast.makeText(activity.getApplicationContext(), + activity.getString(R.string.activate_srtm_plugin), Toast.LENGTH_LONG).show(); + } else { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(plugin.getInstallURL()))); + } + } + + @Override + public void manageSubscription(@NonNull Context ctx, @Nullable String sku) { + String url = "https://play.google.com/store/account/subscriptions?package=" + ctx.getPackageName(); + if (!Algorithms.isEmpty(sku)) { + url += "&sku=" + sku; + } + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + ctx.startActivity(intent); + } + + @Nullable + private SkuDetails getSkuDetails(@NonNull String sku) { + List skuDetailsList = this.skuDetailsList; + if (skuDetailsList != null) { + for (SkuDetails details : skuDetailsList) { + if (details.getSku().equals(sku)) { + return details; + } + } + } + return null; + } + + private boolean hasDetails(@NonNull String sku) { + return getSkuDetails(sku) != null; + } + + @Nullable + private Purchase getPurchase(@NonNull String sku) { + BillingManager billingManager = getBillingManager(); + if (billingManager != null) { + List purchases = billingManager.getPurchases(); + if (purchases != null) { + for (Purchase p : purchases) { + if (p.getSku().equals(sku)) { + return p; + } + } + } + } + return null; + } + + // Listener that's called when we finish querying the items and subscriptions we own + private SkuDetailsResponseListener mSkuDetailsResponseListener = new SkuDetailsResponseListener() { + + @NonNull + private List getAllOwnedSubscriptionSkus() { + List result = new ArrayList<>(); + BillingManager billingManager = getBillingManager(); + if (billingManager != null) { + for (Purchase p : billingManager.getPurchases()) { + if (getInAppPurchases().getInAppSubscriptionBySku(p.getSku()) != null) { + result.add(p.getSku()); + } + } + } + return result; + } + + @Override + public void onSkuDetailsResponse(BillingResult billingResult, List skuDetailsList) { + + logDebug("Query sku details finished."); + + // Have we been disposed of in the meantime? If so, quit. + if (getBillingManager() == null) { + stop(true); + return; + } + + // Is it a failure? + if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) { + logError("Failed to query inventory: " + billingResult.getResponseCode()); + notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage()); + stop(true); + return; + } + + logDebug("Query sku details was successful."); + + /* + * Check for items we own. Notice that for each purchase, we check + * the developer payload to see if it's correct! See + * verifyDeveloperPayload(). + */ + + List allOwnedSubscriptionSkus = getAllOwnedSubscriptionSkus(); + for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) { + if (hasDetails(s.getSku())) { + Purchase purchase = getPurchase(s.getSku()); + SkuDetails liveUpdatesDetails = getSkuDetails(s.getSku()); + if (liveUpdatesDetails != null) { + fetchInAppPurchase(s, liveUpdatesDetails, purchase); + } + allOwnedSubscriptionSkus.remove(s.getSku()); + } + } + for (String sku : allOwnedSubscriptionSkus) { + Purchase purchase = getPurchase(sku); + SkuDetails liveUpdatesDetails = getSkuDetails(sku); + if (liveUpdatesDetails != null) { + InAppSubscription s = getLiveUpdates().upgradeSubscription(sku); + if (s == null) { + s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesDetails); + } + fetchInAppPurchase(s, liveUpdatesDetails, purchase); + } + } + + InAppPurchase fullVersion = getFullVersion(); + if (hasDetails(fullVersion.getSku())) { + Purchase purchase = getPurchase(fullVersion.getSku()); + SkuDetails fullPriceDetails = getSkuDetails(fullVersion.getSku()); + if (fullPriceDetails != null) { + fetchInAppPurchase(fullVersion, fullPriceDetails, purchase); + } + } + + InAppPurchase depthContours = getDepthContours(); + if (hasDetails(depthContours.getSku())) { + Purchase purchase = getPurchase(depthContours.getSku()); + SkuDetails depthContoursDetails = getSkuDetails(depthContours.getSku()); + if (depthContoursDetails != null) { + fetchInAppPurchase(depthContours, depthContoursDetails, purchase); + } + } + + InAppPurchase contourLines = getContourLines(); + if (hasDetails(contourLines.getSku())) { + Purchase purchase = getPurchase(contourLines.getSku()); + SkuDetails contourLinesDetails = getSkuDetails(contourLines.getSku()); + if (contourLinesDetails != null) { + fetchInAppPurchase(contourLines, contourLinesDetails, purchase); + } + } + + Purchase fullVersionPurchase = getPurchase(fullVersion.getSku()); + boolean fullVersionPurchased = fullVersionPurchase != null; + if (fullVersionPurchased) { + ctx.getSettings().FULL_VERSION_PURCHASED.set(true); + } + + Purchase depthContoursPurchase = getPurchase(depthContours.getSku()); + boolean depthContoursPurchased = depthContoursPurchase != null; + if (depthContoursPurchased) { + ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true); + } + + // Do we have the live updates? + boolean subscribedToLiveUpdates = false; + List liveUpdatesPurchases = new ArrayList<>(); + for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) { + Purchase purchase = getPurchase(p.getSku()); + if (purchase != null) { + liveUpdatesPurchases.add(purchase); + if (!subscribedToLiveUpdates) { + subscribedToLiveUpdates = true; + } + } + } + OsmandPreference subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME; + if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) { + if (subscriptionCancelledTime.get() == 0) { + subscriptionCancelledTime.set(System.currentTimeMillis()); + ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.set(false); + ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false); + } else if (System.currentTimeMillis() - subscriptionCancelledTime.get() > SUBSCRIPTION_HOLDING_TIME_MSEC) { + ctx.getSettings().LIVE_UPDATES_PURCHASED.set(false); + if (!isDepthContoursPurchased(ctx)) { + ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(false); + } + } + } else if (subscribedToLiveUpdates) { + subscriptionCancelledTime.set(0L); + ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true); + } + + lastValidationCheckTime = System.currentTimeMillis(); + logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE") + + " live updates purchased."); + + OsmandSettings settings = ctx.getSettings(); + settings.INAPPS_READ.set(true); + + List tokensToSend = new ArrayList<>(); + if (liveUpdatesPurchases.size() > 0) { + List tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";")); + for (Purchase purchase : liveUpdatesPurchases) { + if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get())) + && !Algorithms.isEmpty(purchase.getDeveloperPayload())) { + String payload = purchase.getDeveloperPayload(); + if (!Algorithms.isEmpty(payload)) { + String[] arr = payload.split(" "); + if (arr.length > 0) { + settings.BILLING_USER_ID.set(arr[0]); + } + if (arr.length > 1) { + token = arr[1]; + settings.BILLING_USER_TOKEN.set(token); + } + } + } + if (!tokensSent.contains(purchase.getSku())) { + tokensToSend.add(purchase); + } + } + } + List purchaseInfoList = new ArrayList<>(); + for (Purchase purchase : tokensToSend) { + purchaseInfoList.add(getPurchaseInfo(purchase)); + } + onSkuDetailsResponseDone(purchaseInfoList); + } + }; + + private PurchaseInfo getPurchaseInfo(Purchase purchase) { + return new PurchaseInfo(purchase.getSku(), purchase.getOrderId(), purchase.getPurchaseToken()); + } + + private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull SkuDetails skuDetails, @Nullable Purchase purchase) { + if (purchase != null) { + inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.PURCHASED); + inAppPurchase.setPurchaseTime(purchase.getPurchaseTime()); + } else { + inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.NOT_PURCHASED); + } + inAppPurchase.setPrice(skuDetails.getPrice()); + inAppPurchase.setPriceCurrencyCode(skuDetails.getPriceCurrencyCode()); + if (skuDetails.getPriceAmountMicros() > 0) { + inAppPurchase.setPriceValue(skuDetails.getPriceAmountMicros() / 1000000d); + } + String subscriptionPeriod = skuDetails.getSubscriptionPeriod(); + if (!Algorithms.isEmpty(subscriptionPeriod)) { + if (inAppPurchase instanceof InAppSubscription) { + try { + ((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod); + } catch (ParseException e) { + LOG.error(e); + } + } + } + if (inAppPurchase instanceof InAppSubscription) { + String introductoryPrice = skuDetails.getIntroductoryPrice(); + String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod(); + String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles(); + long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros(); + if (!Algorithms.isEmpty(introductoryPrice)) { + InAppSubscription s = (InAppSubscription) inAppPurchase; + try { + s.setIntroductoryInfo(new InAppPurchases.InAppSubscriptionIntroductoryInfo(s, introductoryPrice, + introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles)); + } catch (ParseException e) { + LOG.error(e); + } + } + } + } + + protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference activity, final String sku, final String payload) { + return new InAppCommand() { + @Override + public void run(InAppPurchaseHelper helper) { + try { + Activity a = activity.get(); + SkuDetails skuDetails = getSkuDetails(sku); + if (AndroidUtils.isActivityNotDestroyed(a) && skuDetails != null) { + BillingManager billingManager = getBillingManager(); + if (billingManager != null) { + billingManager.setPayload(payload); + billingManager.initiatePurchaseFlow(a, skuDetails); + } else { + throw new IllegalStateException("BillingManager disposed"); + } + commandDone(); + } else { + stop(true); + } + } catch (Exception e) { + logError("launchPurchaseFlow Error", e); + stop(true); + } + } + }; + } + + protected InAppCommand getRequestInventoryCommand() { + return new InAppCommand() { + @Override + public void run(InAppPurchaseHelper helper) { + logDebug("Setup successful. Querying inventory."); + try { + BillingManager billingManager = getBillingManager(); + if (billingManager != null) { + billingManager.queryPurchases(); + } else { + throw new IllegalStateException("BillingManager disposed"); + } + commandDone(); + } catch (Exception e) { + logError("queryInventoryAsync Error", e); + notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY); + stop(true); + } + } + }; + } + + // Call when a purchase is finished + private void onPurchaseFinished(Purchase purchase) { + logDebug("Purchase finished: " + purchase); + + // if we were disposed of in the meantime, quit. + if (getBillingManager() == null) { + stop(true); + return; + } + + onPurchaseDone(getPurchaseInfo(purchase)); + } + + @Override + protected boolean isBillingManagerExists() { + return getBillingManager() != null; + } + + @Override + protected void destroyBillingManager() { + BillingManager billingManager = getBillingManager(); + if (billingManager != null) { + billingManager.destroy(); + this.billingManager = null; + } + } +} diff --git a/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java new file mode 100644 index 0000000000..a2a0f8d680 --- /dev/null +++ b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java @@ -0,0 +1,323 @@ +package net.osmand.plus.inapp; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.billingclient.api.SkuDetails; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.Version; + +public class InAppPurchasesImpl extends InAppPurchases { + + private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion(); + private static final InAppPurchaseDepthContoursFull DEPTH_CONTOURS_FULL = new InAppPurchaseDepthContoursFull(); + private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree(); + private static final InAppPurchaseContourLinesFull CONTOUR_LINES_FULL = new InAppPurchaseContourLinesFull(); + private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree(); + + private static final InAppSubscription[] LIVE_UPDATES_FULL = new InAppSubscription[]{ + new InAppPurchaseLiveUpdatesOldMonthlyFull(), + new InAppPurchaseLiveUpdatesMonthlyFull(), + new InAppPurchaseLiveUpdates3MonthsFull(), + new InAppPurchaseLiveUpdatesAnnualFull() + }; + + private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{ + new InAppPurchaseLiveUpdatesOldMonthlyFree(), + new InAppPurchaseLiveUpdatesMonthlyFree(), + new InAppPurchaseLiveUpdates3MonthsFree(), + new InAppPurchaseLiveUpdatesAnnualFree() + }; + + public InAppPurchasesImpl(OsmandApplication ctx) { + super(ctx); + fullVersion = FULL_VERSION; + if (Version.isFreeVersion(ctx)) { + liveUpdates = new LiveUpdatesInAppPurchasesFree(); + } else { + liveUpdates = new LiveUpdatesInAppPurchasesFull(); + } + for (InAppSubscription s : liveUpdates.getAllSubscriptions()) { + if (s instanceof InAppPurchaseLiveUpdatesMonthly) { + if (s.isDiscounted()) { + discountedMonthlyLiveUpdates = s; + } else { + monthlyLiveUpdates = s; + } + } + } + if (Version.isFreeVersion(ctx)) { + depthContours = DEPTH_CONTOURS_FREE; + } else { + depthContours = DEPTH_CONTOURS_FULL; + } + if (Version.isFreeVersion(ctx)) { + contourLines = CONTOUR_LINES_FREE; + } else { + contourLines = CONTOUR_LINES_FULL; + } + + inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines }; + } + + @Override + public boolean isFullVersion(String sku) { + return FULL_VERSION.getSku().equals(sku); + } + + @Override + public boolean isDepthContours(String sku) { + return DEPTH_CONTOURS_FULL.getSku().equals(sku) || DEPTH_CONTOURS_FREE.getSku().equals(sku); + } + + @Override + public boolean isContourLines(String sku) { + return CONTOUR_LINES_FULL.getSku().equals(sku) || CONTOUR_LINES_FREE.getSku().equals(sku); + } + + @Override + public boolean isLiveUpdates(String sku) { + for (InAppPurchase p : LIVE_UPDATES_FULL) { + if (p.getSku().equals(sku)) { + return true; + } + } + for (InAppPurchase p : LIVE_UPDATES_FREE) { + if (p.getSku().equals(sku)) { + return true; + } + } + return false; + } + + private static class InAppPurchaseFullVersion extends InAppPurchase { + + private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price"; + + InAppPurchaseFullVersion() { + super(SKU_FULL_VERSION_PRICE); + } + + @Override + public String getDefaultPrice(Context ctx) { + return ctx.getString(R.string.full_version_price); + } + } + + private static class InAppPurchaseDepthContoursFull extends InAppPurchaseDepthContours { + + private static final String SKU_DEPTH_CONTOURS_FULL = "net.osmand.seadepth_plus"; + + InAppPurchaseDepthContoursFull() { + super(SKU_DEPTH_CONTOURS_FULL); + } + } + + private static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours { + + private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth"; + + InAppPurchaseDepthContoursFree() { + super(SKU_DEPTH_CONTOURS_FREE); + } + } + + private static class InAppPurchaseContourLinesFull extends InAppPurchaseContourLines { + + private static final String SKU_CONTOUR_LINES_FULL = "net.osmand.contourlines_plus"; + + InAppPurchaseContourLinesFull() { + super(SKU_CONTOUR_LINES_FULL); + } + } + + private static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines { + + private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines"; + + InAppPurchaseContourLinesFree() { + super(SKU_CONTOUR_LINES_FREE); + } + } + + private static class InAppPurchaseLiveUpdatesMonthlyFull extends InAppPurchaseLiveUpdatesMonthly { + + private static final String SKU_LIVE_UPDATES_MONTHLY_FULL = "osm_live_subscription_monthly_full"; + + InAppPurchaseLiveUpdatesMonthlyFull() { + super(SKU_LIVE_UPDATES_MONTHLY_FULL, 1); + } + + private InAppPurchaseLiveUpdatesMonthlyFull(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFull(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly { + + private static final String SKU_LIVE_UPDATES_MONTHLY_FREE = "osm_live_subscription_monthly_free"; + + InAppPurchaseLiveUpdatesMonthlyFree() { + super(SKU_LIVE_UPDATES_MONTHLY_FREE, 1); + } + + private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdates3MonthsFull extends InAppPurchaseLiveUpdates3Months { + + private static final String SKU_LIVE_UPDATES_3_MONTHS_FULL = "osm_live_subscription_3_months_full"; + + InAppPurchaseLiveUpdates3MonthsFull() { + super(SKU_LIVE_UPDATES_3_MONTHS_FULL, 1); + } + + private InAppPurchaseLiveUpdates3MonthsFull(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFull(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months { + + private static final String SKU_LIVE_UPDATES_3_MONTHS_FREE = "osm_live_subscription_3_months_free"; + + InAppPurchaseLiveUpdates3MonthsFree() { + super(SKU_LIVE_UPDATES_3_MONTHS_FREE, 1); + } + + private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdatesAnnualFull extends InAppPurchaseLiveUpdatesAnnual { + + private static final String SKU_LIVE_UPDATES_ANNUAL_FULL = "osm_live_subscription_annual_full"; + + InAppPurchaseLiveUpdatesAnnualFull() { + super(SKU_LIVE_UPDATES_ANNUAL_FULL, 1); + } + + private InAppPurchaseLiveUpdatesAnnualFull(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFull(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual { + + private static final String SKU_LIVE_UPDATES_ANNUAL_FREE = "osm_live_subscription_annual_free"; + + InAppPurchaseLiveUpdatesAnnualFree() { + super(SKU_LIVE_UPDATES_ANNUAL_FREE, 1); + } + + private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdatesOldMonthlyFull extends InAppPurchaseLiveUpdatesOldMonthly { + + private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FULL = "osm_live_subscription_2"; + + InAppPurchaseLiveUpdatesOldMonthlyFull() { + super(SKU_LIVE_UPDATES_OLD_MONTHLY_FULL); + } + } + + private static class InAppPurchaseLiveUpdatesOldMonthlyFree extends InAppPurchaseLiveUpdatesOldMonthly { + + private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FREE = "osm_free_live_subscription_2"; + + InAppPurchaseLiveUpdatesOldMonthlyFree() { + super(SKU_LIVE_UPDATES_OLD_MONTHLY_FREE); + } + } + + public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription { + + private SkuDetails details; + + InAppPurchaseLiveUpdatesOldSubscription(@NonNull SkuDetails details) { + super(details.getSku(), true); + this.details = details; + } + + @Override + public String getDefaultPrice(Context ctx) { + return ""; + } + + @Override + public CharSequence getTitle(Context ctx) { + return details.getTitle(); + } + + @Override + public CharSequence getDescription(@NonNull Context ctx) { + return details.getDescription(); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return null; + } + } + + private static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList { + + public LiveUpdatesInAppPurchasesFree() { + super(LIVE_UPDATES_FREE); + } + } + + private static class LiveUpdatesInAppPurchasesFull extends InAppSubscriptionList { + + public LiveUpdatesInAppPurchasesFull() { + super(LIVE_UPDATES_FULL); + } + } +} diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java new file mode 100755 index 0000000000..c7e6cd11be --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java @@ -0,0 +1,96 @@ +/** + * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.osmand.plus.inapp; + +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +/** + * Signature related tools. + * + * @since 2019/12/9 + */ +public class CipherUtil { + private static final String TAG = "CipherUtil"; + private static final String SIGN_ALGORITHMS = "SHA256WithRSA"; + private static final String PUBLIC_KEY = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAsB+oH8rYQncwpTqGa0kS/5E725HJrq2sW1ThAZtmorYVi52Yt9PmZvNDz7284ol9C2skrKQR34eIer8Tr7Qqq3mlNo+/LVUpq9sa++kB2glaG6jj5NNjM3w4nVYHFIYkd5AQhodJgmqFvnp2s7r7YmyQVXZSehei5bA1G70Bs+El9cSv9shNNGTCaU3ARUu2hy3Ltkc/ov7/ZYYpiwjbyD3cmoMh9jO1++zztXb2phjv1h9zeJOp1i6HsotZll+c9J4jjV3GhrF+ZJm5WrSzGLDLtwSldRpMZFxrSvAJJstjzhDz3LpUM+nPV3HZ5VQ/xosmwWYmiibo89E1gw8p73NTBXHzuQMJcTJ6vTjD8LeMskpXHZUAGhifmFLGN1LbNP9662ulCV12kIbXuzWCwwi/h0DWqmnjKmLvzc88e4BrGrp2zZUnHz7m15voPG+4cQ3z9+cwS4gEI3SUTiFyQGE539SO/11VkkQAJ8P7du1JFNqQw5ZEW3AoE1iUsp5XAgMBAAE="; + + /** + * the method to check the signature for the data returned from the interface + * @param content Unsigned data + * @param sign the signature for content + * @param publicKey the public of the application + * @return boolean + */ + public static boolean doCheck(String content, String sign, String publicKey) { + if (TextUtils.isEmpty(publicKey)) { + Log.e(TAG, "publicKey is null"); + return false; + } + + if (TextUtils.isEmpty(content) || TextUtils.isEmpty(sign)) { + Log.e(TAG, "data is error"); + return false; + } + + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + byte[] encodedKey = Base64.decode(publicKey, Base64.DEFAULT); + PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); + + java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS); + + signature.initVerify(pubKey); + signature.update(content.getBytes("UTF-8")); + + boolean bverify = signature.verify(Base64.decode(sign, Base64.DEFAULT)); + return bverify; + + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, "doCheck NoSuchAlgorithmException" + e); + } catch (InvalidKeySpecException e) { + Log.e(TAG, "doCheck InvalidKeySpecException" + e); + } catch (InvalidKeyException e) { + Log.e(TAG, "doCheck InvalidKeyException" + e); + } catch (SignatureException e) { + Log.e(TAG, "doCheck SignatureException" + e); + } catch (UnsupportedEncodingException e) { + Log.e(TAG, "doCheck UnsupportedEncodingException" + e); + } + return false; + } + + /** + * get the publicKey of the application + * During the encoding process, avoid storing the public key in clear text. + * @return publickey + */ + public static String getPublicKey(){ + return PUBLIC_KEY; + } + +} diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java new file mode 100755 index 0000000000..fba4db210a --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java @@ -0,0 +1,33 @@ +/** + * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.osmand.plus.inapp; + +/** + * Constants Class. + * + * @since 2019/12/9 + */ +public class Constants { + + /** requestCode for pull up the pmsPay page */ + public static final int REQ_CODE_BUY_SUB = 4002; + public static final int REQ_CODE_BUY_INAPP = 4003; + + /** requestCode for pull up the login page for isEnvReady interface */ + public static final int REQ_CODE_LOGIN = 2001; + +} diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java new file mode 100755 index 0000000000..585c6b8404 --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java @@ -0,0 +1,103 @@ +/** + * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.osmand.plus.inapp; + +import android.app.Activity; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import com.huawei.hms.iap.IapApiException; +import com.huawei.hms.iap.entity.OrderStatusCode; + +import net.osmand.AndroidUtils; +import net.osmand.PlatformUtil; + +/** + * Handles the exception returned from the iap api. + * + * @since 2019/12/9 + */ +public class ExceptionHandle { + + protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(ExceptionHandle.class); + + /** + * The exception is solved. + */ + public static final int SOLVED = 0; + + /** + * Handles the exception returned from the iap api. + * @param activity The Activity to call the iap api. + * @param e The exception returned from the iap api. + * @return int + */ + public static int handle(@Nullable Activity activity, Exception e) { + + if (e instanceof IapApiException) { + IapApiException iapApiException = (IapApiException) e; + LOG.info("returnCode: " + iapApiException.getStatusCode()); + switch (iapApiException.getStatusCode()) { + case OrderStatusCode.ORDER_STATE_CANCEL: + showToast(activity, "Order has been canceled!"); + return SOLVED; + case OrderStatusCode.ORDER_STATE_PARAM_ERROR: + showToast(activity, "Order state param error!"); + return SOLVED; + case OrderStatusCode.ORDER_STATE_NET_ERROR: + showToast(activity, "Order state net error!"); + return SOLVED; + case OrderStatusCode.ORDER_VR_UNINSTALL_ERROR: + showToast(activity, "Order vr uninstall error!"); + return SOLVED; + case OrderStatusCode.ORDER_HWID_NOT_LOGIN: + IapRequestHelper.startResolutionForResult(activity, iapApiException.getStatus(), Constants.REQ_CODE_LOGIN); + return SOLVED; + case OrderStatusCode.ORDER_PRODUCT_OWNED: + showToast(activity, "Product already owned error!"); + return OrderStatusCode.ORDER_PRODUCT_OWNED; + case OrderStatusCode.ORDER_PRODUCT_NOT_OWNED: + showToast(activity, "Product not owned error!"); + return SOLVED; + case OrderStatusCode.ORDER_PRODUCT_CONSUMED: + showToast(activity, "Product consumed error!"); + return SOLVED; + case OrderStatusCode.ORDER_ACCOUNT_AREA_NOT_SUPPORTED: + showToast(activity, "Order account area not supported error!"); + return SOLVED; + case OrderStatusCode.ORDER_NOT_ACCEPT_AGREEMENT: + showToast(activity, "User does not agree the agreement"); + return SOLVED; + default: + // handle other error scenarios + showToast(activity, "Order unknown error (" + iapApiException.getStatusCode() + ")"); + return SOLVED; + } + } else { + showToast(activity, "External error"); + LOG.error(e.getMessage(), e); + return SOLVED; + } + } + + private static void showToast(@Nullable Activity activity, String s) { + if (AndroidUtils.isActivityNotDestroyed(activity)) { + Toast.makeText(activity, s, Toast.LENGTH_SHORT).show(); + } + } +} \ No newline at end of file diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java new file mode 100755 index 0000000000..d8fb908093 --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java @@ -0,0 +1,37 @@ +/** + * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.osmand.plus.inapp; + +/** + * Used to callback the result from iap api. + * + * @since 2019/12/9 + */ +public interface IapApiCallback { + + /** + * The request is successful. + * @param result The result of a successful response. + */ + void onSuccess(T result); + + /** + * Callback fail. + * @param e An Exception from IAPSDK. + */ + void onFail(Exception e); +} diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java new file mode 100755 index 0000000000..5c1b7838a5 --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java @@ -0,0 +1,351 @@ +/** + * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.osmand.plus.inapp; + +import android.app.Activity; +import android.content.IntentSender; +import android.text.TextUtils; +import android.util.Log; + +import com.huawei.hmf.tasks.OnFailureListener; +import com.huawei.hmf.tasks.OnSuccessListener; +import com.huawei.hmf.tasks.Task; +import com.huawei.hms.iap.Iap; +import com.huawei.hms.iap.IapApiException; +import com.huawei.hms.iap.IapClient; +import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseReq; +import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseResult; +import com.huawei.hms.iap.entity.IsEnvReadyResult; +import com.huawei.hms.iap.entity.OwnedPurchasesReq; +import com.huawei.hms.iap.entity.OwnedPurchasesResult; +import com.huawei.hms.iap.entity.ProductInfoReq; +import com.huawei.hms.iap.entity.ProductInfoResult; +import com.huawei.hms.iap.entity.PurchaseIntentReq; +import com.huawei.hms.iap.entity.PurchaseIntentResult; +import com.huawei.hms.iap.entity.StartIapActivityReq; +import com.huawei.hms.iap.entity.StartIapActivityResult; +import com.huawei.hms.support.api.client.Status; + +import java.util.List; + +/** + * The tool class of Iap interface. + * + * @since 2019/12/9 + */ +public class IapRequestHelper { + private final static String TAG = "IapRequestHelper"; + + /** + * Create a PurchaseIntentReq object. + * @param type In-app product type. + * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription + * @param productId ID of the in-app product to be paid. + * The in-app product ID is the product ID you set during in-app product configuration in AppGallery Connect. + * @return PurchaseIntentReq + */ + private static PurchaseIntentReq createPurchaseIntentReq(int type, String productId) { + PurchaseIntentReq req = new PurchaseIntentReq(); + req.setPriceType(type); + req.setProductId(productId); + req.setDeveloperPayload("testPurchase"); + return req; + } + + /** + * Create a ConsumeOwnedPurchaseReq object. + * @param purchaseToken which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData. + * The app transfers this parameter for the Huawei payment server to update the order status and then deliver the in-app product. + * @return ConsumeOwnedPurchaseReq + */ + private static ConsumeOwnedPurchaseReq createConsumeOwnedPurchaseReq(String purchaseToken) { + ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq(); + req.setPurchaseToken(purchaseToken); + req.setDeveloperChallenge("testConsume"); + return req; + } + + /** + * Create a OwnedPurchasesReq object. + * @param type type In-app product type. + * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription + * @param continuationToken A data location flag which returns from obtainOwnedPurchases api or obtainOwnedPurchaseRecord api. + * @return OwnedPurchasesReq + */ + private static OwnedPurchasesReq createOwnedPurchasesReq(int type, String continuationToken) { + OwnedPurchasesReq req = new OwnedPurchasesReq(); + req.setPriceType(type); + req.setContinuationToken(continuationToken); + return req; + } + + /** + * Create a ProductInfoReq object. + * @param type In-app product type. + * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription + * @param productIds ID list of products to be queried. Each product ID must exist and be unique in the current app. + * @return ProductInfoReq + */ + private static ProductInfoReq createProductInfoReq(int type, List productIds) { + ProductInfoReq req = new ProductInfoReq(); + req.setPriceType(type); + req.setProductIds(productIds); + return req; + } + + /** + * To check whether the country or region of the logged in HUAWEI ID is included in the countries or regions supported by HUAWEI IAP. + * @param mClient IapClient instance to call the isEnvReady API. + * @param callback IapApiCallback. + */ + public static void isEnvReady(IapClient mClient, final IapApiCallback callback) { + Log.i(TAG, "call isEnvReady"); + Task task = mClient.isEnvReady(); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(IsEnvReadyResult result) { + Log.i(TAG, "isEnvReady, success"); + callback.onSuccess(result); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + Log.e(TAG, "isEnvReady, fail"); + callback.onFail(e); + } + }); + } + + /** + * Obtain in-app product details configured in AppGallery Connect. + * @param iapClient IapClient instance to call the obtainProductInfo API. + * @param productIds ID list of products to be queried. Each product ID must exist and be unique in the current app. + * @param type In-app product type. + * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription + * @param callback IapApiCallback + */ + public static void obtainProductInfo(IapClient iapClient, final List productIds, int type, final IapApiCallback callback) { + Log.i(TAG, "call obtainProductInfo"); + + Task task = iapClient.obtainProductInfo(createProductInfoReq(type, productIds)); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(ProductInfoResult result) { + Log.i(TAG, "obtainProductInfo, success"); + callback.onSuccess(result); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + Log.e(TAG, "obtainProductInfo, fail"); + callback.onFail(e); + } + }); + } + + /** + * create orders for in-app products in the PMS + * @param iapClient IapClient instance to call the createPurchaseIntent API. + * @param productId ID of the in-app product to be paid. + * The in-app product ID is the product ID you set during in-app product configuration in AppGallery Connect. + * @param type In-app product type. + * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription + * @param callback IapApiCallback + */ + public static void createPurchaseIntent(final IapClient iapClient, String productId, int type, final IapApiCallback callback) { + Log.i(TAG, "call createPurchaseIntent"); + Task task = iapClient.createPurchaseIntent(createPurchaseIntentReq(type, productId)); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(PurchaseIntentResult result) { + Log.i(TAG, "createPurchaseIntent, success"); + callback.onSuccess(result); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + Log.e(TAG, "createPurchaseIntent, fail"); + callback.onFail(e); + + } + }); + } + + public static void createPurchaseIntent(final IapClient iapClient, String productId, int type, String payload, final IapApiCallback callback) { + Log.i(TAG, "call createPurchaseIntent"); + PurchaseIntentReq req = createPurchaseIntentReq(type, productId); + req.setDeveloperPayload(payload); + Task task = iapClient.createPurchaseIntent(req); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(PurchaseIntentResult result) { + Log.i(TAG, "createPurchaseIntent, success"); + callback.onSuccess(result); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + Log.e(TAG, "createPurchaseIntent, fail"); + callback.onFail(e); + + } + }); + } + + /** + * to start an activity. + * @param activity the activity to launch a new page. + * @param status This parameter contains the pendingIntent object of the payment page. + * @param reqCode Result code. + */ + public static void startResolutionForResult(Activity activity, Status status, int reqCode) { + if (status == null) { + Log.e(TAG, "status is null"); + return; + } + if (status.hasResolution()) { + try { + status.startResolutionForResult(activity, reqCode); + } catch (IntentSender.SendIntentException exp) { + Log.e(TAG, exp.getMessage()); + } + } else { + Log.e(TAG, "intent is null"); + } + } + + /** + * query information about all subscribed in-app products, including consumables, non-consumables, and auto-renewable subscriptions.
+ * If consumables are returned, the system needs to deliver them and calls the consumeOwnedPurchase API to consume the products. + * If non-consumables are returned, the in-app products do not need to be consumed. + * If subscriptions are returned, all existing subscription relationships of the user under the app are returned. + * @param mClient IapClient instance to call the obtainOwnedPurchases API. + * @param type In-app product type. + * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription + * @param callback IapApiCallback + */ + public static void obtainOwnedPurchases(IapClient mClient, final int type, String continuationToken, final IapApiCallback callback) { + Log.i(TAG, "call obtainOwnedPurchases"); + Task task = mClient.obtainOwnedPurchases(IapRequestHelper.createOwnedPurchasesReq(type, continuationToken)); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(OwnedPurchasesResult result) { + Log.i(TAG, "obtainOwnedPurchases, success"); + callback.onSuccess(result); + + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + Log.e(TAG, "obtainOwnedPurchases, fail"); + callback.onFail(e); + } + }); + + } + + /** + * obtain the historical consumption information about a consumable in-app product or all subscription receipts of a subscription. + * @param iapClient IapClient instance to call the obtainOwnedPurchaseRecord API. + * @param priceType In-app product type. + * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription. + * @param continuationToken Data locating flag for supporting query in pagination mode. + * @param callback IapApiCallback + */ + public static void obtainOwnedPurchaseRecord(IapClient iapClient, int priceType, String continuationToken, final IapApiCallback callback) { + Log.i(TAG, "call obtainOwnedPurchaseRecord"); + Task task = iapClient.obtainOwnedPurchaseRecord(createOwnedPurchasesReq(priceType, continuationToken)); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(OwnedPurchasesResult result) { + Log.i(TAG, "obtainOwnedPurchaseRecord, success"); + callback.onSuccess(result); + + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + Log.e(TAG, "obtainOwnedPurchaseRecord, fail"); + callback.onFail(e); + } + }); + } + + /** + * Consume all the unconsumed purchases with priceType 0. + * @param iapClient IapClient instance to call the consumeOwnedPurchase API. + * @param purchaseToken which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData. + */ + public static void consumeOwnedPurchase(IapClient iapClient, String purchaseToken) { + Log.i(TAG, "call consumeOwnedPurchase"); + Task task = iapClient.consumeOwnedPurchase(createConsumeOwnedPurchaseReq(purchaseToken)); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(ConsumeOwnedPurchaseResult result) { + // Consume success. + Log.i(TAG, "consumeOwnedPurchase success"); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + if (e instanceof IapApiException) { + IapApiException apiException = (IapApiException)e; + int returnCode = apiException.getStatusCode(); + Log.e(TAG, "consumeOwnedPurchase fail, IapApiException returnCode: " + returnCode); + } else { + // Other external errors + Log.e(TAG, e.getMessage()); + } + + } + }); + + } + + /** + * link to subscription manager page + * @param activity activity + * @param productId the productId of the subscription product + */ + public static void showSubscription(final Activity activity, String productId) { + StartIapActivityReq req = new StartIapActivityReq(); + if (TextUtils.isEmpty(productId)) { + req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_MANAGER_ACTIVITY); + } else { + req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_EDIT_ACTIVITY); + req.setSubscribeProductId(productId); + } + + IapClient iapClient = Iap.getIapClient(activity); + Task task = iapClient.startIapActivity(req); + + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(StartIapActivityResult result) { + if(result != null) { + result.startActivity(activity); + } + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + ExceptionHandle.handle(activity, e); + } + }); + } + +} diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java new file mode 100644 index 0000000000..0f3536e5d0 --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java @@ -0,0 +1,703 @@ +package net.osmand.plus.inapp; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.huawei.hmf.tasks.OnFailureListener; +import com.huawei.hmf.tasks.OnSuccessListener; +import com.huawei.hmf.tasks.Task; +import com.huawei.hms.iap.Iap; +import com.huawei.hms.iap.IapClient; +import com.huawei.hms.iap.entity.InAppPurchaseData; +import com.huawei.hms.iap.entity.IsEnvReadyResult; +import com.huawei.hms.iap.entity.OrderStatusCode; +import com.huawei.hms.iap.entity.OwnedPurchasesResult; +import com.huawei.hms.iap.entity.ProductInfo; +import com.huawei.hms.iap.entity.ProductInfoResult; +import com.huawei.hms.iap.entity.PurchaseIntentResult; +import com.huawei.hms.iap.entity.PurchaseResultInfo; +import com.huawei.hms.iap.entity.StartIapActivityReq; +import com.huawei.hms.iap.entity.StartIapActivityResult; + +import net.osmand.AndroidUtils; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.inapp.InAppPurchases.InAppPurchase; +import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; +import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionIntroductoryInfo; +import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.util.Algorithms; + +import java.lang.ref.WeakReference; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class InAppPurchaseHelperImpl extends InAppPurchaseHelper { + + private boolean envReady = false; + private boolean purchaseSupported = false; + + private List productInfos; + private OwnedPurchasesResult ownedSubscriptions; + private List ownedInApps = new ArrayList<>(); + + public InAppPurchaseHelperImpl(OsmandApplication ctx) { + super(ctx); + purchases = new InAppPurchasesImpl(ctx); + } + + @Override + public void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback) { + if (envReady) { + if (callback != null) { + if (purchaseSupported) { + callback.onSuccess(); + } else { + callback.onFail(); + } + } + } else { + // Initiating an isEnvReady request when entering the app. + // Check if the account service country supports IAP. + IapClient mClient = Iap.getIapClient(activity); + final WeakReference activityRef = new WeakReference<>(activity); + IapRequestHelper.isEnvReady(mClient, new IapApiCallback() { + + private void onReady(boolean succeed) { + logDebug("Setup finished."); + envReady = true; + purchaseSupported = succeed; + if (callback != null) { + if (succeed) { + callback.onSuccess(); + } else { + callback.onFail(); + } + } + } + + @Override + public void onSuccess(IsEnvReadyResult result) { + onReady(true); + } + + @Override + public void onFail(Exception e) { + onReady(false); + LOG.error("isEnvReady fail, " + e.getMessage(), e); + ExceptionHandle.handle(activityRef.get(), e); + } + }); + } + } + + protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand command) { + if (envReady) { + command.run(this); + } else { + command.commandDone(); + } + } + + private InAppCommand getPurchaseInAppCommand(@NonNull final Activity activity, @NonNull final String productId) throws UnsupportedOperationException { + return new InAppCommand() { + @Override + public void run(InAppPurchaseHelper helper) { + try { + ProductInfo productInfo = getProductInfo(productId); + if (productInfo != null) { + IapRequestHelper.createPurchaseIntent(getIapClient(), productInfo.getProductId(), + IapClient.PriceType.IN_APP_NONCONSUMABLE, new IapApiCallback() { + @Override + public void onSuccess(PurchaseIntentResult result) { + if (result == null) { + logError("result is null"); + } else { + // you should pull up the page to complete the payment process + IapRequestHelper.startResolutionForResult(activity, result.getStatus(), Constants.REQ_CODE_BUY_INAPP); + } + commandDone(); + } + + @Override + public void onFail(Exception e) { + int errorCode = ExceptionHandle.handle(activity, e); + if (errorCode != ExceptionHandle.SOLVED) { + logDebug("createPurchaseIntent, returnCode: " + errorCode); + if (OrderStatusCode.ORDER_PRODUCT_OWNED == errorCode) { + logError("already own this product"); + } else { + logError("unknown error"); + } + } + commandDone(); + } + }); + } else { + commandDone(); + } + } catch (Exception e) { + complain("Cannot launch full version purchase!"); + logError("purchaseFullVersion Error", e); + stop(true); + } + } + }; + } + + @Override + public void purchaseFullVersion(@NonNull final Activity activity) throws UnsupportedOperationException { + notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION); + exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, getPurchaseInAppCommand(activity, purchases.getFullVersion().getSku())); + } + + @Override + public void purchaseDepthContours(@NonNull final Activity activity) throws UnsupportedOperationException { + notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS); + exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, getPurchaseInAppCommand(activity, purchases.getDepthContours().getSku())); + } + + @Override + public void purchaseContourLines(@NonNull Activity activity) throws UnsupportedOperationException { + notifyShowProgress(InAppPurchaseTaskType.PURCHASE_CONTOUR_LINES); + exec(InAppPurchaseTaskType.PURCHASE_CONTOUR_LINES, getPurchaseInAppCommand(activity, purchases.getContourLines().getSku())); + } + + @Override + public void manageSubscription(@NonNull Context ctx, @Nullable String sku) { + if (uiActivity != null) { + StartIapActivityReq req = new StartIapActivityReq(); + if (!Algorithms.isEmpty(sku)) { + req.setSubscribeProductId(sku); + req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_EDIT_ACTIVITY); + } else { + req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_MANAGER_ACTIVITY); + } + Task task = getIapClient().startIapActivity(req); + task.addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(StartIapActivityResult result) { + logDebug("startIapActivity: onSuccess"); + Activity activity = (Activity) uiActivity; + if (result != null && AndroidUtils.isActivityNotDestroyed(activity)) { + result.startActivity(activity); + } + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(Exception e) { + logDebug("startIapActivity: onFailure"); + } + }); + } + } + + @Nullable + private ProductInfo getProductInfo(@NonNull String productId) { + List productInfos = this.productInfos; + if (productInfos != null) { + for (ProductInfo info : productInfos) { + if (info.getProductId().equals(productId)) { + return info; + } + } + } + return null; + } + + private boolean hasDetails(@NonNull String productId) { + return getProductInfo(productId) != null; + } + + @Nullable + private InAppPurchaseData getPurchaseData(@NonNull String productId) { + InAppPurchaseData data = SubscriptionUtils.getPurchaseData(ownedSubscriptions, productId); + if (data == null) { + for (OwnedPurchasesResult result : ownedInApps) { + data = InAppUtils.getPurchaseData(result, productId); + if (data != null) { + break; + } + } + } + return data; + } + + private PurchaseInfo getPurchaseInfo(InAppPurchaseData purchase) { + return new PurchaseInfo(purchase.getProductId(), purchase.getOrderID(), purchase.getPurchaseToken()); + } + + private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull ProductInfo productInfo, @Nullable InAppPurchaseData purchaseData) { + if (purchaseData != null) { + inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.PURCHASED); + inAppPurchase.setPurchaseTime(purchaseData.getPurchaseTime()); + } else { + inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.NOT_PURCHASED); + } + inAppPurchase.setPrice(productInfo.getPrice()); + inAppPurchase.setPriceCurrencyCode(productInfo.getCurrency()); + if (productInfo.getMicrosPrice() > 0) { + inAppPurchase.setPriceValue(productInfo.getMicrosPrice() / 1000000d); + } + String subscriptionPeriod = productInfo.getSubPeriod(); + if (!Algorithms.isEmpty(subscriptionPeriod)) { + if (inAppPurchase instanceof InAppSubscription) { + try { + ((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod); + } catch (ParseException e) { + LOG.error(e); + } + } + } + if (inAppPurchase instanceof InAppSubscription) { + String introductoryPrice = productInfo.getSubSpecialPrice(); + String introductoryPricePeriod = productInfo.getSubPeriod(); + int introductoryPriceCycles = productInfo.getSubSpecialPeriodCycles(); + long introductoryPriceAmountMicros = productInfo.getSubSpecialPriceMicros(); + if (!Algorithms.isEmpty(introductoryPrice)) { + InAppSubscription s = (InAppSubscription) inAppPurchase; + try { + s.setIntroductoryInfo(new InAppSubscriptionIntroductoryInfo(s, introductoryPrice, + introductoryPriceAmountMicros, introductoryPricePeriod, String.valueOf(introductoryPriceCycles))); + } catch (ParseException e) { + LOG.error(e); + } + } + } + } + + protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference activity, final String sku, final String payload) { + return new InAppCommand() { + @Override + public void run(InAppPurchaseHelper helper) { + try { + Activity a = activity.get(); + ProductInfo productInfo = getProductInfo(sku); + if (AndroidUtils.isActivityNotDestroyed(a) && productInfo != null) { + IapRequestHelper.createPurchaseIntent(getIapClient(), sku, + IapClient.PriceType.IN_APP_SUBSCRIPTION, payload, new IapApiCallback() { + @Override + public void onSuccess(PurchaseIntentResult result) { + if (result == null) { + logError("GetBuyIntentResult is null"); + } else { + Activity a = activity.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + IapRequestHelper.startResolutionForResult(a, result.getStatus(), Constants.REQ_CODE_BUY_SUB); + } else { + logError("startResolutionForResult on destroyed activity"); + } + } + commandDone(); + } + + @Override + public void onFail(Exception e) { + int errorCode = ExceptionHandle.handle(activity.get(), e); + if (ExceptionHandle.SOLVED != errorCode) { + logError("createPurchaseIntent, returnCode: " + errorCode); + if (OrderStatusCode.ORDER_PRODUCT_OWNED == errorCode) { + logError("already own this product"); + } else { + logError("unknown error"); + } + } + commandDone(); + } + }); + } else { + stop(true); + } + } catch (Exception e) { + logError("launchPurchaseFlow Error", e); + stop(true); + } + } + }; + } + + @Override + protected InAppCommand getRequestInventoryCommand() { + return new InAppCommand() { + + @Override + protected void commandDone() { + super.commandDone(); + inventoryRequested = false; + } + + @Override + public void run(InAppPurchaseHelper helper) { + logDebug("Setup successful. Querying inventory."); + try { + productInfos = new ArrayList<>(); + obtainOwnedSubscriptions(); + } catch (Exception e) { + logError("queryInventoryAsync Error", e); + notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY); + stop(true); + commandDone(); + } + } + + private void obtainOwnedSubscriptions() { + if (uiActivity != null) { + IapRequestHelper.obtainOwnedPurchases(getIapClient(), IapClient.PriceType.IN_APP_SUBSCRIPTION, + null, new IapApiCallback() { + @Override + public void onSuccess(OwnedPurchasesResult result) { + ownedSubscriptions = result; + obtainOwnedInApps(null); + } + + @Override + public void onFail(Exception e) { + logError("obtainOwnedSubscriptions exception", e); + ExceptionHandle.handle((Activity) uiActivity, e); + commandDone(); + } + }); + } else { + commandDone(); + } + } + + private void obtainOwnedInApps(final String continuationToken) { + if (uiActivity != null) { + // Query users' purchased non-consumable products. + IapRequestHelper.obtainOwnedPurchases(getIapClient(), IapClient.PriceType.IN_APP_NONCONSUMABLE, + continuationToken, new IapApiCallback() { + @Override + public void onSuccess(OwnedPurchasesResult result) { + ownedInApps.add(result); + if (result != null && !TextUtils.isEmpty(result.getContinuationToken())) { + obtainOwnedInApps(result.getContinuationToken()); + } else { + obtainSubscriptionsInfo(); + } + } + + @Override + public void onFail(Exception e) { + logError("obtainOwnedInApps exception", e); + ExceptionHandle.handle((Activity) uiActivity, e); + commandDone(); + } + }); + } else { + commandDone(); + } + } + + private void obtainSubscriptionsInfo() { + if (uiActivity != null) { + Set productIds = new HashSet<>(); + List subscriptions = purchases.getLiveUpdates().getAllSubscriptions(); + for (InAppSubscription s : subscriptions) { + productIds.add(s.getSku()); + } + productIds.addAll(ownedSubscriptions.getItemList()); + IapRequestHelper.obtainProductInfo(getIapClient(), new ArrayList<>(productIds), + IapClient.PriceType.IN_APP_SUBSCRIPTION, new IapApiCallback() { + @Override + public void onSuccess(final ProductInfoResult result) { + if (result != null && result.getProductInfoList() != null) { + productInfos.addAll(result.getProductInfoList()); + } + obtainInAppsInfo(); + } + + @Override + public void onFail(Exception e) { + int errorCode = ExceptionHandle.handle((Activity) uiActivity, e); + if (ExceptionHandle.SOLVED != errorCode) { + LOG.error("Unknown error"); + } + commandDone(); + } + }); + } else { + commandDone(); + } + } + + private void obtainInAppsInfo() { + if (uiActivity != null) { + Set productIds = new HashSet<>(); + for (InAppPurchase purchase : getInAppPurchases().getAllInAppPurchases(false)) { + productIds.add(purchase.getSku()); + } + for (OwnedPurchasesResult result : ownedInApps) { + productIds.addAll(result.getItemList()); + } + IapRequestHelper.obtainProductInfo(getIapClient(), new ArrayList<>(productIds), + IapClient.PriceType.IN_APP_NONCONSUMABLE, new IapApiCallback() { + @Override + public void onSuccess(ProductInfoResult result) { + if (result != null && result.getProductInfoList() != null) { + productInfos.addAll(result.getProductInfoList()); + } + processInventory(); + } + + @Override + public void onFail(Exception e) { + int errorCode = ExceptionHandle.handle((Activity) uiActivity, e); + if (ExceptionHandle.SOLVED != errorCode) { + LOG.error("Unknown error"); + } + commandDone(); + } + }); + } else { + commandDone(); + } + } + + private void processInventory() { + logDebug("Query sku details was successful."); + + /* + * Check for items we own. Notice that for each purchase, we check + * the developer payload to see if it's correct! + */ + + List allOwnedSubscriptionSkus = ownedSubscriptions.getItemList(); + for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) { + if (hasDetails(s.getSku())) { + InAppPurchaseData purchaseData = getPurchaseData(s.getSku()); + ProductInfo liveUpdatesInfo = getProductInfo(s.getSku()); + if (liveUpdatesInfo != null) { + fetchInAppPurchase(s, liveUpdatesInfo, purchaseData); + } + allOwnedSubscriptionSkus.remove(s.getSku()); + } + } + for (String sku : allOwnedSubscriptionSkus) { + InAppPurchaseData purchaseData = getPurchaseData(sku); + ProductInfo liveUpdatesInfo = getProductInfo(sku); + if (liveUpdatesInfo != null) { + InAppSubscription s = getLiveUpdates().upgradeSubscription(sku); + if (s == null) { + s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesInfo); + } + fetchInAppPurchase(s, liveUpdatesInfo, purchaseData); + } + } + + InAppPurchase fullVersion = getFullVersion(); + if (hasDetails(fullVersion.getSku())) { + InAppPurchaseData purchaseData = getPurchaseData(fullVersion.getSku()); + ProductInfo fullPriceDetails = getProductInfo(fullVersion.getSku()); + if (fullPriceDetails != null) { + fetchInAppPurchase(fullVersion, fullPriceDetails, purchaseData); + } + } + InAppPurchase depthContours = getDepthContours(); + if (hasDetails(depthContours.getSku())) { + InAppPurchaseData purchaseData = getPurchaseData(depthContours.getSku()); + ProductInfo depthContoursDetails = getProductInfo(depthContours.getSku()); + if (depthContoursDetails != null) { + fetchInAppPurchase(depthContours, depthContoursDetails, purchaseData); + } + } + InAppPurchase contourLines = getContourLines(); + if (hasDetails(contourLines.getSku())) { + InAppPurchaseData purchaseData = getPurchaseData(contourLines.getSku()); + ProductInfo contourLinesDetails = getProductInfo(contourLines.getSku()); + if (contourLinesDetails != null) { + fetchInAppPurchase(contourLines, contourLinesDetails, purchaseData); + } + } + + if (getPurchaseData(fullVersion.getSku()) != null) { + ctx.getSettings().FULL_VERSION_PURCHASED.set(true); + } + if (getPurchaseData(depthContours.getSku()) != null) { + ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true); + } + if (getPurchaseData(contourLines.getSku()) != null) { + ctx.getSettings().CONTOUR_LINES_PURCHASED.set(true); + } + + // Do we have the live updates? + boolean subscribedToLiveUpdates = false; + List liveUpdatesPurchases = new ArrayList<>(); + for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) { + InAppPurchaseData purchaseData = getPurchaseData(p.getSku()); + if (purchaseData != null) { + liveUpdatesPurchases.add(purchaseData); + if (!subscribedToLiveUpdates) { + subscribedToLiveUpdates = true; + } + } + } + OsmandPreference subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME; + if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) { + if (subscriptionCancelledTime.get() == 0) { + subscriptionCancelledTime.set(System.currentTimeMillis()); + ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.set(false); + ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false); + } else if (System.currentTimeMillis() - subscriptionCancelledTime.get() > SUBSCRIPTION_HOLDING_TIME_MSEC) { + ctx.getSettings().LIVE_UPDATES_PURCHASED.set(false); + if (!isDepthContoursPurchased(ctx)) { + ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(false); + } + } + } else if (subscribedToLiveUpdates) { + subscriptionCancelledTime.set(0L); + ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true); + } + + lastValidationCheckTime = System.currentTimeMillis(); + logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE") + + " live updates purchased."); + + OsmandSettings settings = ctx.getSettings(); + settings.INAPPS_READ.set(true); + + List tokensToSend = new ArrayList<>(); + if (liveUpdatesPurchases.size() > 0) { + List tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";")); + for (InAppPurchaseData purchase : liveUpdatesPurchases) { + if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get())) + && !Algorithms.isEmpty(purchase.getDeveloperPayload())) { + String payload = purchase.getDeveloperPayload(); + if (!Algorithms.isEmpty(payload)) { + String[] arr = payload.split(" "); + if (arr.length > 0) { + settings.BILLING_USER_ID.set(arr[0]); + } + if (arr.length > 1) { + token = arr[1]; + settings.BILLING_USER_TOKEN.set(token); + } + } + } + if (!tokensSent.contains(purchase.getProductId())) { + tokensToSend.add(purchase); + } + } + } + List purchaseInfoList = new ArrayList<>(); + for (InAppPurchaseData purchase : tokensToSend) { + purchaseInfoList.add(getPurchaseInfo(purchase)); + } + onSkuDetailsResponseDone(purchaseInfoList); + } + }; + } + + private IapClient getIapClient() { + return Iap.getIapClient((Activity) uiActivity); + } + + // Call when a purchase is finished + private void onPurchaseFinished(InAppPurchaseData purchase) { + logDebug("Purchase finished: " + purchase.getProductId()); + onPurchaseDone(getPurchaseInfo(purchase)); + } + + @Override + protected boolean isBillingManagerExists() { + return false; + } + + @Override + protected void destroyBillingManager() { + // non implemented + } + + @Override + public boolean onActivityResult(@NonNull Activity activity, int requestCode, int resultCode, Intent data) { + if (requestCode == Constants.REQ_CODE_BUY_SUB) { + boolean succeed = false; + if (resultCode == Activity.RESULT_OK) { + PurchaseResultInfo result = SubscriptionUtils.getPurchaseResult(activity, data); + if (result != null) { + switch (result.getReturnCode()) { + case OrderStatusCode.ORDER_STATE_CANCEL: + logDebug("Purchase cancelled"); + break; + case OrderStatusCode.ORDER_STATE_FAILED: + inventoryRequestPending = true; + logDebug("Purchase failed"); + break; + case OrderStatusCode.ORDER_PRODUCT_OWNED: + inventoryRequestPending = true; + logDebug("Product already owned"); + break; + case OrderStatusCode.ORDER_STATE_SUCCESS: + inventoryRequestPending = true; + InAppPurchaseData purchaseData = SubscriptionUtils.getInAppPurchaseData(null, + result.getInAppPurchaseData(), result.getInAppDataSignature()); + if (purchaseData != null) { + onPurchaseFinished(purchaseData); + succeed = true; + } else { + logDebug("Purchase failed"); + } + break; + default: + break; + } + } else { + logDebug("Purchase failed"); + } + } else { + logDebug("Purchase cancelled"); + } + if (!succeed) { + stop(true); + } + return true; + } else if (requestCode == Constants.REQ_CODE_BUY_INAPP) { + boolean succeed = false; + if (data == null) { + logDebug("data is null"); + } else { + PurchaseResultInfo buyResultInfo = Iap.getIapClient(activity).parsePurchaseResultInfoFromIntent(data); + switch (buyResultInfo.getReturnCode()) { + case OrderStatusCode.ORDER_STATE_CANCEL: + logDebug("Order has been canceled"); + break; + case OrderStatusCode.ORDER_STATE_FAILED: + inventoryRequestPending = true; + logDebug("Order has been failed"); + break; + case OrderStatusCode.ORDER_PRODUCT_OWNED: + inventoryRequestPending = true; + logDebug("Product already owned"); + break; + case OrderStatusCode.ORDER_STATE_SUCCESS: + InAppPurchaseData purchaseData = InAppUtils.getInAppPurchaseData(null, + buyResultInfo.getInAppPurchaseData(), buyResultInfo.getInAppDataSignature()); + if (purchaseData != null) { + onPurchaseFinished(purchaseData); + succeed = true; + } else { + logDebug("Purchase failed"); + } + break; + default: + break; + } + } + if (!succeed) { + stop(true); + } + return true; + } + return false; + } +} diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java new file mode 100644 index 0000000000..4ed0021b6f --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java @@ -0,0 +1,196 @@ +package net.osmand.plus.inapp; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.huawei.hms.iap.entity.ProductInfo; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; + +public class InAppPurchasesImpl extends InAppPurchases { + + private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion(); + private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree(); + private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree(); + + + private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{ + new InAppPurchaseLiveUpdatesMonthlyFree(), + new InAppPurchaseLiveUpdates3MonthsFree(), + new InAppPurchaseLiveUpdatesAnnualFree() + }; + + public InAppPurchasesImpl(OsmandApplication ctx) { + super(ctx); + fullVersion = FULL_VERSION; + depthContours = DEPTH_CONTOURS_FREE; + contourLines = CONTOUR_LINES_FREE; + inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines }; + + liveUpdates = new LiveUpdatesInAppPurchasesFree(); + for (InAppSubscription s : liveUpdates.getAllSubscriptions()) { + if (s instanceof InAppPurchaseLiveUpdatesMonthly) { + if (s.isDiscounted()) { + discountedMonthlyLiveUpdates = s; + } else { + monthlyLiveUpdates = s; + } + } + } + } + + @Override + public boolean isFullVersion(String sku) { + return FULL_VERSION.getSku().equals(sku); + } + + @Override + public boolean isDepthContours(String sku) { + return DEPTH_CONTOURS_FREE.getSku().equals(sku); + } + + @Override + public boolean isContourLines(String sku) { + return CONTOUR_LINES_FREE.getSku().equals(sku); + } + + @Override + public boolean isLiveUpdates(String sku) { + for (InAppPurchase p : LIVE_UPDATES_FREE) { + if (p.getSku().equals(sku)) { + return true; + } + } + return false; + } + + private static class InAppPurchaseFullVersion extends InAppPurchase { + + private static final String SKU_FULL_VERSION_PRICE = "net.osmand.huawei.full"; + + InAppPurchaseFullVersion() { + super(SKU_FULL_VERSION_PRICE); + } + + @Override + public String getDefaultPrice(Context ctx) { + return ctx.getString(R.string.full_version_price); + } + } + + private static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours { + + private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.huawei.seadepth"; + + InAppPurchaseDepthContoursFree() { + super(SKU_DEPTH_CONTOURS_FREE); + } + } + + private static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines { + + private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.huawei.contourlines"; + + InAppPurchaseContourLinesFree() { + super(SKU_CONTOUR_LINES_FREE); + } + } + + private static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly { + + private static final String SKU_LIVE_UPDATES_MONTHLY_HW_FREE = "net.osmand.huawei.monthly"; + + InAppPurchaseLiveUpdatesMonthlyFree() { + super(SKU_LIVE_UPDATES_MONTHLY_HW_FREE, 1); + } + + private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months { + + private static final String SKU_LIVE_UPDATES_3_MONTHS_HW_FREE = "net.osmand.huawei.3months"; + + InAppPurchaseLiveUpdates3MonthsFree() { + super(SKU_LIVE_UPDATES_3_MONTHS_HW_FREE, 1); + } + + private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null; + } + } + + private static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual { + + private static final String SKU_LIVE_UPDATES_ANNUAL_HW_FREE = "net.osmand.huawei.annual"; + + InAppPurchaseLiveUpdatesAnnualFree() { + super(SKU_LIVE_UPDATES_ANNUAL_HW_FREE, 1); + } + + private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) { + super(sku); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null; + } + } + + public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription { + + private ProductInfo info; + + InAppPurchaseLiveUpdatesOldSubscription(@NonNull ProductInfo info) { + super(info.getProductId(), true); + this.info = info; + } + + @Override + public String getDefaultPrice(Context ctx) { + return ""; + } + + @Override + public CharSequence getTitle(Context ctx) { + return info.getProductName(); + } + + @Override + public CharSequence getDescription(@NonNull Context ctx) { + return info.getProductDesc(); + } + + @Nullable + @Override + protected InAppSubscription newInstance(@NonNull String sku) { + return null; + } + } + + private static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList { + + public LiveUpdatesInAppPurchasesFree() { + super(LIVE_UPDATES_FREE); + } + } +} diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java new file mode 100644 index 0000000000..445727de96 --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java @@ -0,0 +1,49 @@ +package net.osmand.plus.inapp; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.huawei.hms.iap.entity.InAppPurchaseData; +import com.huawei.hms.iap.entity.OwnedPurchasesResult; + +import org.json.JSONException; + +public class InAppUtils { + private static final String TAG = "InAppUtils"; + + @Nullable + public static InAppPurchaseData getPurchaseData(OwnedPurchasesResult result, String productId) { + if (result == null || result.getInAppPurchaseDataList() == null) { + Log.i(TAG, "result is null"); + return null; + } + int index = result.getItemList().indexOf(productId); + if (index != -1) { + String data = result.getInAppPurchaseDataList().get(index); + String signature = result.getInAppSignature().get(index); + return getInAppPurchaseData(productId, data, signature); + } + return null; + } + + @Nullable + public static InAppPurchaseData getInAppPurchaseData(@Nullable String productId, @NonNull String data, @NonNull String signature) { + if (CipherUtil.doCheck(data, signature, CipherUtil.getPublicKey())) { + try { + InAppPurchaseData purchaseData = new InAppPurchaseData(data); + if (purchaseData.getPurchaseState() == InAppPurchaseData.PurchaseState.PURCHASED) { + if (productId == null || productId.equals(purchaseData.getProductId())) { + return purchaseData; + } + } + } catch (JSONException e) { + Log.e(TAG, "delivery: " + e.getMessage()); + } + } else { + Log.e(TAG, "delivery: verify signature error"); + } + return null; + } +} \ No newline at end of file diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java new file mode 100755 index 0000000000..3b249cb300 --- /dev/null +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java @@ -0,0 +1,138 @@ +/** + * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.osmand.plus.inapp; + +import android.app.Activity; +import android.content.Intent; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.huawei.hms.iap.Iap; +import com.huawei.hms.iap.entity.InAppPurchaseData; +import com.huawei.hms.iap.entity.OrderStatusCode; +import com.huawei.hms.iap.entity.OwnedPurchasesResult; +import com.huawei.hms.iap.entity.PurchaseResultInfo; + +import org.json.JSONException; + +import java.util.List; + +/** + * Util for Subscription function. + * + * @since 2019/12/9 + */ +public class SubscriptionUtils { + private static final String TAG = "SubscriptionUtils"; + + /** + * Decide whether to offer subscription service + * + * @param result the OwnedPurchasesResult from IapClient.obtainOwnedPurchases + * @param productId subscription product id + * @return decision result + */ + @Nullable + public static InAppPurchaseData getPurchaseData(OwnedPurchasesResult result, String productId) { + if (null == result) { + Log.e(TAG, "OwnedPurchasesResult is null"); + return null; + } + List dataList = result.getInAppPurchaseDataList(); + List signatureList = result.getInAppSignature(); + for (int i = 0; i < dataList.size(); i++) { + String data = dataList.get(i); + String signature = signatureList.get(i); + InAppPurchaseData purchaseData = getInAppPurchaseData(productId, data, signature); + if (purchaseData != null) { + return purchaseData; + } + } + return null; + } + + @Nullable + public static InAppPurchaseData getInAppPurchaseData(@Nullable String productId, @NonNull String data, @NonNull String signature) { + try { + InAppPurchaseData purchaseData = new InAppPurchaseData(data); + if (productId == null || productId.equals(purchaseData.getProductId())) { + boolean credible = CipherUtil.doCheck(data, signature, CipherUtil.getPublicKey()); + if (credible) { + return purchaseData.isSubValid() ? purchaseData : null; + } else { + Log.e(TAG, "check the data signature fail"); + return null; + } + } + } catch (JSONException e) { + Log.e(TAG, "parse InAppPurchaseData JSONException", e); + return null; + } + return null; + } + + /** + * Parse PurchaseResult data from intent + * + * @param activity Activity + * @param data the intent from onActivityResult + * @return PurchaseResultInfo + */ + public static PurchaseResultInfo getPurchaseResult(Activity activity, Intent data) { + PurchaseResultInfo purchaseResultInfo = Iap.getIapClient(activity).parsePurchaseResultInfoFromIntent(data); + if (null == purchaseResultInfo) { + Log.e(TAG, "PurchaseResultInfo is null"); + } else { + int returnCode = purchaseResultInfo.getReturnCode(); + String errMsg = purchaseResultInfo.getErrMsg(); + switch (returnCode) { + case OrderStatusCode.ORDER_PRODUCT_OWNED: + Log.w(TAG, "you have owned this product"); + break; + case OrderStatusCode.ORDER_STATE_SUCCESS: + boolean credible = CipherUtil.doCheck(purchaseResultInfo.getInAppPurchaseData(), purchaseResultInfo.getInAppDataSignature(), CipherUtil + .getPublicKey()); + if (credible) { + try { + InAppPurchaseData inAppPurchaseData = new InAppPurchaseData(purchaseResultInfo.getInAppPurchaseData()); + if (!inAppPurchaseData.isSubValid()) { + return getFailedPurchaseResultInfo(); + } + } catch (JSONException e) { + Log.e(TAG, "parse InAppPurchaseData JSONException", e); + return getFailedPurchaseResultInfo(); + } + } else { + Log.e(TAG, "check the data signature fail"); + return getFailedPurchaseResultInfo(); + } + default: + Log.e(TAG, "returnCode: " + returnCode + " , errMsg: " + errMsg); + break; + } + } + return purchaseResultInfo; + } + + private static PurchaseResultInfo getFailedPurchaseResultInfo() { + PurchaseResultInfo info = new PurchaseResultInfo(); + info.setReturnCode(OrderStatusCode.ORDER_STATE_FAILED); + return info; + } +} diff --git a/OsmAnd/src/net/osmand/FileUtils.java b/OsmAnd/src/net/osmand/FileUtils.java index 426c5c10c2..1f9a755be5 100644 --- a/OsmAnd/src/net/osmand/FileUtils.java +++ b/OsmAnd/src/net/osmand/FileUtils.java @@ -205,10 +205,7 @@ public class FileUtils { if (!src.exists()) { return null; } - File tempDir = app.getAppPath(IndexConstants.TEMP_DIR); - if (!tempDir.exists()) { - tempDir.mkdirs(); - } + File tempDir = getTempDir(app); File dest = new File(tempDir, src.getName()); try { Algorithms.fileCopy(src, dest); @@ -218,6 +215,27 @@ public class FileUtils { return dest; } + public static File getTempDir(OsmandApplication app) { + File tempDir = app.getAppPath(IndexConstants.TEMP_DIR); + if (!tempDir.exists()) { + tempDir.mkdirs(); + } + return tempDir; + } + + public static boolean isWritable(File dirToTest) { + boolean isWriteable; + try { + dirToTest.mkdirs(); + File writeTestFile = File.createTempFile("osmand_", ".tmp", dirToTest); + isWriteable = writeTestFile.exists(); + writeTestFile.delete(); + } catch (IOException e) { + isWriteable = false; + } + return isWriteable; + } + public interface RenameCallback { void renamedTo(File file); } diff --git a/OsmAnd/src/net/osmand/aidl/ConnectedApp.java b/OsmAnd/src/net/osmand/aidl/ConnectedApp.java index 02f412887e..be25ab931d 100644 --- a/OsmAnd/src/net/osmand/aidl/ConnectedApp.java +++ b/OsmAnd/src/net/osmand/aidl/ConnectedApp.java @@ -18,10 +18,10 @@ import net.osmand.AndroidUtils; import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.layers.AidlMapLayer; import net.osmand.plus.views.layers.MapInfoLayer; diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index 07c545a424..6329d5db2e 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -26,9 +26,11 @@ import com.google.gson.reflect.TypeToken; import net.osmand.AndroidUtils; import net.osmand.CallbackWithObject; +import net.osmand.FileUtils; import net.osmand.GPXUtilities; import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXTrackAnalysis; +import net.osmand.IProgress; import net.osmand.IndexConstants; import net.osmand.Location; import net.osmand.PlatformUtil; @@ -80,6 +82,7 @@ import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.SettingsHelper; +import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.AidlMapLayer; @@ -129,11 +132,13 @@ import static net.osmand.aidlapi.OsmandAidlConstants.COPY_FILE_PART_SIZE_LIMIT_E import static net.osmand.aidlapi.OsmandAidlConstants.COPY_FILE_UNSUPPORTED_FILE_TYPE_ERROR; import static net.osmand.aidlapi.OsmandAidlConstants.COPY_FILE_WRITE_LOCK_ERROR; import static net.osmand.aidlapi.OsmandAidlConstants.OK_RESPONSE; +import static net.osmand.plus.FavouritesDbHelper.FILE_TO_SAVE; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_DIRECTION_LANES; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_DIRECTION_NAME; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_DIRECTION_TURN; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_DISTANCE; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_IMMINENT; +import static net.osmand.plus.settings.backend.SettingsHelper.REPLACE_KEY; public class OsmandAidlApi { @@ -204,7 +209,7 @@ public class OsmandAidlApi { private static final ApplicationMode DEFAULT_PROFILE = ApplicationMode.CAR; - private static final ApplicationMode[] VALID_PROFILES = new ApplicationMode[] { + private static final ApplicationMode[] VALID_PROFILES = new ApplicationMode[]{ ApplicationMode.CAR, ApplicationMode.BICYCLE, ApplicationMode.PEDESTRIAN @@ -284,7 +289,7 @@ public class OsmandAidlApi { } private void initOsmandTelegram() { - String[] packages = new String[] {"net.osmand.telegram", "net.osmand.telegram.debug"}; + String[] packages = new String[]{"net.osmand.telegram", "net.osmand.telegram.debug"}; Intent intent = new Intent("net.osmand.telegram.InitApp"); for (String pack : packages) { intent.setComponent(new ComponentName(pack, "net.osmand.telegram.InitAppBroadcastReceiver")); @@ -1015,7 +1020,7 @@ public class OsmandAidlApi { } if (!newName.equals(f.getName()) || !newDescription.equals(f.getDescription()) || !newCategory.equals(f.getCategory()) || !newAddress.equals(f.getAddress())) { - favoritesHelper.editFavouriteName(f, newName, newCategory, newDescription,newAddress); + favoritesHelper.editFavouriteName(f, newName, newCategory, newDescription, newAddress); } refreshMap(); return true; @@ -2260,6 +2265,21 @@ public class OsmandAidlApi { return false; } + public boolean importProfileV2(final Uri profileUri, ArrayList settingsTypeKeys, boolean replace, + String latestChanges, int version) { + if (profileUri != null) { + Bundle bundle = new Bundle(); + bundle.putStringArrayList(SettingsHelper.SETTINGS_TYPE_LIST_KEY, settingsTypeKeys); + bundle.putBoolean(REPLACE_KEY, replace); + bundle.putString(SettingsHelper.SETTINGS_LATEST_CHANGES_KEY, latestChanges); + bundle.putInt(SettingsHelper.SETTINGS_VERSION_KEY, version); + + MapActivity.launchMapActivityMoveToTop(app, null, profileUri, bundle); + return true; + } + return false; + } + public void registerLayerContextMenu(ContextMenuAdapter adapter, MapActivity mapActivity) { for (ConnectedApp connectedApp : getConnectedApps()) { if (!connectedApp.getLayers().isEmpty()) { @@ -2323,6 +2343,25 @@ public class OsmandAidlApi { return true; } + public boolean exportProfile(String appModeKey, List settingsTypesKeys) { + ApplicationMode appMode = ApplicationMode.valueOfStringKey(appModeKey, null); + if (app != null && appMode != null) { + List settingsTypes = new ArrayList<>(); + for (String key : settingsTypesKeys) { + settingsTypes.add(ExportSettingsType.valueOf(key)); + } + List settingsItems = new ArrayList<>(); + settingsItems.add(new SettingsHelper.ProfileSettingsItem(app, appMode)); + File exportDir = app.getSettings().getExternalStorageDirectory(); + String fileName = appMode.toHumanString(); + SettingsHelper settingsHelper = app.getSettingsHelper(); + settingsItems.addAll(settingsHelper.getFilteredSettingsItems(settingsHelper.getAdditionalData(), settingsTypes)); + settingsHelper.exportSettings(exportDir, fileName, null, settingsItems, true); + return true; + } + return false; + } + private static class FileCopyInfo { long startTime; long lastAccessTime; @@ -2349,13 +2388,35 @@ public class OsmandAidlApi { } } - private int copyFileImpl(String fileName, byte[] filePartData, long startTime, boolean done, String destinationDir) { - File file = app.getAppPath(IndexConstants.TEMP_DIR + fileName); - File tempDir = app.getAppPath(IndexConstants.TEMP_DIR); - if (!tempDir.exists()) { - tempDir.mkdirs(); + int copyFileV2(String destinationDir, String fileName, byte[] filePartData, long startTime, boolean done) { + if (Algorithms.isEmpty(fileName) || filePartData == null) { + return COPY_FILE_PARAMS_ERROR; } - File destFile = app.getAppPath(destinationDir + fileName); + if (filePartData.length > COPY_FILE_PART_SIZE_LIMIT) { + return COPY_FILE_PART_SIZE_LIMIT_ERROR; + } + int result = copyFileImpl(fileName, filePartData, startTime, done, destinationDir); + if (done) { + if (fileName.endsWith(IndexConstants.BINARY_MAP_INDEX_EXT) && IndexConstants.MAPS_PATH.equals(destinationDir)) { + app.getResourceManager().reloadIndexes(IProgress.EMPTY_PROGRESS, new ArrayList()); + app.getDownloadThread().updateLoadedFiles(); + } else if (fileName.endsWith(IndexConstants.GPX_FILE_EXT)) { + if (destinationDir.startsWith(IndexConstants.GPX_INDEX_DIR) + && !FILE_TO_SAVE.equals(fileName)) { + destinationDir = destinationDir.replaceFirst(IndexConstants.GPX_INDEX_DIR, ""); + showGpx(new File(destinationDir, fileName).getPath()); + } else if (destinationDir.isEmpty() && FILE_TO_SAVE.equals(fileName)) { + app.getFavorites().loadFavorites(); + } + } + } + return result; + } + + private int copyFileImpl(String fileName, byte[] filePartData, long startTime, boolean done, String destinationDir) { + File tempDir = FileUtils.getTempDir(app); + File file = new File(tempDir, fileName); + File destFile = app.getAppPath(new File(destinationDir, fileName).getPath()); long currentTime = System.currentTimeMillis(); try { FileCopyInfo info = copyFilesCache.get(fileName); diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java index f1c9493fc8..99e888973f 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java @@ -1299,7 +1299,8 @@ public class OsmandAidlService extends Service implements AidlCallbackListener { public boolean importProfile(ProfileSettingsParams params) { try { OsmandAidlApi api = getApi("importProfile"); - return api != null && api.importProfile(params.getProfileSettingsUri(), params.getLatestChanges(), params.getVersion()); + return api != null && api.importProfile(params.getProfileSettingsUri(), params.getLatestChanges(), + params.getVersion()); } catch (Exception e) { handleException(e); return false; diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlServiceV2.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlServiceV2.java index 333fd01895..7c69be1e94 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlServiceV2.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlServiceV2.java @@ -85,6 +85,7 @@ import net.osmand.aidlapi.note.StartVideoRecordingParams; import net.osmand.aidlapi.note.StopRecordingParams; import net.osmand.aidlapi.note.TakePhotoNoteParams; import net.osmand.aidlapi.plugins.PluginParams; +import net.osmand.aidlapi.profile.ExportProfileParams; import net.osmand.aidlapi.quickaction.QuickActionInfoParams; import net.osmand.aidlapi.quickaction.QuickActionParams; import net.osmand.aidlapi.search.SearchParams; @@ -1091,7 +1092,8 @@ public class OsmandAidlServiceV2 extends Service implements AidlCallbackListener if (api == null) { return CANNOT_ACCESS_API_ERROR; } - return api.copyFile(params.getFileName(), params.getFilePartData(), params.getStartTime(), params.isDone()); + return api.copyFileV2(params.getDestinationDir(), params.getFileName(), params.getFilePartData(), + params.getStartTime(), params.isDone()); } catch (Exception e) { handleException(e); return UNKNOWN_API_ERROR; @@ -1258,7 +1260,19 @@ public class OsmandAidlServiceV2 extends Service implements AidlCallbackListener public boolean importProfile(ProfileSettingsParams params) { try { OsmandAidlApi api = getApi("importProfile"); - return api != null && api.importProfile(params.getProfileSettingsUri(), params.getLatestChanges(), params.getVersion()); + return api != null && api.importProfileV2(params.getProfileSettingsUri(), params.getSettingsTypeKeys(), + params.isReplace(), params.getLatestChanges(), params.getVersion()); + } catch (Exception e) { + handleException(e); + return false; + } + } + + @Override + public boolean exportProfile(ExportProfileParams params) { + try { + OsmandAidlApi api = getApi("exportProfile"); + return api != null && api.exportProfile(params.getProfile(), params.getSettingsTypeKeys()); } catch (Exception e) { handleException(e); return false; diff --git a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java index 03d88fa11c..d65880877c 100644 --- a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java +++ b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java @@ -25,7 +25,7 @@ import net.osmand.core.jni.ResolvedMapStyle; import net.osmand.core.jni.SwigUtilities; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.render.RendererRegistry; import net.osmand.render.RenderingRuleProperty; import net.osmand.render.RenderingRuleStorageProperties; diff --git a/OsmAnd/src/net/osmand/data/FavouritePoint.java b/OsmAnd/src/net/osmand/data/FavouritePoint.java index e5f4f6c8ea..7205b4a422 100644 --- a/OsmAnd/src/net/osmand/data/FavouritePoint.java +++ b/OsmAnd/src/net/osmand/data/FavouritePoint.java @@ -13,8 +13,8 @@ import androidx.annotation.StringRes; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.FavouritesDbHelper; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings.BooleanPreference; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.BooleanPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.parkingpoint.ParkingPositionPlugin; import net.osmand.util.Algorithms; diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index 2837bff6f8..78364460a7 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -28,7 +28,7 @@ import net.osmand.map.OsmandRegions.RegionTranslation; import net.osmand.map.WorldRegion; import net.osmand.osm.AbstractPoiType; import net.osmand.osm.MapPoiTypes; -import net.osmand.plus.activities.DayNightHelper; +import net.osmand.plus.helpers.DayNightHelper; import net.osmand.plus.activities.LocalIndexHelper; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.activities.SavingTrackHelper; @@ -38,7 +38,7 @@ import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask; import net.osmand.plus.helpers.AvoidSpecificRoads; import net.osmand.plus.helpers.LockHelper; import net.osmand.plus.helpers.WaypointHelper; -import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.inapp.InAppPurchaseHelperImpl; import net.osmand.plus.liveupdates.LiveUpdatesHelper; import net.osmand.plus.mapmarkers.MapMarkersDbHelper; import net.osmand.plus.monitoring.LiveMonitoringHelper; @@ -428,7 +428,7 @@ public class AppInitializer implements IProgress { } getLazyRoutingConfig(); app.applyTheme(app); - app.inAppPurchaseHelper = startupInit(new InAppPurchaseHelper(app), InAppPurchaseHelper.class); + app.inAppPurchaseHelper = startupInit(new InAppPurchaseHelperImpl(app), InAppPurchaseHelperImpl.class); app.poiTypes = startupInit(MapPoiTypes.getDefaultNoInit(), MapPoiTypes.class); app.transportRoutingHelper = startupInit(new TransportRoutingHelper(app), TransportRoutingHelper.class); app.routingHelper = startupInit(new RoutingHelper(app), RoutingHelper.class); diff --git a/OsmAnd/src/net/osmand/plus/AppVersionUpgradeOnInit.java b/OsmAnd/src/net/osmand/plus/AppVersionUpgradeOnInit.java index c49f0a12a2..9eeec4542b 100644 --- a/OsmAnd/src/net/osmand/plus/AppVersionUpgradeOnInit.java +++ b/OsmAnd/src/net/osmand/plus/AppVersionUpgradeOnInit.java @@ -3,7 +3,30 @@ package net.osmand.plus; import android.annotation.SuppressLint; import android.content.SharedPreferences; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +import net.osmand.data.FavouritePoint.SpecialPointType; +import net.osmand.data.LatLon; +import net.osmand.plus.AppInitializer.AppInitializeListener; +import net.osmand.plus.AppInitializer.InitEvents; +import net.osmand.plus.api.SettingsAPI; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.EnumStringPreference; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + class AppVersionUpgradeOnInit { + public static final String FIRST_TIME_APP_RUN = "FIRST_TIME_APP_RUN"; //$NON-NLS-1$ public static final String VERSION_INSTALLED_NUMBER = "VERSION_INSTALLED_NUMBER"; //$NON-NLS-1$ public static final String NUMBER_OF_STARTS = "NUMBER_OF_STARTS"; //$NON-NLS-1$ @@ -43,7 +66,7 @@ class AppVersionUpgradeOnInit { @SuppressLint("ApplySharedPref") void upgradeVersion(SharedPreferences startPrefs, int lastVersion) { - if(!startPrefs.contains(NUMBER_OF_STARTS)) { + if (!startPrefs.contains(NUMBER_OF_STARTS)) { startPrefs.edit().putInt(NUMBER_OF_STARTS, 1).commit(); } else { startPrefs.edit().putInt(NUMBER_OF_STARTS, startPrefs.getInt(NUMBER_OF_STARTS, 0) + 1).commit(); @@ -59,27 +82,28 @@ class AppVersionUpgradeOnInit { } else { prevAppVersion = startPrefs.getInt(VERSION_INSTALLED_NUMBER, 0); if (needsUpgrade(startPrefs, lastVersion)) { + OsmandSettings settings = app.getSettings(); if (prevAppVersion < VERSION_2_2) { - app.getSettings().SHOW_DASHBOARD_ON_START.set(true); - app.getSettings().SHOW_DASHBOARD_ON_MAP_SCREEN.set(true); - app.getSettings().SHOW_CARD_TO_CHOOSE_DRAWER.set(true); + settings.SHOW_DASHBOARD_ON_START.set(true); + settings.SHOW_DASHBOARD_ON_MAP_SCREEN.set(true); + settings.SHOW_CARD_TO_CHOOSE_DRAWER.set(true); startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_2_2).commit(); } if (prevAppVersion < VERSION_2_3) { startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_2_3).commit(); } if (prevAppVersion < VERSION_3_2) { - app.getSettings().BILLING_PURCHASE_TOKENS_SENT.set(""); + settings.BILLING_PURCHASE_TOKENS_SENT.set(""); startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_3_2).commit(); } if (prevAppVersion < VERSION_3_5 || Version.getAppVersion(app).equals("3.5.3") || Version.getAppVersion(app).equals("3.5.4")) { - app.getSettings().migratePreferences(); + migratePreferences(); app.getAppInitializer().addListener(new AppInitializer.AppInitializeListener() { @Override public void onProgress(AppInitializer init, AppInitializer.InitEvents event) { if (event.equals(AppInitializer.InitEvents.FAVORITES_INITIALIZED)) { - app.getSettings().migrateHomeWorkParkingToFavorites(); + migrateHomeWorkParkingToFavorites(); } } @@ -90,21 +114,22 @@ class AppVersionUpgradeOnInit { startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_3_5).commit(); } if (prevAppVersion < VERSION_3_6) { - app.getSettings().migratePreferences(); + migratePreferences(); startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_3_6).commit(); } if (prevAppVersion < VERSION_3_7) { - app.getSettings().migrateEnumPreferences(); + migrateEnumPreferences(); startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_3_7).commit(); } if (prevAppVersion < VERSION_3_7_01) { - app.getAppInitializer().addListener(new AppInitializer.AppInitializeListener() { + app.getAppInitializer().addListener(new AppInitializeListener() { @Override public void onProgress(AppInitializer init, AppInitializer.InitEvents event) { - if (event.equals(AppInitializer.InitEvents.FAVORITES_INITIALIZED)) { + if (event.equals(InitEvents.FAVORITES_INITIALIZED)) { app.getFavorites().fixBlackBackground(); } } + @Override public void onFinish(AppInitializer init) { } @@ -112,7 +137,7 @@ class AppVersionUpgradeOnInit { startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_3_7_01).commit(); } if (prevAppVersion < VERSION_3_8_00) { - app.getSettings().migrateQuickActionStates(); + migrateQuickActionStates(); startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_3_8_00).commit(); } startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, lastVersion).commit(); @@ -135,20 +160,20 @@ class AppVersionUpgradeOnInit { } public void resetFirstTimeRun(SharedPreferences startPrefs) { - if(startPrefs != null) { + if (startPrefs != null) { startPrefs.edit().remove(FIRST_TIME_APP_RUN).commit(); } } public int getNumberOfStarts(SharedPreferences startPrefs) { - if(startPrefs == null) { + if (startPrefs == null) { return 0; } return startPrefs.getInt(NUMBER_OF_STARTS, 1); } public long getFirstInstalledDays(SharedPreferences startPrefs) { - if(startPrefs == null) { + if (startPrefs == null) { return 0; } long nd = startPrefs.getLong(FIRST_INSTALLED, 0); @@ -159,4 +184,165 @@ class AppVersionUpgradeOnInit { public boolean isFirstTime() { return firstTime; } -} + + public void migratePreferences() { + OsmandSettings settings = app.getSettings(); + migrateEnumPreferences(); + SharedPreferences globalSharedPreferences = (SharedPreferences) settings.getGlobalPreferences(); + Map globalPrefsMap = globalSharedPreferences.getAll(); + for (String key : globalPrefsMap.keySet()) { + OsmandPreference pref = settings.getPreference(key); + if (pref instanceof CommonPreference) { + CommonPreference commonPreference = (CommonPreference) pref; + if (!commonPreference.isGlobal()) { + for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { + if (!commonPreference.isSetForMode(mode) && !commonPreference.hasDefaultValueForMode(mode)) { + settings.setPreference(key, globalPrefsMap.get(key), mode); + } + } + } + } + } + SharedPreferences defaultProfilePreferences = (SharedPreferences) settings.getProfilePreferences(ApplicationMode.DEFAULT); + Map defaultPrefsMap = defaultProfilePreferences.getAll(); + for (String key : defaultPrefsMap.keySet()) { + OsmandPreference pref = settings.getPreference(key); + if (pref instanceof CommonPreference) { + CommonPreference commonPreference = (CommonPreference) pref; + if (commonPreference.isGlobal() && !commonPreference.isSet()) { + settings.setPreference(key, defaultPrefsMap.get(key)); + } + } + } + for (OsmandPreference pref : getGeneralPrefs()) { + if (pref instanceof CommonPreference) { + CommonPreference commonPref = (CommonPreference) pref; + Object defaultVal = commonPref.getModeValue(ApplicationMode.DEFAULT); + for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { + if (!commonPref.isSetForMode(mode) && !commonPref.hasDefaultValueForMode(mode)) { + settings.setPreference(commonPref.getId(), defaultVal, mode); + } + } + } + } + String json = settings.getSettingsAPI().getString(settings.getGlobalPreferences(), "custom_app_profiles", ""); + if (!Algorithms.isEmpty(json)) { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + Type t = new TypeToken>() { + }.getType(); + List customProfiles = gson.fromJson(json, t); + if (!Algorithms.isEmpty(customProfiles)) { + for (ApplicationMode.ApplicationModeBean modeBean : customProfiles) { + ApplicationMode.ApplicationModeBuilder builder = ApplicationMode.fromModeBean(app, modeBean); + ApplicationMode.saveProfile(builder, app); + } + } + } + } + + public void migrateEnumPreferences() { + OsmandSettings settings = app.getSettings(); + for (OsmandPreference pref : settings.getRegisteredPreferences().values()) { + if (pref instanceof EnumStringPreference) { + EnumStringPreference enumPref = (EnumStringPreference) pref; + if (enumPref.isGlobal()) { + migrateEnumPref(enumPref, (SharedPreferences) settings.getGlobalPreferences()); + } else { + for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { + migrateEnumPref(enumPref, (SharedPreferences) settings.getProfilePreferences(mode)); + } + } + } + } + } + + public void migrateQuickActionStates() { + OsmandSettings settings = app.getSettings(); + String quickActionsJson = settings.getSettingsAPI().getString(settings.getGlobalPreferences(), "quick_action_new", ""); + if (!Algorithms.isEmpty(quickActionsJson)) { + Gson gson = new GsonBuilder().create(); + Type type = new TypeToken>() { + }.getType(); + HashMap quickActions = gson.fromJson(quickActionsJson, type); + if (!Algorithms.isEmpty(quickActions)) { + for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { + settings.setQuickActions(quickActions, mode); + } + } + } + } + + private void migrateEnumPref(EnumStringPreference enumPref, SharedPreferences sharedPreferences) { + Object value = sharedPreferences.getAll().get(enumPref.getId()); + if (value instanceof Integer) { + int enumIndex = (int) value; + if (enumIndex >= 0 && enumIndex < enumPref.getValues().length) { + Enum savedValue = enumPref.getValues()[enumIndex]; + enumPref.setValue(sharedPreferences, savedValue); + } + } + } + + public void migrateHomeWorkParkingToFavorites() { + OsmandSettings settings = app.getSettings(); + FavouritesDbHelper favorites = app.getFavorites(); + SettingsAPI settingsAPI = settings.getSettingsAPI(); + Object globalPreferences = settings.getGlobalPreferences(); + + LatLon homePoint = null; + float lat = settingsAPI.getFloat(globalPreferences, "home_point_lat", 0); + float lon = settingsAPI.getFloat(globalPreferences, "home_point_lon", 0); + if (lat != 0 || lon != 0) { + homePoint = new LatLon(lat, lon); + } + LatLon workPoint = null; + lat = settingsAPI.getFloat(globalPreferences, "work_point_lat", 0); + lon = settingsAPI.getFloat(globalPreferences, "work_point_lon", 0); + if (lat != 0 || lon != 0) { + workPoint = new LatLon(lat, lon); + } + if (homePoint != null) { + favorites.setSpecialPoint(homePoint, SpecialPointType.HOME, null); + } + if (workPoint != null) { + favorites.setSpecialPoint(workPoint, SpecialPointType.WORK, null); + } + } + + + public OsmandPreference[] getGeneralPrefs() { + OsmandSettings settings = app.getSettings(); + return new OsmandPreference[] { + settings.EXTERNAL_INPUT_DEVICE, + settings.CENTER_POSITION_ON_MAP, + settings.ROTATE_MAP, + settings.MAP_SCREEN_ORIENTATION, + settings.LIVE_MONITORING_URL, + settings.LIVE_MONITORING_MAX_INTERVAL_TO_SEND, + settings.LIVE_MONITORING_INTERVAL, + settings.LIVE_MONITORING, + settings.SHOW_TRIP_REC_NOTIFICATION, + settings.AUTO_SPLIT_RECORDING, + settings.SAVE_TRACK_MIN_SPEED, + settings.SAVE_TRACK_PRECISION, + settings.SAVE_TRACK_MIN_DISTANCE, + settings.SAVE_TRACK_INTERVAL, + settings.TRACK_STORAGE_DIRECTORY, + settings.SAVE_HEADING_TO_GPX, + settings.DISABLE_RECORDING_ONCE_APP_KILLED, + settings.SAVE_TRACK_TO_GPX, + settings.SAVE_GLOBAL_TRACK_REMEMBER, + settings.SAVE_GLOBAL_TRACK_INTERVAL, + settings.MAP_EMPTY_STATE_ALLOWED, + settings.DO_NOT_USE_ANIMATIONS, + settings.USE_KALMAN_FILTER_FOR_COMPASS, + settings.USE_MAGNETIC_FIELD_SENSOR_COMPASS, + settings.USE_TRACKBALL_FOR_MOVEMENTS, + settings.SPEED_SYSTEM, + settings.ANGULAR_UNITS, + settings.METRIC_SYSTEM, + settings.DRIVING_REGION, + settings.DRIVING_REGION_AUTOMATIC + }; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java b/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java index 7263f119bd..d85d6ddd68 100644 --- a/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java +++ b/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java @@ -38,9 +38,8 @@ import net.osmand.plus.dialogs.HelpArticleDialogFragment; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.ContextMenuItemsPreference; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.ContextMenuItemsPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -53,6 +52,9 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_CONFIGURE_PROFILE_ID; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SWITCH_PROFILE_ID; + public class ContextMenuAdapter { private static final Log LOG = PlatformUtil.getLog(ContextMenuAdapter.class); @@ -134,6 +136,19 @@ public class ContextMenuAdapter { Collections.sort(items, new Comparator() { @Override public int compare(ContextMenuItem item1, ContextMenuItem item2) { + if (DRAWER_CONFIGURE_PROFILE_ID.equals(item1.getId()) + && DRAWER_SWITCH_PROFILE_ID.equals(item2.getId())) { + return 1; + } else if (DRAWER_SWITCH_PROFILE_ID.equals(item1.getId()) + && DRAWER_CONFIGURE_PROFILE_ID.equals(item2.getId())) { + return -1; + } else if (DRAWER_SWITCH_PROFILE_ID.equals(item1.getId()) + || DRAWER_CONFIGURE_PROFILE_ID.equals(item1.getId())) { + return -1; + } else if (DRAWER_SWITCH_PROFILE_ID.equals(item2.getId()) + || DRAWER_CONFIGURE_PROFILE_ID.equals(item2.getId())) { + return 1; + } int order1 = item1.getOrder(); int order2 = item2.getOrder(); if (order1 < order2) { @@ -631,7 +646,7 @@ public class ContextMenuAdapter { return makeDeleteAction(prefs.toArray(new OsmandPreference[prefs.size()])); } - private static void resetSetting(ApplicationMode appMode, OsmandSettings.OsmandPreference preference, boolean profileOnly) { + private static void resetSetting(ApplicationMode appMode, OsmandPreference preference, boolean profileOnly) { if (profileOnly) { preference.resetModeToDefault(appMode); } else { diff --git a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java index 7217e8af5f..923bbe646a 100644 --- a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java +++ b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java @@ -29,7 +29,7 @@ import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType; import net.osmand.plus.routing.RouteProvider; -import net.osmand.plus.settings.backend.OsmandSettings.MetricsConstants; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.track.GpxSplitType; import net.osmand.plus.track.GradientScaleType; import net.osmand.util.Algorithms; diff --git a/OsmAnd/src/net/osmand/plus/HuaweiDrmHelper.java b/OsmAnd/src/net/osmand/plus/HuaweiDrmHelper.java deleted file mode 100644 index 7cc2f2798e..0000000000 --- a/OsmAnd/src/net/osmand/plus/HuaweiDrmHelper.java +++ /dev/null @@ -1,66 +0,0 @@ -package net.osmand.plus; - -import android.app.Activity; -import android.util.Log; - -import java.lang.ref.WeakReference; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class HuaweiDrmHelper { - private static final String TAG = HuaweiDrmHelper.class.getSimpleName(); - - //Copyright protection id - private static final String DRM_ID = "101117397"; - //Copyright protection public key - private static final String DRM_PUBLIC_KEY = "9d6f861e7d46be167809a6a62302749a6753b3c1bd02c9729efb3973e268091d"; - - public static void check(Activity activity) { - boolean succeed = false; - try { - final WeakReference activityRef = new WeakReference<>(activity); - Class drmCheckCallbackClass = Class.forName("com.huawei.android.sdk.drm.DrmCheckCallback"); - Object callback = java.lang.reflect.Proxy.newProxyInstance( - drmCheckCallbackClass.getClassLoader(), - new java.lang.Class[]{drmCheckCallbackClass}, - new java.lang.reflect.InvocationHandler() { - - @Override - public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) { - Activity a = activityRef.get(); - if (a != null && !a.isFinishing()) { - String method_name = method.getName(); - if (method_name.equals("onCheckSuccess")) { - // skip now - } else if (method_name.equals("onCheckFailed")) { - closeApplication(a); - } - } - return null; - } - }); - - Class drmClass = Class.forName("com.huawei.android.sdk.drm.Drm"); - Class[] partypes = new Class[]{Activity.class, String.class, String.class, String.class, drmCheckCallbackClass}; - Method check = drmClass.getMethod("check", partypes); - check.invoke(null, activity, activity.getPackageName(), DRM_ID, DRM_PUBLIC_KEY, callback); - succeed = true; - - } catch (ClassNotFoundException e) { - Log.e(TAG, "check: ", e); - } catch (NoSuchMethodException e) { - Log.e(TAG, "check: ", e); - } catch (IllegalAccessException e) { - Log.e(TAG, "check: ", e); - } catch (InvocationTargetException e) { - Log.e(TAG, "check: ", e); - } - if (!succeed) { - closeApplication(activity); - } - } - - private static void closeApplication(Activity activity) { - ((OsmandApplication) activity.getApplication()).closeApplicationAnywayImpl(activity, true); - } -} diff --git a/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java b/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java index 2ac142d06a..b58b3eba5e 100644 --- a/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java +++ b/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java @@ -15,9 +15,9 @@ import net.osmand.osm.PoiCategory; import net.osmand.osm.PoiType; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.AngularConstants; -import net.osmand.plus.settings.backend.OsmandSettings.MetricsConstants; -import net.osmand.plus.settings.backend.OsmandSettings.SpeedConstants; +import net.osmand.plus.helpers.enums.AngularConstants; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.helpers.enums.SpeedConstants; import net.osmand.util.Algorithms; import java.text.DateFormatSymbols; diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 43c3a17f09..1a48650a4d 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -30,6 +30,7 @@ import androidx.multidex.MultiDex; import androidx.multidex.MultiDexApplication; import net.osmand.AndroidUtils; +import net.osmand.FileUtils; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.access.AccessibilityPlugin; @@ -42,7 +43,7 @@ import net.osmand.osm.MapPoiTypes; import net.osmand.osm.io.NetworkUtils; import net.osmand.plus.AppInitializer.AppInitializeListener; import net.osmand.plus.access.AccessibilityMode; -import net.osmand.plus.activities.DayNightHelper; +import net.osmand.plus.helpers.DayNightHelper; import net.osmand.plus.activities.ExitActivity; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.SavingTrackHelper; @@ -56,7 +57,9 @@ import net.osmand.plus.download.DownloadIndexesThread; import net.osmand.plus.download.DownloadService; import net.osmand.plus.download.IndexItem; import net.osmand.plus.helpers.AvoidSpecificRoads; +import net.osmand.plus.helpers.enums.DrivingRegion; import net.osmand.plus.helpers.LockHelper; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.helpers.WaypointHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.mapmarkers.MapMarkersDbHelper; @@ -189,7 +192,7 @@ public class OsmandApplication extends MultiDexApplication { osmandSettings.initExternalStorageDirectory(); } externalStorageDirectory = osmandSettings.getExternalStorageDirectory(); - if (!OsmandSettings.isWritable(externalStorageDirectory)) { + if (!FileUtils.isWritable(externalStorageDirectory)) { externalStorageDirectoryReadOnly = true; externalStorageDirectory = osmandSettings.getInternalAppPath(); } @@ -983,15 +986,15 @@ public class OsmandApplication extends MultiDexApplication { } public void setupDrivingRegion(WorldRegion reg) { - OsmandSettings.DrivingRegion drg = null; + DrivingRegion drg = null; WorldRegion.RegionParams params = reg.getParams(); // boolean americanSigns = "american".equals(params.getRegionRoadSigns()); boolean leftHand = "yes".equals(params.getRegionLeftHandDriving()); - OsmandSettings.MetricsConstants mc1 = "miles".equals(params.getRegionMetric()) ? - OsmandSettings.MetricsConstants.MILES_AND_FEET : OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS; - OsmandSettings.MetricsConstants mc2 = "miles".equals(params.getRegionMetric()) ? - OsmandSettings.MetricsConstants.MILES_AND_METERS : OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS; - for (OsmandSettings.DrivingRegion r : OsmandSettings.DrivingRegion.values()) { + MetricsConstants mc1 = "miles".equals(params.getRegionMetric()) ? + MetricsConstants.MILES_AND_FEET : MetricsConstants.KILOMETERS_AND_METERS; + MetricsConstants mc2 = "miles".equals(params.getRegionMetric()) ? + MetricsConstants.MILES_AND_METERS : MetricsConstants.KILOMETERS_AND_METERS; + for (DrivingRegion r : DrivingRegion.values()) { if (r.leftHandDriving == leftHand && (r.defMetrics == mc1 || r.defMetrics == mc2)) { drg = r; break; diff --git a/OsmAnd/src/net/osmand/plus/OsmandPlugin.java b/OsmAnd/src/net/osmand/plus/OsmandPlugin.java index 0d887bc47f..4e9ca3d420 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandPlugin.java +++ b/OsmAnd/src/net/osmand/plus/OsmandPlugin.java @@ -47,6 +47,8 @@ import net.osmand.plus.rastermaps.OsmandRasterMapsPlugin; import net.osmand.plus.search.QuickSearchDialogFragment; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.skimapsplugin.SkiMapsPlugin; import net.osmand.plus.srtmplugin.SRTMPlugin; @@ -80,7 +82,7 @@ public abstract class OsmandPlugin { protected OsmandApplication app; - protected List pluginPreferences = new ArrayList<>(); + protected List pluginPreferences = new ArrayList<>(); private boolean active; private String installURL = null; @@ -118,7 +120,7 @@ public abstract class OsmandPlugin { return null; } - public List getPreferences() { + public List getPreferences() { return pluginPreferences; } @@ -919,44 +921,44 @@ public abstract class OsmandPlugin { } } - protected OsmandSettings.CommonPreference registerBooleanPreference(OsmandApplication app, String prefId, boolean defValue) { - OsmandSettings.CommonPreference preference = app.getSettings().registerBooleanPreference(prefId, defValue); + protected CommonPreference registerBooleanPreference(OsmandApplication app, String prefId, boolean defValue) { + CommonPreference preference = app.getSettings().registerBooleanPreference(prefId, defValue); pluginPreferences.add(preference); return preference; } - private OsmandSettings.CommonPreference registerBooleanAccessibilityPreference(OsmandApplication app, String prefId, boolean defValue) { - OsmandSettings.CommonPreference preference = app.getSettings().registerBooleanAccessibilityPreference(prefId, defValue); + private CommonPreference registerBooleanAccessibilityPreference(OsmandApplication app, String prefId, boolean defValue) { + CommonPreference preference = app.getSettings().registerBooleanAccessibilityPreference(prefId, defValue); pluginPreferences.add(preference); return preference; } - protected OsmandSettings.CommonPreference registerStringPreference(OsmandApplication app, String prefId, String defValue) { - OsmandSettings.CommonPreference preference = app.getSettings().registerStringPreference(prefId, defValue); + protected CommonPreference registerStringPreference(OsmandApplication app, String prefId, String defValue) { + CommonPreference preference = app.getSettings().registerStringPreference(prefId, defValue); pluginPreferences.add(preference); return preference; } - protected OsmandSettings.CommonPreference registerIntPreference(OsmandApplication app, String prefId, int defValue) { - OsmandSettings.CommonPreference preference = app.getSettings().registerIntPreference(prefId, defValue); + protected CommonPreference registerIntPreference(OsmandApplication app, String prefId, int defValue) { + CommonPreference preference = app.getSettings().registerIntPreference(prefId, defValue); pluginPreferences.add(preference); return preference; } - protected OsmandSettings.CommonPreference registerLongPreference(OsmandApplication app, String prefId, long defValue) { - OsmandSettings.CommonPreference preference = app.getSettings().registerLongPreference(prefId, defValue); + protected CommonPreference registerLongPreference(OsmandApplication app, String prefId, long defValue) { + CommonPreference preference = app.getSettings().registerLongPreference(prefId, defValue); pluginPreferences.add(preference); return preference; } - protected OsmandSettings.CommonPreference registerFloatPreference(OsmandApplication app, String prefId, float defValue) { - OsmandSettings.CommonPreference preference = app.getSettings().registerFloatPreference(prefId, defValue); + protected CommonPreference registerFloatPreference(OsmandApplication app, String prefId, float defValue) { + CommonPreference preference = app.getSettings().registerFloatPreference(prefId, defValue); pluginPreferences.add(preference); return preference; } - protected OsmandSettings.CommonPreference registerEnumIntPreference(OsmandApplication app, String prefId, Enum defaultValue, Enum[] values, Class clz) { - OsmandSettings.CommonPreference preference = app.getSettings().registerEnumIntPreference(prefId, defaultValue, values, clz); + protected CommonPreference registerEnumIntPreference(OsmandApplication app, String prefId, Enum defaultValue, Enum[] values, Class clz) { + CommonPreference preference = app.getSettings().registerEnumIntPreference(prefId, defaultValue, values, clz); pluginPreferences.add(preference); return preference; } diff --git a/OsmAnd/src/net/osmand/plus/Version.java b/OsmAnd/src/net/osmand/plus/Version.java index 14ed68b100..48dd9b1feb 100644 --- a/OsmAnd/src/net/osmand/plus/Version.java +++ b/OsmAnd/src/net/osmand/plus/Version.java @@ -1,13 +1,15 @@ package net.osmand.plus; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; +import android.content.pm.PackageManager; import net.osmand.plus.inapp.InAppPurchaseHelper; +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + public class Version { private final String appVersion; @@ -121,8 +123,8 @@ public class Version { public static boolean isFreeVersion(OsmandApplication ctx){ return ctx.getPackageName().equals(FREE_VERSION_NAME) || ctx.getPackageName().equals(FREE_DEV_VERSION_NAME) || - ctx.getPackageName().equals(FREE_CUSTOM_VERSION_NAME) - ; + ctx.getPackageName().equals(FREE_CUSTOM_VERSION_NAME) || + isHuawei(ctx); } public static boolean isPaidVersion(OsmandApplication ctx) { @@ -149,4 +151,21 @@ public class Version { return v; } -} + public static boolean isOpenGlAvailable(OsmandApplication app) { + if ("qnx".equals(System.getProperty("os.name"))) { + return false; + } + File nativeLibraryDir = new File(app.getApplicationInfo().nativeLibraryDir); + if (nativeLibraryDir.exists() && nativeLibraryDir.canRead()) { + File[] files = nativeLibraryDir.listFiles(); + if (files != null) { + for (File file : files) { + if ("libOsmAndCoreWithJNI.so".equals(file.getName())) { + return true; + } + } + } + } + return false; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java b/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java index 91bf4ffbf9..b1a625d27e 100644 --- a/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java @@ -43,6 +43,7 @@ import net.osmand.plus.FavouritesDbHelper.FavoriteGroup; import net.osmand.plus.FavouritesDbHelper.FavoritesListener; import net.osmand.plus.MapMarkersHelper; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -96,7 +97,7 @@ public class FavoritesTreeFragment extends OsmandExpandableListFragment implemen private Set groupsToDelete = new LinkedHashSet<>(); private ActionMode actionMode; private Drawable arrowImageDisabled; - private HashMap> preferenceCache = new HashMap<>(); + private HashMap> preferenceCache = new HashMap<>(); private View footerView; private Location lastLocation; private float lastHeading; @@ -763,8 +764,8 @@ public class FavoritesTreeFragment extends OsmandExpandableListFragment implemen } } - private OsmandSettings.OsmandPreference getGroupExpandedPreference(String groupName) { - OsmandSettings.OsmandPreference preference = preferenceCache.get(groupName); + private OsmandPreference getGroupExpandedPreference(String groupName) { + OsmandPreference preference = preferenceCache.get(groupName); if (preference == null) { String groupKey = groupName + GROUP_EXPANDED_POSTFIX; preference = getSettings().registerBooleanPreference(groupKey, false); diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 2e840f8b64..122eb9ad7e 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -67,7 +67,6 @@ import net.osmand.plus.AppInitializer; import net.osmand.plus.AppInitializer.AppInitializeListener; import net.osmand.plus.AppInitializer.InitEvents; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; -import net.osmand.plus.HuaweiDrmHelper; import net.osmand.plus.MapMarkersHelper.MapMarker; import net.osmand.plus.MapMarkersHelper.MapMarkerChangedListener; import net.osmand.plus.OnDismissDialogFragmentListener; @@ -75,6 +74,9 @@ import net.osmand.plus.OsmAndConstants; import net.osmand.plus.OsmAndLocationSimulation; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.helpers.DayNightHelper; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; @@ -131,7 +133,6 @@ import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchTab; import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchType; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization.OsmAndAppCustomizationListener; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.plus.settings.fragments.ConfigureProfileFragment; @@ -276,9 +277,6 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven super.onCreate(savedInstanceState); - if (Version.isHuawei(getMyApplication())) { - HuaweiDrmHelper.check(this); - } // Full screen is not used here // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.main); @@ -537,7 +535,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven @Override public void requestPrivateAccessRouting() { if (!settings.FORCE_PRIVATE_ACCESS_ROUTING_ASKED.getModeValue(getRoutingHelper().getAppMode())) { - final OsmandSettings.CommonPreference allowPrivate + final CommonPreference allowPrivate = settings.getCustomRoutingBooleanProperty(GeneralRouter.ALLOW_PRIVATE, false); final List modes = ApplicationMode.values(app); for (ApplicationMode mode : modes) { diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index dbd0f133fa..d9137ee2d6 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -91,6 +91,7 @@ import java.util.Map; import static net.osmand.IndexConstants.GPX_FILE_EXT; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_CONFIGURE_MAP_ID; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_CONFIGURE_PROFILE_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_CONFIGURE_SCREEN_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_DASHBOARD_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_DIRECTIONS_ID; @@ -104,6 +105,7 @@ import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_OSMAND_LIVE import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_PLUGINS_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SEARCH_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SETTINGS_ID; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SWITCH_PROFILE_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_TRAVEL_GUIDES_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_ADD_GPX_WAYPOINT; import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_ADD_ID; @@ -145,7 +147,7 @@ public class MapActivityActions implements DialogProvider { private static final int DIALOG_RELOAD_TITLE = 103; private static final int DIALOG_SAVE_DIRECTIONS = 106; - + private static final int DRAWER_MODE_NORMAL = 0; private static final int DRAWER_MODE_SWITCH_PROFILE = 1; @@ -476,7 +478,7 @@ public class MapActivityActions implements DialogProvider { mapActivity.showQuickSearch(latitude, longitude); } else if (standardId == R.string.context_menu_item_directions_from) { //if (OsmAndLocationProvider.isLocationPermissionAvailable(mapActivity)) { - enterDirectionsFromPoint(latitude, longitude); + enterDirectionsFromPoint(latitude, longitude); //} else { // ActivityCompat.requestPermissions(mapActivity, // new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, @@ -535,17 +537,17 @@ public class MapActivityActions implements DialogProvider { } public void enterRoutePlanningModeGivenGpx(GPXFile gpxFile, LatLon from, PointDescription fromName, - boolean useIntermediatePointsByDefault, boolean showMenu) { + boolean useIntermediatePointsByDefault, boolean showMenu) { enterRoutePlanningModeGivenGpx(gpxFile, from, fromName, useIntermediatePointsByDefault, showMenu, MapRouteInfoMenu.DEFAULT_MENU_STATE); } public void enterRoutePlanningModeGivenGpx(GPXFile gpxFile, LatLon from, PointDescription fromName, - boolean useIntermediatePointsByDefault, boolean showMenu, int menuState) { + boolean useIntermediatePointsByDefault, boolean showMenu, int menuState) { enterRoutePlanningModeGivenGpx(gpxFile, null, from, fromName, useIntermediatePointsByDefault, showMenu, menuState); } public void enterRoutePlanningModeGivenGpx(GPXFile gpxFile, ApplicationMode appMode, LatLon from, PointDescription fromName, - boolean useIntermediatePointsByDefault, boolean showMenu, int menuState) { + boolean useIntermediatePointsByDefault, boolean showMenu, int menuState) { settings.USE_INTERMEDIATE_POINTS_NAVIGATION.set(useIntermediatePointsByDefault); OsmandApplication app = mapActivity.getMyApplication(); TargetPointsHelper targets = app.getTargetPointsHelper(); @@ -727,7 +729,7 @@ public class MapActivityActions implements DialogProvider { private ContextMenuAdapter createSwitchProfileOptionsMenu(final OsmandApplication app, ContextMenuAdapter optionsMenuHelper, boolean nightMode) { drawerMode = DRAWER_MODE_NORMAL; createProfilesController(app, optionsMenuHelper, nightMode, true); - + List activeModes = ApplicationMode.values(app); ApplicationMode currentMode = app.getSettings().APPLICATION_MODE.get(); @@ -759,7 +761,7 @@ public class MapActivityActions implements DialogProvider { }) .createItem()); } - + int activeColorPrimaryResId = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light; optionsMenuHelper.addItem(new ItemBuilder().setLayout(R.layout.profile_list_item) .setColor(activeColorPrimaryResId) @@ -778,7 +780,7 @@ public class MapActivityActions implements DialogProvider { } private ContextMenuAdapter createNormalOptionsMenu(final OsmandApplication app, ContextMenuAdapter optionsMenuHelper, boolean nightMode) { - + createProfilesController(app, optionsMenuHelper, nightMode, false); optionsMenuHelper.addItem(new ItemBuilder().setTitleId(R.string.home, mapActivity) @@ -899,7 +901,7 @@ public class MapActivityActions implements DialogProvider { } }).createItem()); - if (Version.isGooglePlayEnabled(app) || Version.isDeveloperVersion(app)) { + if (Version.isGooglePlayEnabled(app) || Version.isHuawei(app) || Version.isDeveloperVersion(app)) { optionsMenuHelper.addItem(new ItemBuilder().setTitleId(R.string.osm_live, mapActivity) .setId(DRAWER_OSMAND_LIVE_ID) .setIcon(R.drawable.ic_action_osm_live) @@ -1055,6 +1057,7 @@ public class MapActivityActions implements DialogProvider { int icArrowResId = listExpanded ? R.drawable.ic_action_arrow_drop_up : R.drawable.ic_action_arrow_drop_down; final int nextMode = listExpanded ? DRAWER_MODE_NORMAL : DRAWER_MODE_SWITCH_PROFILE; optionsMenuHelper.addItem(new ItemBuilder().setLayout(R.layout.main_menu_drawer_btn_switch_profile) + .setId(DRAWER_SWITCH_PROFILE_ID) .setIcon(currentMode.getIconRes()) .setSecondaryIcon(icArrowResId) .setColor(currentMode.getIconColorInfo().getColor(nightMode)) @@ -1070,6 +1073,7 @@ public class MapActivityActions implements DialogProvider { }) .createItem()); optionsMenuHelper.addItem(new ItemBuilder().setLayout(R.layout.main_menu_drawer_btn_configure_profile) + .setId(DRAWER_CONFIGURE_PROFILE_ID) .setColor(currentMode.getIconColorInfo().getColor(nightMode)) .setTitle(getString(R.string.configure_profile)) .setListener(new ItemClickListener() { @@ -1084,8 +1088,8 @@ public class MapActivityActions implements DialogProvider { } private String getProfileDescription(OsmandApplication app, ApplicationMode mode, - Map profilesObjects, String defaultDescription){ - String description = defaultDescription; + Map profilesObjects, String defaultDescription) { + String description = defaultDescription; String routingProfileKey = mode.getRoutingProfile(); if (!Algorithms.isEmpty(routingProfileKey)) { diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java index 64f3ad8b9f..442e2f41a1 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java @@ -29,7 +29,7 @@ import net.osmand.plus.DialogListItemAdapter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.SQLiteTileSource; import net.osmand.plus.activities.MapActivity.ShowQuickSearchMode; diff --git a/OsmAnd/src/net/osmand/plus/activities/OsmandInAppPurchaseActivity.java b/OsmAnd/src/net/osmand/plus/activities/OsmandInAppPurchaseActivity.java index cb91f7d167..47f7a17444 100644 --- a/OsmAnd/src/net/osmand/plus/activities/OsmandInAppPurchaseActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/OsmandInAppPurchaseActivity.java @@ -5,7 +5,6 @@ import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Intent; import android.net.Uri; -import android.os.Bundle; import android.widget.Toast; import androidx.annotation.NonNull; @@ -14,12 +13,15 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.Version; +import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseInitCallback; import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener; import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType; import net.osmand.plus.liveupdates.OsmLiveRestartBottomSheetDialogFragment; @@ -27,6 +29,7 @@ import net.osmand.plus.srtmplugin.SRTMPlugin; import org.apache.commons.logging.Log; +import java.lang.ref.WeakReference; import java.util.List; @SuppressLint("Registered") @@ -34,14 +37,7 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In private static final Log LOG = PlatformUtil.getLog(OsmandInAppPurchaseActivity.class); private InAppPurchaseHelper purchaseHelper; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isInAppPurchaseAllowed() && isInAppPurchaseSupported()) { - purchaseHelper = getMyApplication().getInAppPurchaseHelper(); - } - } + private boolean activityDestroyed; @Override protected void onResume() { @@ -53,17 +49,39 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In protected void onDestroy() { super.onDestroy(); deinitInAppPurchaseHelper(); + activityDestroyed = true; } private void initInAppPurchaseHelper() { deinitInAppPurchaseHelper(); - - if (purchaseHelper != null) { - purchaseHelper.setUiActivity(this); - if (purchaseHelper.needRequestInventory()) { - purchaseHelper.requestInventory(); + if (purchaseHelper == null) { + OsmandApplication app = getMyApplication(); + InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); + if (app.getSettings().isInternetConnectionAvailable() + && isInAppPurchaseAllowed() + && isInAppPurchaseSupported(purchaseHelper)) { + this.purchaseHelper = purchaseHelper; } } + if (purchaseHelper != null) { + final WeakReference activityRef = new WeakReference<>(this); + purchaseHelper.isInAppPurchaseSupported(this, new InAppPurchaseInitCallback() { + @Override + public void onSuccess() { + OsmandInAppPurchaseActivity activity = activityRef.get(); + if (!activityDestroyed && AndroidUtils.isActivityNotDestroyed(activity)) { + purchaseHelper.setUiActivity(activity); + if (purchaseHelper.needRequestInventory()) { + purchaseHelper.requestInventory(); + } + } + } + + @Override + public void onFail() { + } + }); + } } private void deinitInAppPurchaseHelper() { @@ -80,7 +98,11 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); if (purchaseHelper != null) { app.logEvent("in_app_purchase_redirect"); - purchaseHelper.purchaseFullVersion(activity); + try { + purchaseHelper.purchaseFullVersion(activity); + } catch (UnsupportedOperationException e) { + LOG.error("purchaseFullVersion is not supported", e); + } } } else { app.logEvent("paid_version_redirect"); @@ -101,18 +123,27 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); if (purchaseHelper != null) { app.logEvent("depth_contours_purchase_redirect"); - purchaseHelper.purchaseDepthContours(activity); + try { + purchaseHelper.purchaseDepthContours(activity); + } catch (UnsupportedOperationException e) { + LOG.error("purchaseDepthContours is not supported", e); + } } } } - public static void purchaseSrtmPlugin(@NonNull final Activity activity) { - OsmandPlugin plugin = OsmandPlugin.getPlugin(SRTMPlugin.class); - if(plugin == null || plugin.getInstallURL() == null) { - Toast.makeText(activity.getApplicationContext(), - activity.getString(R.string.activate_srtm_plugin), Toast.LENGTH_LONG).show(); - } else { - activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(plugin.getInstallURL()))); + public static void purchaseContourLines(@NonNull final Activity activity) { + OsmandApplication app = (OsmandApplication) activity.getApplication(); + if (app != null) { + InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); + if (purchaseHelper != null) { + app.logEvent("contour_lines_purchase_redirect"); + try { + purchaseHelper.purchaseContourLines(activity); + } catch (UnsupportedOperationException e) { + LOG.error("purchaseContourLines is not supported", e); + } + } } } @@ -129,8 +160,9 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In return false; } - public boolean isInAppPurchaseSupported() { - return Version.isGooglePlayEnabled(getMyApplication()); + public boolean isInAppPurchaseSupported(InAppPurchaseHelper purchaseHelper) { + OsmandApplication app = getMyApplication(); + return Version.isGooglePlayEnabled(app) || Version.isHuawei(app); } @Override @@ -178,6 +210,11 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In } onInAppPurchaseItemPurchased(sku); fireInAppPurchaseItemPurchasedOnFragments(fragmentManager, sku, active); + if (purchaseHelper != null && purchaseHelper.getContourLines().getSku().equals(sku)) { + if (!(this instanceof MapActivity)) { + finish(); + } + } } public void fireInAppPurchaseItemPurchasedOnFragments(@NonNull FragmentManager fragmentManager, @@ -222,6 +259,17 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In } } + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + boolean handled = false; + if (purchaseHelper != null) { + handled = purchaseHelper.onActivityResult(this, requestCode, resultCode, data); + } + if (!handled) { + super.onActivityResult(requestCode, resultCode, data); + } + } + public void onInAppPurchaseError(InAppPurchaseTaskType taskType, String error) { // not implemented } diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java index 2187e801a2..316de777b0 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsBaseActivity.java @@ -25,13 +25,13 @@ import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.profiles.AppProfileArrayAdapter; import net.osmand.plus.profiles.ProfileDataObject; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; import org.apache.commons.logging.Log; diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsGeneralActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsGeneralActivity.java index aaf206ca06..225384349c 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsGeneralActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsGeneralActivity.java @@ -42,9 +42,9 @@ import net.osmand.osm.io.NetworkUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.AngularConstants; -import net.osmand.plus.settings.backend.OsmandSettings.DrivingRegion; -import net.osmand.plus.settings.backend.OsmandSettings.MetricsConstants; +import net.osmand.plus.helpers.enums.AngularConstants; +import net.osmand.plus.helpers.enums.DrivingRegion; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.base.MapViewTrackingUtilities; diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java index 260fd63719..a0a5c74992 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java @@ -34,6 +34,12 @@ import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.helpers.enums.AutoZoomMap; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.helpers.enums.SpeedConstants; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.Version; @@ -44,10 +50,6 @@ import net.osmand.plus.routepreparationmenu.RoutingOptionsHelper; import net.osmand.plus.routing.RouteProvider.RouteService; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.AutoZoomMap; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; -import net.osmand.plus.settings.backend.OsmandSettings.SpeedConstants; import net.osmand.plus.voice.CommandPlayer; import net.osmand.router.GeneralRouter; import net.osmand.router.GeneralRouter.GeneralRouterProfile; @@ -171,7 +173,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity { //array size must be equal! Float[] speedLimitsKmhPos = new Float[]{0f, 5f, 7f, 10f, 15f, 20f}; Float[] speedLimitsMphPos = new Float[]{0f, 3f, 5f, 7f, 10f, 15f}; - if (settings.METRIC_SYSTEM.get() == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS) { + if (settings.METRIC_SYSTEM.get() == MetricsConstants.KILOMETERS_AND_METERS) { String[] speedNames = new String[speedLimitsKmh.length]; String[] speedNamesPos = new String[speedLimitsKmhPos.length]; for (int i = 0; i < speedLimitsKmh.length; i++) { @@ -407,7 +409,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity { } public static boolean isRoutingParameterSelected(OsmandSettings settings, ApplicationMode am, RoutingParameter routingParameter) { - final OsmandSettings.CommonPreference property = + final CommonPreference property = settings.getCustomRoutingBooleanProperty(routingParameter.getId(), routingParameter.getDefaultBoolean()); if(am != null) { return property.getModeValue(am); @@ -417,7 +419,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity { } public static void setRoutingParameterSelected(OsmandSettings settings, ApplicationMode am, String routingParameterId, boolean defaultBoolean, boolean isChecked) { - final OsmandSettings.CommonPreference property = settings.getCustomRoutingBooleanProperty(routingParameterId, defaultBoolean); + final CommonPreference property = settings.getCustomRoutingBooleanProperty(routingParameterId, defaultBoolean); if (am != null) { property.setModeValue(am, isChecked); } else { diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index de3453550f..a68a8bc749 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -53,8 +53,8 @@ import net.osmand.plus.ContextMenuAdapter.ItemClickListener; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; diff --git a/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java b/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java index 09acdca8d8..8e911b04fc 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/NotesFragment.java @@ -38,7 +38,6 @@ import net.osmand.PlatformUtil; import net.osmand.data.PointDescription; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandSettings.NotesSortByMode; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.ActionBarProgressActivity; diff --git a/OsmAnd/src/net/osmand/plus/audionotes/NotesSortByMode.java b/OsmAnd/src/net/osmand/plus/audionotes/NotesSortByMode.java new file mode 100644 index 0000000000..cf791dbf26 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/audionotes/NotesSortByMode.java @@ -0,0 +1,14 @@ +package net.osmand.plus.audionotes; + +public enum NotesSortByMode { + BY_TYPE, + BY_DATE; + + public boolean isByType() { + return this == BY_TYPE; + } + + public boolean isByDate() { + return this == BY_DATE; + } +} diff --git a/OsmAnd/src/net/osmand/plus/audionotes/SortByMenuBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/audionotes/SortByMenuBottomSheetDialogFragment.java index de1208a735..06fa83f6ca 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/SortByMenuBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/SortByMenuBottomSheetDialogFragment.java @@ -3,8 +3,7 @@ package net.osmand.plus.audionotes; import android.os.Bundle; import android.view.View; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.NotesSortByMode; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; @@ -53,7 +52,7 @@ public class SortByMenuBottomSheetDialogFragment extends MenuBottomSheetDialogFr } private void selectSortByMode(NotesSortByMode mode) { - final OsmandSettings.CommonPreference sortByMode = getMyApplication().getSettings().NOTES_SORT_BY_MODE; + final CommonPreference sortByMode = getMyApplication().getSettings().NOTES_SORT_BY_MODE; if (sortByMode.get() != mode) { sortByMode.set(mode); if (listener != null) { diff --git a/OsmAnd/src/net/osmand/plus/base/MenuBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/base/MenuBottomSheetDialogFragment.java index 7625e5de20..307cfb3bbe 100644 --- a/OsmAnd/src/net/osmand/plus/base/MenuBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/base/MenuBottomSheetDialogFragment.java @@ -8,11 +8,11 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.os.Bundle; -import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnScrollChangedListener; import android.view.Window; import android.view.WindowManager; import android.widget.LinearLayout; @@ -50,8 +50,11 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra protected int themeRes; protected View dismissButton; protected View rightButton; + protected View thirdButton; + private View buttonsShadow; private LinearLayout itemsContainer; + private LinearLayout buttonsContainer; @StringRes protected int dismissButtonStringRes = R.string.shared_string_cancel; @@ -74,45 +77,21 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { createMenuItems(savedInstanceState); - Context ctx = requireContext(); - View mainView = View.inflate(new ContextThemeWrapper(ctx, themeRes), R.layout.bottom_sheet_menu_base, null); + Activity activity = requireActivity(); + LayoutInflater themedInflater = UiUtilities.getInflater(activity, nightMode); + View mainView = themedInflater.inflate(R.layout.bottom_sheet_menu_base, null); if (useScrollableItemsContainer()) { - itemsContainer = (LinearLayout) mainView.findViewById(R.id.scrollable_items_container); + itemsContainer = mainView.findViewById(R.id.scrollable_items_container); } else { mainView.findViewById(R.id.scroll_view).setVisibility(View.GONE); - itemsContainer = (LinearLayout) mainView.findViewById(R.id.non_scrollable_items_container); + itemsContainer = mainView.findViewById(R.id.non_scrollable_items_container); itemsContainer.setVisibility(View.VISIBLE); } + buttonsShadow = mainView.findViewById(R.id.buttons_shadow); inflateMenuItems(); - - dismissButton = mainView.findViewById(R.id.dismiss_button); - UiUtilities.setupDialogButton(nightMode, dismissButton, getDismissButtonType(), getDismissButtonTextId()); - dismissButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onDismissButtonClickAction(); - dismiss(); - } - }); - if (hideButtonsContainer()) { - mainView.findViewById(R.id.buttons_container).setVisibility(View.GONE); - } else { - int rightBottomButtonTextId = getRightBottomButtonTextId(); - if (rightBottomButtonTextId != DEFAULT_VALUE) { - mainView.findViewById(R.id.buttons_divider).setVisibility(View.VISIBLE); - rightButton = mainView.findViewById(R.id.right_bottom_button); - UiUtilities.setupDialogButton(nightMode, rightButton, getRightBottomButtonType(), rightBottomButtonTextId); - rightButton.setVisibility(View.VISIBLE); - rightButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onRightBottomButtonClick(); - } - }); - } - } - updateBottomButtons(); + setupScrollShadow(mainView); + setupBottomButtons((ViewGroup) mainView); setupHeightAndBackground(mainView); return mainView; } @@ -199,7 +178,7 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra if (contentView.getHeight() > contentHeight) { if (useScrollableItemsContainer() || useExpandableList()) { contentView.getLayoutParams().height = contentHeight; - mainView.findViewById(R.id.buttons_shadow).setVisibility(View.VISIBLE); + buttonsShadow.setVisibility(View.VISIBLE); } else { contentView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; } @@ -222,7 +201,18 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra private int getContentHeight(int availableScreenHeight) { int customHeight = getCustomHeight(); - int maxHeight = availableScreenHeight - getResources().getDimensionPixelSize(R.dimen.dialog_button_ex_height); + int buttonsHeight; + if (useVerticalButtons()) { + int padding = getResources().getDimensionPixelSize(R.dimen.content_padding_small); + int buttonHeight = getResources().getDimensionPixelSize(R.dimen.dialog_button_height); + buttonsHeight = (buttonHeight + padding) * 2 + getFirstDividerHeight(); + if (getThirdBottomButtonTextId() != DEFAULT_VALUE) { + buttonsHeight += buttonHeight + getSecondDividerHeight(); + } + } else { + buttonsHeight = getResources().getDimensionPixelSize(R.dimen.dialog_button_ex_height); + } + int maxHeight = availableScreenHeight - buttonsHeight; if (customHeight != DEFAULT_VALUE && customHeight <= maxHeight) { return customHeight; } @@ -280,6 +270,18 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra } + protected int getThirdBottomButtonTextId() { + return DEFAULT_VALUE; + } + + protected DialogButtonType getThirdBottomButtonType() { + return DialogButtonType.PRIMARY; + } + + protected void onThirdBottomButtonClick() { + + } + protected boolean isDismissButtonEnabled() { return true; } @@ -288,6 +290,42 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra return true; } + protected void setupBottomButtons(ViewGroup view) { + Activity activity = requireActivity(); + LayoutInflater themedInflater = UiUtilities.getInflater(activity, nightMode); + if (!hideButtonsContainer()) { + if (useVerticalButtons()) { + buttonsContainer = (LinearLayout) themedInflater.inflate(R.layout.bottom_buttons_vertical, view); + setupThirdButton(); + } else { + buttonsContainer = (LinearLayout) themedInflater.inflate(R.layout.bottom_buttons, view); + } + setupRightButton(); + setupDismissButton(); + updateBottomButtons(); + } + } + + boolean useVerticalButtons() { + Activity activity = requireActivity(); + int rightBottomButtonTextId = getRightBottomButtonTextId(); + if (getDismissButtonTextId() != DEFAULT_VALUE && rightBottomButtonTextId != DEFAULT_VALUE) { + if (getThirdBottomButtonTextId() != DEFAULT_VALUE) { + return true; + } + String rightButtonText = getString(rightBottomButtonTextId); + boolean portrait = AndroidUiHelper.isOrientationPortrait(activity); + int outerPadding = getResources().getDimensionPixelSize(R.dimen.content_padding); + int innerPadding = getResources().getDimensionPixelSize(R.dimen.content_padding_small); + int dialogWidth = portrait ? AndroidUtils.getScreenWidth(activity) : getResources().getDimensionPixelSize(R.dimen.landscape_bottom_sheet_dialog_fragment_width); + int availableTextWidth = (dialogWidth - (outerPadding * 3 + innerPadding * 4)) / 2; + + int measuredTextWidth = AndroidUtils.getTextWidth(getResources().getDimensionPixelSize(R.dimen.default_desc_text_size), rightButtonText); + return measuredTextWidth > availableTextWidth; + } + return false; + } + protected void updateBottomButtons() { if (dismissButton != null) { boolean enabled = isDismissButtonEnabled(); @@ -301,6 +339,66 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra } } + private void setupDismissButton() { + dismissButton = buttonsContainer.findViewById(R.id.dismiss_button); + int buttonTextId = getDismissButtonTextId(); + if (buttonTextId != DEFAULT_VALUE) { + UiUtilities.setupDialogButton(nightMode, dismissButton, getDismissButtonType(), buttonTextId); + dismissButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onDismissButtonClickAction(); + dismiss(); + } + }); + } + AndroidUiHelper.updateVisibility(dismissButton, buttonTextId != DEFAULT_VALUE); + } + + private void setupRightButton() { + rightButton = buttonsContainer.findViewById(R.id.right_bottom_button); + int buttonTextId = getRightBottomButtonTextId(); + if (buttonTextId != DEFAULT_VALUE) { + UiUtilities.setupDialogButton(nightMode, rightButton, getRightBottomButtonType(), buttonTextId); + rightButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onRightBottomButtonClick(); + } + }); + } + View divider = buttonsContainer.findViewById(R.id.buttons_divider); + divider.getLayoutParams().height = getFirstDividerHeight(); + AndroidUiHelper.updateVisibility(rightButton, buttonTextId != DEFAULT_VALUE); + AndroidUiHelper.updateVisibility(divider, buttonTextId != DEFAULT_VALUE); + } + + protected int getFirstDividerHeight() { + return getResources().getDimensionPixelSize(R.dimen.content_padding); + } + + private void setupThirdButton() { + thirdButton = buttonsContainer.findViewById(R.id.third_button); + int buttonTextId = getThirdBottomButtonTextId(); + if (buttonTextId != DEFAULT_VALUE) { + UiUtilities.setupDialogButton(nightMode, thirdButton, getThirdBottomButtonType(), buttonTextId); + thirdButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onThirdBottomButtonClick(); + } + }); + } + View divider = buttonsContainer.findViewById(R.id.buttons_divider_top); + divider.getLayoutParams().height = getSecondDividerHeight(); + AndroidUiHelper.updateVisibility(thirdButton, buttonTextId != DEFAULT_VALUE); + AndroidUiHelper.updateVisibility(divider, buttonTextId != DEFAULT_VALUE); + } + + protected int getSecondDividerHeight() { + return getResources().getDimensionPixelSize(R.dimen.content_padding); + } + @ColorRes protected int getBgColorId() { return nightMode ? R.color.list_background_color_dark : R.color.list_background_color_light; @@ -325,7 +423,7 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra private LayerDrawable createBackgroundDrawable(@NonNull Context ctx, @DrawableRes int shadowDrawableResId) { Drawable shadowDrawable = ContextCompat.getDrawable(ctx, shadowDrawableResId); - Drawable[] layers = new Drawable[]{shadowDrawable, getColoredBg(ctx)}; + Drawable[] layers = new Drawable[] {shadowDrawable, getColoredBg(ctx)}; return new LayerDrawable(layers); } @@ -335,4 +433,21 @@ public abstract class MenuBottomSheetDialogFragment extends BottomSheetDialogFra } return !app.getSettings().isLightContent(); } -} + + private void setupScrollShadow(View view) { + final View scrollView; + if (useScrollableItemsContainer()) { + scrollView = view.findViewById(R.id.scroll_view); + } else { + scrollView = itemsContainer; + } + scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() { + + @Override + public void onScrollChanged() { + boolean scrollToBottomAvailable = scrollView.canScrollVertically(1); + AndroidUiHelper.updateVisibility(buttonsShadow, scrollToBottomAvailable); + } + }); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java b/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java index 1e3b650a8f..9a7215867f 100644 --- a/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java @@ -215,6 +215,9 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment if (!TextUtils.isEmpty(getInfoDescription())) { infoDescription.setText(getInfoDescription()); } + TextViewEx planInfoDescription = (TextViewEx) view.findViewById(R.id.plan_info_description); + planInfoDescription.setText(Version.isHuawei(app) + ? R.string.osm_live_payment_subscription_management_hw : R.string.osm_live_payment_subscription_management); ViewGroup osmLiveCard = buildOsmLiveCard(ctx, cardsContainer); if (osmLiveCard != null) { cardsContainer.addView(osmLiveCard); @@ -428,7 +431,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment buttonCancelView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - manageSubscription(ctx, s.getSku()); + purchaseHelper.manageSubscription(ctx, s.getSku()); } }); div.setVisibility(View.VISIBLE); @@ -538,15 +541,6 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment } } - private void manageSubscription(@NonNull Context ctx, @Nullable String sku) { - String url = "https://play.google.com/store/account/subscriptions?package=" + ctx.getPackageName(); - if (!Algorithms.isEmpty(sku)) { - url += "&sku=" + sku; - } - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - startActivity(intent); - } - private ViewGroup buildPlanTypeCard(@NonNull Context ctx, ViewGroup container) { if (getPlanTypeFeatures().length == 0) { return null; diff --git a/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanHillshadeSrtmDialogFragment.java b/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanHillshadeSrtmDialogFragment.java index b2858b53b8..d3a48a5a15 100644 --- a/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanHillshadeSrtmDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanHillshadeSrtmDialogFragment.java @@ -79,7 +79,7 @@ public class ChoosePlanHillshadeSrtmDialogFragment extends ChoosePlanDialogFragm public void onClick(View v) { Activity activity = getActivity(); if (activity != null) { - OsmandInAppPurchaseActivity.purchaseSrtmPlugin(activity); + OsmandInAppPurchaseActivity.purchaseContourLines(activity); dismiss(); } } diff --git a/OsmAnd/src/net/osmand/plus/chooseplan/OsmLiveCancelledDialog.java b/OsmAnd/src/net/osmand/plus/chooseplan/OsmLiveCancelledDialog.java index 788b605eec..a62ffcd296 100644 --- a/OsmAnd/src/net/osmand/plus/chooseplan/OsmLiveCancelledDialog.java +++ b/OsmAnd/src/net/osmand/plus/chooseplan/OsmLiveCancelledDialog.java @@ -23,8 +23,9 @@ import androidx.fragment.app.FragmentManager; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.Version; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.BaseOsmAndDialogFragment; @@ -110,6 +111,8 @@ public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements descr.append("\n").append("— ").append(feature.toHumanString(ctx)); } infoDescr.setText(descr); + TextViewEx inappDescr = (TextViewEx) view.findViewById(R.id.inapp_descr); + inappDescr.setText(Version.isHuawei(app) ? R.string.osm_live_payment_desc_hw : R.string.osm_live_payment_desc); osmLiveButton = view.findViewById(R.id.card_button); diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java b/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java index 9f37cd1b41..883010ea46 100644 --- a/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java +++ b/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java @@ -30,6 +30,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import net.osmand.AndroidUtils; +import net.osmand.FileUtils; import net.osmand.ValueHolder; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; @@ -113,7 +114,7 @@ public class DashChooseAppDirFragment { boolean copyFiles = !currentAppFile.getAbsolutePath().equals(selectedFile.getAbsolutePath()) && !mapsCopied; warningReadonly.setVisibility(copyFiles ? View.VISIBLE : View.GONE); if (copyFiles) { - if (!OsmandSettings.isWritable(currentAppFile)) { + if (!FileUtils.isWritable(currentAppFile)) { warningReadonly.setText(activity.getString(R.string.android_19_location_disabled, currentAppFile.getAbsolutePath())); } else { @@ -385,7 +386,7 @@ public class DashChooseAppDirFragment { @Override public void onClick(View v) { - boolean wr = OsmandSettings.isWritable(selectedFile); + boolean wr = FileUtils.isWritable(selectedFile); if (wr) { boolean changed = !currentAppFile.getAbsolutePath().equals(selectedFile.getAbsolutePath()); getMyApplication().setExternalStorageDirectory(type, selectedFile.getAbsolutePath()); diff --git a/OsmAnd/src/net/osmand/plus/dashboard/tools/DashboardSettingsDialogFragment.java b/OsmAnd/src/net/osmand/plus/dashboard/tools/DashboardSettingsDialogFragment.java index 1689a4e298..725a8fefc6 100644 --- a/OsmAnd/src/net/osmand/plus/dashboard/tools/DashboardSettingsDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/dashboard/tools/DashboardSettingsDialogFragment.java @@ -24,7 +24,7 @@ import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; diff --git a/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java b/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java index 7a3503e004..bad57f326b 100644 --- a/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java @@ -35,12 +35,7 @@ public class DevelopmentSettingsFragment extends BaseSettingsFragment { setupOpenglRenderPref(); setupSafeModePref(); - setupPTSafeMode(); - setupDisableComplexRoutingPref(); - setupFastRecalculationPref(); - setupOsmLiveForRoutingPref(); - setupOsmLiveForPublicTransportPref(); setupSimulateYourLocationPref(); Preference debuggingAndDevelopment = findPreference("debugging_and_development"); @@ -62,13 +57,17 @@ public class DevelopmentSettingsFragment extends BaseSettingsFragment { } private void setupOpenglRenderPref() { - SwitchPreferenceEx useOpenglRender = (SwitchPreferenceEx) findPreference(settings.USE_OPENGL_RENDER.getId()); - useOpenglRender.setDescription(getString(R.string.use_opengl_render_descr)); - useOpenglRender.setIconSpaceReserved(false); + SwitchPreferenceEx useOpenglRender = findPreference(settings.USE_OPENGL_RENDER.getId()); + if (Version.isOpenGlAvailable(app)) { + useOpenglRender.setDescription(getString(R.string.use_opengl_render_descr)); + useOpenglRender.setIconSpaceReserved(false); + } else { + useOpenglRender.setVisible(false); + } } private void setupSafeModePref() { - SwitchPreferenceEx safeMode = (SwitchPreferenceEx) findPreference(settings.SAFE_MODE.getId()); + SwitchPreferenceEx safeMode = findPreference(settings.SAFE_MODE.getId()); if (!Version.isBlackberry(app)) { safeMode.setDescription(getString(R.string.safe_mode_description)); safeMode.setIconSpaceReserved(false); @@ -82,40 +81,6 @@ public class DevelopmentSettingsFragment extends BaseSettingsFragment { } } - private void setupPTSafeMode() { - SwitchPreferenceEx ptSafeMode = (SwitchPreferenceEx) findPreference(settings.PT_SAFE_MODE.getId()); - if (!Version.isBlackberry(app)) { - ptSafeMode.setDescription("Switch to Java (safe) Public Transport routing calculation"); - ptSafeMode.setIconSpaceReserved(false); - } else { - ptSafeMode.setVisible(false); - } - } - - private void setupDisableComplexRoutingPref() { - SwitchPreferenceEx disableComplexRouting = (SwitchPreferenceEx) findPreference(settings.DISABLE_COMPLEX_ROUTING.getId()); - disableComplexRouting.setDescription(getString(R.string.disable_complex_routing_descr)); - disableComplexRouting.setIconSpaceReserved(false); - } - - private void setupFastRecalculationPref() { - SwitchPreferenceEx useFastRecalculation = (SwitchPreferenceEx) findPreference(settings.USE_FAST_RECALCULATION.getId()); - useFastRecalculation.setDescription(getString(R.string.use_fast_recalculation_desc)); - useFastRecalculation.setIconSpaceReserved(false); - } - - private void setupOsmLiveForRoutingPref() { - SwitchPreferenceEx useOsmLiveForRouting = (SwitchPreferenceEx) findPreference(settings.USE_OSM_LIVE_FOR_ROUTING.getId()); - useOsmLiveForRouting.setDescription(getString(R.string.use_osm_live_routing_description)); - useOsmLiveForRouting.setIconSpaceReserved(false); - } - - private void setupOsmLiveForPublicTransportPref() { - SwitchPreferenceEx useOsmLiveForPublicTransport = (SwitchPreferenceEx) findPreference(settings.USE_OSM_LIVE_FOR_PUBLIC_TRANSPORT.getId()); - useOsmLiveForPublicTransport.setDescription(getString(R.string.use_osm_live_public_transport_description)); - useOsmLiveForPublicTransport.setIconSpaceReserved(false); - } - private void setupSimulateYourLocationPref() { final Preference simulateYourLocation = findPreference(SIMULATE_YOUR_LOCATION); simulateYourLocation.setIconSpaceReserved(false); diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java index b5ea815bed..c500a28254 100644 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java @@ -14,11 +14,11 @@ import android.preference.PreferenceScreen; import net.osmand.plus.OsmAndLocationSimulation; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.SettingsBaseActivity; import net.osmand.plus.render.NativeOsmandLibrary; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.SunriseSunset; import java.text.SimpleDateFormat; @@ -34,41 +34,27 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { app.applyTheme(this); super.onCreate(savedInstanceState); getToolbar().setTitle(R.string.debugging_and_development); - PreferenceScreen cat = getPreferenceScreen(); + PreferenceScreen category = getPreferenceScreen(); Preference pref; - cat.addPreference(createCheckBoxPreference(settings.USE_OPENGL_RENDER, - R.string.use_opengl_render, R.string.use_opengl_render_descr)); + if (Version.isOpenGlAvailable(app)) { + category.addPreference(createCheckBoxPreference(settings.USE_OPENGL_RENDER, + R.string.use_opengl_render, R.string.use_opengl_render_descr)); + } if (!Version.isBlackberry(app)) { CheckBoxPreference nativeCheckbox = createCheckBoxPreference(settings.SAFE_MODE, R.string.safe_mode, R.string.safe_mode_description); - CheckBoxPreference nativePTCheckbox = createCheckBoxPreference(settings.PT_SAFE_MODE, "Native PT routing development", "Switch to Java (safe) Public Transport routing calculation"); // disable the checkbox if the library cannot be used if ((NativeOsmandLibrary.isLoaded() && !NativeOsmandLibrary.isSupported()) || settings.NATIVE_RENDERING_FAILED.get()) { nativeCheckbox.setEnabled(false); nativeCheckbox.setChecked(true); } - cat.addPreference(nativeCheckbox); - cat.addPreference(nativePTCheckbox); + category.addPreference(nativeCheckbox); } PreferenceCategory navigation = new PreferenceCategory(this); navigation.setTitle(R.string.routing_settings); - cat.addPreference(navigation); - navigation.addPreference(createCheckBoxPreference(settings.DISABLE_COMPLEX_ROUTING, - R.string.disable_complex_routing, R.string.disable_complex_routing_descr)); - - navigation.addPreference(createCheckBoxPreference(settings.USE_FAST_RECALCULATION, - R.string.use_fast_recalculation, R.string.use_fast_recalculation_desc)); - - navigation.addPreference(createCheckBoxPreference(settings.USE_OSM_LIVE_FOR_ROUTING, - R.string.use_osm_live_routing, - R.string.use_osm_live_routing_description)); - - navigation.addPreference(createCheckBoxPreference(settings.USE_OSM_LIVE_FOR_PUBLIC_TRANSPORT, - R.string.use_osm_live_public_transport, - R.string.use_osm_live_public_transport_description)); - + category.addPreference(navigation); pref = new Preference(this); final Preference simulate = pref; final OsmAndLocationSimulation sim = getMyApplication().getLocationProvider().getLocationSimulation(); @@ -95,7 +81,7 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { PreferenceCategory debug = new PreferenceCategory(this); debug.setTitle(R.string.debugging_and_development); - cat.addPreference(debug); + category.addPreference(debug); CheckBoxPreference dbg = createCheckBoxPreference(settings.DEBUG_RENDERING_INFO, R.string.trace_rendering, R.string.trace_rendering_descr); @@ -125,10 +111,6 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { debug.addPreference(createCheckBoxPreference(settings.SHOULD_SHOW_FREE_VERSION_BANNER, R.string.show_free_version_banner, R.string.show_free_version_banner_description)); - - - - pref = new Preference(this); pref.setTitle(R.string.test_voice_prompts); @@ -141,7 +123,7 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { return true; } }); - cat.addPreference(pref); + category.addPreference(pref); pref = new Preference(this); pref.setTitle(R.string.logcat_buffer); @@ -154,11 +136,11 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { return true; } }); - cat.addPreference(pref); + category.addPreference(pref); PreferenceCategory info = new PreferenceCategory(this); info.setTitle(R.string.info_button); - cat.addPreference(info); + category.addPreference(info); pref = new Preference(this); pref.setTitle(R.string.global_app_allocated_memory); diff --git a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java index f1cddde833..a9a0ba563e 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java @@ -19,7 +19,7 @@ import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.activities.OsmandActionBarActivity; import net.osmand.plus.routing.data.StreetName; @@ -151,7 +151,7 @@ public class TestVoiceActivity extends OsmandActionBarActivity { v += "\n \u25CF BT SCO: The current app profile is not set to use 'Phone call audio'."; } - //OsmandSettings.OsmandPreference pref = ((OsmandApplication) getApplication()).getSettings().VOICE_PROMPT_DELAY[stream]; + //OsmandPreference pref = ((OsmandApplication) getApplication()).getSettings().VOICE_PROMPT_DELAY[stream]; //if(pref != null) { // v += "\n \u25CF Voice prompt delay for selected output: " + pref.get() + "\u00A0ms"; //} @@ -299,7 +299,7 @@ public class TestVoiceActivity extends OsmandActionBarActivity { } if (description.startsWith("\u25BA (11.2)")) { int ams = ((OsmandApplication) getApplication()).getSettings().AUDIO_MANAGER_STREAM.get(); - OsmandSettings.OsmandPreference pref = ((OsmandApplication) getApplication()).getSettings().VOICE_PROMPT_DELAY[ams]; + OsmandPreference pref = ((OsmandApplication) getApplication()).getSettings().VOICE_PROMPT_DELAY[ams]; if (pref != null) { if (pref.get() >= 3000) { pref.set(0); diff --git a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java index 83c4e1942d..1c01808be8 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java @@ -33,6 +33,11 @@ import net.osmand.plus.ContextMenuItem; import net.osmand.plus.DialogListItemAdapter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.helpers.enums.DayNightMode; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.ListStringPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -40,9 +45,6 @@ import net.osmand.plus.activities.SettingsActivity; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.render.RendererRegistry; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings.ListStringPreference; import net.osmand.plus.srtmplugin.SRTMPlugin; import net.osmand.plus.transport.TransportLinesMenu; import net.osmand.plus.views.OsmandMapTileView; @@ -307,7 +309,7 @@ public class ConfigureMapMenu { DateFormat dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT); String sunriseTime = dateFormat.format(sunriseSunset.getSunrise()); String sunsetTime = dateFormat.format(sunriseSunset.getSunset()); - OsmandSettings.DayNightMode dayNightMode = activity.getMyApplication().getSettings().DAYNIGHT_MODE.get(); + DayNightMode dayNightMode = activity.getMyApplication().getSettings().DAYNIGHT_MODE.get(); if (dayNightMode.isDay() || dayNightMode.isNight()) { if (sunriseSunset.isDaytime()) { description = String.format(app.getString(R.string.sunset_at), sunsetTime); @@ -334,9 +336,9 @@ public class ConfigureMapMenu { final OsmandMapTileView view = activity.getMapView(); AlertDialog.Builder bld = new AlertDialog.Builder(new ContextThemeWrapper(view.getContext(), themeRes)); bld.setTitle(R.string.daynight); - final String[] items = new String[OsmandSettings.DayNightMode.values().length]; + final String[] items = new String[DayNightMode.values().length]; for (int i = 0; i < items.length; i++) { - items[i] = OsmandSettings.DayNightMode.values()[i].toHumanString(app); + items[i] = DayNightMode.values()[i].toHumanString(app); } int i = view.getSettings().DAYNIGHT_MODE.get().ordinal(); bld.setNegativeButton(R.string.shared_string_dismiss, null); @@ -345,7 +347,7 @@ public class ConfigureMapMenu { @Override public void onClick(View v) { int which = (int) v.getTag(); - view.getSettings().DAYNIGHT_MODE.set(OsmandSettings.DayNightMode.values()[which]); + view.getSettings().DAYNIGHT_MODE.set(DayNightMode.values()[which]); refreshMapComplete(activity); activity.getDashboard().refreshContent(true); // adapter.getItem(pos).setDescription(s, getDayNightDescr(activity)); @@ -376,7 +378,7 @@ public class ConfigureMapMenu { return false; } final OsmandMapTileView view = activity.getMapView(); - final OsmandSettings.OsmandPreference mapDensity = view.getSettings().MAP_DENSITY; + final OsmandPreference mapDensity = view.getSettings().MAP_DENSITY; AlertDialog.Builder bld = new AlertDialog.Builder(new ContextThemeWrapper(view.getContext(), themeRes)); int p = (int) (mapDensity.get() * 100); final TIntArrayList tlist = new TIntArrayList(new int[]{25, 33, 50, 75, 100, 125, 150, 200, 300, 400}); @@ -669,25 +671,25 @@ public class ConfigureMapMenu { @ColorInt final int selectedProfileColor) { final List ps = new ArrayList<>(); - final List> prefs = new ArrayList<>(); + final List> prefs = new ArrayList<>(); Iterator it = customRules.iterator(); while (it.hasNext()) { RenderingRuleProperty p = it.next(); if (category.equals(p.getCategory()) && p.isBoolean()) { ps.add(p); - final OsmandSettings.CommonPreference pref = activity.getMyApplication().getSettings() + final CommonPreference pref = activity.getMyApplication().getSettings() .getCustomRenderBooleanProperty(p.getAttrName()); prefs.add(pref); it.remove(); } } if (prefs.size() > 0) { - final List> includedPrefs = new ArrayList<>(); + final List> includedPrefs = new ArrayList<>(); if (customRulesIncluded != null) { for (RenderingRuleProperty p : customRulesIncluded) { if (!p.isBoolean()) { - final OsmandSettings.CommonPreference pref = activity.getMyApplication().getSettings() + final CommonPreference pref = activity.getMyApplication().getSettings() .getCustomRenderProperty(p.getAttrName()); includedPrefs.add(pref); } @@ -729,14 +731,14 @@ public class ConfigureMapMenu { .setId(id) .setIcon(icon).setListener(clickListener); boolean selected = false; - for (OsmandSettings.CommonPreference p : prefs) { + for (CommonPreference p : prefs) { if (p.get()) { selected = true; break; } } if (!selected && includedPrefs.size() > 0) { - for (OsmandSettings.CommonPreference p : includedPrefs) { + for (CommonPreference p : includedPrefs) { if (!Algorithms.isEmpty(p.get())) { selected = true; break; @@ -773,17 +775,17 @@ public class ConfigureMapMenu { return null; } - protected String getDescription(final List> prefs, - final List> includedPrefs) { + protected String getDescription(final List> prefs, + final List> includedPrefs) { int count = 0; int enabled = 0; - for (OsmandSettings.CommonPreference p : prefs) { + for (CommonPreference p : prefs) { count++; if (p.get()) { enabled++; } } - for (OsmandSettings.CommonPreference p : includedPrefs) { + for (CommonPreference p : includedPrefs) { count++; if (!Algorithms.isEmpty(p.get())) { enabled++; @@ -856,11 +858,11 @@ public class ConfigureMapMenu { prefs.get(i).set(tempPrefs[i]); selected |= tempPrefs[i]; } - final List> includedPrefs = new ArrayList<>(); + final List> includedPrefs = new ArrayList<>(); if (customRulesIncluded != null) { for (RenderingRuleProperty p : customRulesIncluded) { if (p.getAttrName().equals(HIKING_ROUTES_OSMC_ATTR)) { - final OsmandSettings.CommonPreference pref = activity.getMyApplication().getSettings() + final CommonPreference pref = activity.getMyApplication().getSettings() .getCustomRenderProperty(p.getAttrName()); includedPrefs.add(pref); if (hikingRouteOSMCValue == 0) { @@ -894,7 +896,7 @@ public class ConfigureMapMenu { if (customRulesIncluded != null) { for (RenderingRuleProperty p : customRulesIncluded) { if (!p.isBoolean()) { - final OsmandSettings.CommonPreference pref = activity.getMyApplication().getSettings() + final CommonPreference pref = activity.getMyApplication().getSettings() .getCustomRenderProperty(p.getAttrName()); View spinnerView = View.inflate(new ContextThemeWrapper(activity, themeRes), R.layout.spinner_rule_layout, null); @@ -1031,7 +1033,7 @@ public class ConfigureMapMenu { final String propertyDescr = SettingsActivity.getStringPropertyDescription(view.getContext(), p.getAttrName(), p.getName()); if (p.isBoolean()) { - final OsmandSettings.CommonPreference pref = view.getApplication().getSettings() + final CommonPreference pref = view.getApplication().getSettings() .getCustomRenderBooleanProperty(p.getAttrName()); return ContextMenuItem.createBuilder(propertyName) .setId(id) @@ -1047,7 +1049,7 @@ public class ConfigureMapMenu { .setSelected(pref.get()) .createItem(); } else { - final OsmandSettings.CommonPreference pref = view.getApplication().getSettings() + final CommonPreference pref = view.getApplication().getSettings() .getCustomRenderProperty(p.getAttrName()); final String descr; if (!Algorithms.isEmpty(pref.get())) { diff --git a/OsmAnd/src/net/osmand/plus/dialogs/DetailsBottomSheet.java b/OsmAnd/src/net/osmand/plus/dialogs/DetailsBottomSheet.java index 4e8febb56e..ecb8f04b86 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/DetailsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/DetailsBottomSheet.java @@ -27,7 +27,7 @@ import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemTwoChoicesButton.OnBo import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerItem; import net.osmand.plus.helpers.FontCache; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.bottomsheets.BasePreferenceBottomSheet; import net.osmand.render.RenderingRuleProperty; import net.osmand.render.RenderingRuleStorageProperties; diff --git a/OsmAnd/src/net/osmand/plus/dialogs/RasterMapMenu.java b/OsmAnd/src/net/osmand/plus/dialogs/RasterMapMenu.java index 534c334aec..8219cc5a23 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/RasterMapMenu.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/RasterMapMenu.java @@ -10,8 +10,9 @@ import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.LayerTransparencySeekbarMode; +import net.osmand.plus.rastermaps.LayerTransparencySeekbarMode; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivityLayers; @@ -39,9 +40,9 @@ public class RasterMapMenu { final OsmandSettings settings = app.getSettings(); final OsmandRasterMapsPlugin plugin = OsmandPlugin.getEnabledPlugin(OsmandRasterMapsPlugin.class); assert plugin != null; - final OsmandSettings.CommonPreference mapTransparencyPreference; - final OsmandSettings.CommonPreference mapTypePreference; - final OsmandSettings.CommonPreference exMapTypePreference; + final CommonPreference mapTransparencyPreference; + final CommonPreference mapTypePreference; + final CommonPreference exMapTypePreference; final LayerTransparencySeekbarMode currentMapTypeSeekbarMode = type == RasterMapType.OVERLAY ? LayerTransparencySeekbarMode.OVERLAY : LayerTransparencySeekbarMode.UNDERLAY; @StringRes final int mapTypeString; @@ -62,7 +63,7 @@ public class RasterMapMenu { throw new RuntimeException("Unexpected raster map type"); } - final OsmandSettings.CommonPreference hidePolygonsPref = + final CommonPreference hidePolygonsPref = mapActivity.getMyApplication().getSettings().getCustomRenderBooleanProperty("noPolygons"); String mapTypeDescr = mapTypePreference.get(); @@ -181,8 +182,8 @@ public class RasterMapMenu { @NonNull public static Boolean isSeekbarVisible(OsmandApplication app, RasterMapType type) { - final OsmandSettings.LayerTransparencySeekbarMode currentMapTypeSeekbarMode = - type == RasterMapType.OVERLAY ? OsmandSettings.LayerTransparencySeekbarMode.OVERLAY : OsmandSettings.LayerTransparencySeekbarMode.UNDERLAY; + final LayerTransparencySeekbarMode currentMapTypeSeekbarMode = + type == RasterMapType.OVERLAY ? LayerTransparencySeekbarMode.OVERLAY : LayerTransparencySeekbarMode.UNDERLAY; LayerTransparencySeekbarMode seekbarMode = app.getSettings().LAYER_TRANSPARENCY_SEEKBAR_MODE.get(); return seekbarMode == LayerTransparencySeekbarMode.UNDEFINED || seekbarMode == currentMapTypeSeekbarMode; } diff --git a/OsmAnd/src/net/osmand/plus/dialogs/SendAnalyticsBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/dialogs/SendAnalyticsBottomSheetDialogFragment.java index f84ac5239e..983315c503 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/SendAnalyticsBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/SendAnalyticsBottomSheetDialogFragment.java @@ -19,7 +19,7 @@ import androidx.fragment.app.FragmentManager; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java index 4b0dd632aa..80ce6cfb27 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java @@ -22,7 +22,7 @@ import net.osmand.map.WorldRegion; import net.osmand.map.WorldRegion.RegionParams; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.base.BasicProgressAsyncTask; diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java index dceeac1862..f97784cb36 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java @@ -21,6 +21,7 @@ import androidx.annotation.NonNull; import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; +import net.osmand.FileUtils; import net.osmand.IProgress; import net.osmand.plus.OnDismissDialogFragmentListener; import net.osmand.plus.OsmandApplication; @@ -71,7 +72,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { File internalStorage = getInternalStorageDirectory(activity); File external1Storage = getExternal1StorageDirectory(activity); - if (external1Storage != null && external1Storage.exists() && OsmandSettings.isWritable(external1Storage)) { + if (external1Storage != null && external1Storage.exists() && FileUtils.isWritable(external1Storage)) { deviceStorage = external1Storage; deviceStorageType = OsmandSettings.EXTERNAL_STORAGE_TYPE_EXTERNAL_FILE; deviceStorageName = getString(R.string.storage_directory_external); @@ -246,7 +247,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { }; public boolean saveFilesLocation(int type, File selectedFile, Activity context) { - boolean wr = OsmandSettings.isWritable(selectedFile); + boolean wr = FileUtils.isWritable(selectedFile); if (wr) { ((OsmandApplication) context.getApplication()) .setExternalStorageDirectory(type, selectedFile.getAbsolutePath()); diff --git a/OsmAnd/src/net/osmand/plus/activities/DayNightHelper.java b/OsmAnd/src/net/osmand/plus/helpers/DayNightHelper.java similarity index 98% rename from OsmAnd/src/net/osmand/plus/activities/DayNightHelper.java rename to OsmAnd/src/net/osmand/plus/helpers/DayNightHelper.java index f9ca2afca4..d3ebe9d6ae 100644 --- a/OsmAnd/src/net/osmand/plus/activities/DayNightHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/DayNightHelper.java @@ -1,20 +1,6 @@ -package net.osmand.plus.activities; +package net.osmand.plus.helpers; -import java.util.Date; -import java.util.List; -import java.util.TimeZone; - -import net.osmand.Location; -import net.osmand.PlatformUtil; -import net.osmand.StateChangedListener; -import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings.DayNightMode; -import net.osmand.util.SunriseSunset; - -import org.apache.commons.logging.Log; - import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorEvent; @@ -22,6 +8,20 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.location.LocationManager; +import net.osmand.Location; +import net.osmand.PlatformUtil; +import net.osmand.StateChangedListener; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.helpers.enums.DayNightMode; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.util.SunriseSunset; + +import org.apache.commons.logging.Log; + +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + /** * Class to help determine if we want to render day or night map - it uses the * DayNightMode enumeration for its behavior
diff --git a/OsmAnd/src/net/osmand/plus/helpers/DiscountHelper.java b/OsmAnd/src/net/osmand/plus/helpers/DiscountHelper.java index 61ae82399f..a4bb6ec74f 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/DiscountHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/DiscountHelper.java @@ -20,12 +20,14 @@ import androidx.annotation.NonNull; import androidx.appcompat.content.res.AppCompatResources; import net.osmand.AndroidNetworkUtils; +import net.osmand.PlatformUtil; import net.osmand.osm.AbstractPoiType; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; import net.osmand.osm.PoiType; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.activities.OsmandInAppPurchaseActivity; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.Version; @@ -56,7 +58,7 @@ import java.util.Map; public class DiscountHelper { private static final String TAG = "DiscountHelper"; - //private static final String DISCOUNT_JSON = "discount.json"; + private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(DiscountHelper.class); private static long mLastCheckTime; private static ControllerData mData; @@ -81,7 +83,7 @@ public class DiscountHelper { public static void checkAndDisplay(final MapActivity mapActivity) { OsmandApplication app = mapActivity.getMyApplication(); OsmandSettings settings = app.getSettings(); - if (settings.DO_NOT_SHOW_STARTUP_MESSAGES.get() || !settings.INAPPS_READ.get() || Version.isHuawei(app)) { + if (settings.DO_NOT_SHOW_STARTUP_MESSAGES.get() || !settings.INAPPS_READ.get()) { return; } if (mBannerVisible) { @@ -312,7 +314,11 @@ public class DiscountHelper { if (purchaseHelper != null) { if (url.contains(purchaseHelper.getFullVersion().getSku())) { app.logEvent("in_app_purchase_redirect"); - purchaseHelper.purchaseFullVersion(mapActivity); + try { + purchaseHelper.purchaseFullVersion(mapActivity); + } catch (UnsupportedOperationException e) { + LOG.error("purchaseFullVersion is not supported", e); + } } else { for (InAppPurchase p : purchaseHelper.getLiveUpdates().getAllSubscriptions()) { if (url.contains(p.getSku())) { diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index 989bbaa0f8..47650dc2ec 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -80,6 +80,10 @@ import net.osmand.plus.OsmAndConstants; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.helpers.enums.SpeedConstants; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.Version; @@ -93,7 +97,6 @@ import net.osmand.plus.dialogs.GpxAppearanceAdapter; import net.osmand.plus.dialogs.GpxAppearanceAdapter.AppearanceListItem; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; import net.osmand.plus.routing.RouteCalculationResult; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.render.RenderingRuleProperty; import net.osmand.render.RenderingRulesStorage; import net.osmand.router.RouteStatisticsHelper; @@ -514,9 +517,9 @@ public class GpxUiHelper { } else { final View apprTitleView = View.inflate(new ContextThemeWrapper(activity, themeRes), R.layout.select_gpx_appearance_title, null); - final OsmandSettings.CommonPreference prefWidth + final CommonPreference prefWidth = app.getSettings().getCustomRenderProperty(CURRENT_TRACK_WIDTH_ATTR); - final OsmandSettings.CommonPreference prefColor + final CommonPreference prefColor = app.getSettings().getCustomRenderProperty(CURRENT_TRACK_COLOR_ATTR); updateAppearanceTitle(activity, app, trackWidthProp, renderer, apprTitleView, prefWidth.get(), prefColor.get()); @@ -574,7 +577,7 @@ public class GpxUiHelper { if (SHOW_START_FINISH_ATTR.equals(entry.getKey())) { app.getSettings().SHOW_START_FINISH_ICONS.set("true".equals(entry.getValue())); } else { - final OsmandSettings.CommonPreference pref + final CommonPreference pref = app.getSettings().getCustomRenderProperty(entry.getKey()); pref.set(entry.getValue()); } @@ -1039,7 +1042,7 @@ public class GpxUiHelper { private static float setupAxisDistance(OsmandApplication ctx, AxisBase axisBase, float meters) { OsmandSettings settings = ctx.getSettings(); - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); + MetricsConstants mc = settings.METRIC_SYSTEM.get(); float divX; String format1 = "{0,number,0.#} "; @@ -1048,10 +1051,10 @@ public class GpxUiHelper { float granularity = 1f; int mainUnitStr; float mainUnitInMeters; - if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS) { + if (mc == MetricsConstants.KILOMETERS_AND_METERS) { mainUnitStr = R.string.km; mainUnitInMeters = METERS_IN_KILOMETER; - } else if (mc == OsmandSettings.MetricsConstants.NAUTICAL_MILES) { + } else if (mc == MetricsConstants.NAUTICAL_MILES) { mainUnitStr = R.string.nm; mainUnitInMeters = METERS_IN_ONE_NAUTICALMILE; } else { @@ -1065,10 +1068,10 @@ public class GpxUiHelper { if (meters >= 100 * mainUnitInMeters || meters > 9.99f * mainUnitInMeters || meters > 0.999f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.MILES_AND_FEET && meters > 0.249f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.MILES_AND_METERS && meters > 0.249f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS && meters > 0.249f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.NAUTICAL_MILES && meters > 0.99f * mainUnitInMeters) { + mc == MetricsConstants.MILES_AND_FEET && meters > 0.249f * mainUnitInMeters || + mc == MetricsConstants.MILES_AND_METERS && meters > 0.249f * mainUnitInMeters || + mc == MetricsConstants.MILES_AND_YARDS && meters > 0.249f * mainUnitInMeters || + mc == MetricsConstants.NAUTICAL_MILES && meters > 0.99f * mainUnitInMeters) { divX = mainUnitInMeters; if (fmt == null) { @@ -1078,13 +1081,13 @@ public class GpxUiHelper { } else { fmt = null; granularity = 1f; - if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS || mc == OsmandSettings.MetricsConstants.MILES_AND_METERS) { + if (mc == MetricsConstants.KILOMETERS_AND_METERS || mc == MetricsConstants.MILES_AND_METERS) { divX = 1f; mainUnitStr = R.string.m; - } else if (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) { + } else if (mc == MetricsConstants.MILES_AND_FEET) { divX = 1f / FEET_IN_ONE_METER; mainUnitStr = R.string.foot; - } else if (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS) { + } else if (mc == MetricsConstants.MILES_AND_YARDS) { divX = 1f / YARDS_IN_ONE_METER; mainUnitStr = R.string.yard; } else { @@ -1306,8 +1309,8 @@ public class GpxUiHelper { boolean drawFilled, boolean calcWithoutGaps) { OsmandSettings settings = ctx.getSettings(); - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); - boolean useFeet = (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) || (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS); + MetricsConstants mc = settings.METRIC_SYSTEM.get(); + boolean useFeet = (mc == MetricsConstants.MILES_AND_FEET) || (mc == MetricsConstants.MILES_AND_YARDS); boolean light = settings.isLightContent(); final float convEle = useFeet ? 3.28084f : 1.0f; @@ -1408,19 +1411,19 @@ public class GpxUiHelper { divX = setupAxisDistance(ctx, xAxis, calcWithoutGaps ? analysis.totalDistanceWithoutGaps : analysis.totalDistance); } - OsmandSettings.SpeedConstants sps = settings.SPEED_SYSTEM.get(); + SpeedConstants sps = settings.SPEED_SYSTEM.get(); float mulSpeed = Float.NaN; float divSpeed = Float.NaN; final String mainUnitY = sps.toShortString(ctx); - if (sps == OsmandSettings.SpeedConstants.KILOMETERS_PER_HOUR) { + if (sps == SpeedConstants.KILOMETERS_PER_HOUR) { mulSpeed = 3.6f; - } else if (sps == OsmandSettings.SpeedConstants.MILES_PER_HOUR) { + } else if (sps == SpeedConstants.MILES_PER_HOUR) { mulSpeed = 3.6f * METERS_IN_KILOMETER / METERS_IN_ONE_MILE; - } else if (sps == OsmandSettings.SpeedConstants.NAUTICALMILES_PER_HOUR) { + } else if (sps == SpeedConstants.NAUTICALMILES_PER_HOUR) { mulSpeed = 3.6f * METERS_IN_KILOMETER / METERS_IN_ONE_NAUTICALMILE; - } else if (sps == OsmandSettings.SpeedConstants.MINUTES_PER_KILOMETER) { + } else if (sps == SpeedConstants.MINUTES_PER_KILOMETER) { divSpeed = METERS_IN_KILOMETER / 60; - } else if (sps == OsmandSettings.SpeedConstants.MINUTES_PER_MILE) { + } else if (sps == SpeedConstants.MINUTES_PER_MILE) { divSpeed = METERS_IN_ONE_MILE / 60; } else { mulSpeed = 1f; @@ -1571,8 +1574,8 @@ public class GpxUiHelper { } OsmandSettings settings = ctx.getSettings(); boolean light = settings.isLightContent(); - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); - boolean useFeet = (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) || (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS); + MetricsConstants mc = settings.METRIC_SYSTEM.get(); + boolean useFeet = (mc == MetricsConstants.MILES_AND_FEET) || (mc == MetricsConstants.MILES_AND_YARDS); final float convEle = useFeet ? 3.28084f : 1.0f; final float totalDistance = calcWithoutGaps ? analysis.totalDistanceWithoutGaps : analysis.totalDistance; diff --git a/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java b/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java index b0f34058d3..ddbb06f2be 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/LockHelper.java @@ -20,7 +20,8 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.routing.VoiceRouter.VoiceMessageListener; import net.osmand.plus.settings.backend.OsmAndAppCustomization.OsmAndAppCustomizationListener; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.routing.VoiceRouter.VoiceMessageListener; import org.apache.commons.logging.Log; diff --git a/OsmAnd/src/net/osmand/plus/helpers/WaypointHelper.java b/OsmAnd/src/net/osmand/plus/helpers/WaypointHelper.java index b143653754..7f20de3e60 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/WaypointHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/WaypointHelper.java @@ -24,6 +24,9 @@ import net.osmand.plus.TargetPointsHelper.TargetPoint; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.IntermediatePointsDialog; import net.osmand.plus.base.PointImageDrawable; +import net.osmand.plus.helpers.enums.DrivingRegion; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.helpers.enums.SpeedConstants; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.render.RenderingIcons; import net.osmand.plus.routing.AlarmInfo; @@ -31,8 +34,6 @@ import net.osmand.plus.routing.AlarmInfo.AlarmInfoType; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.VoiceRouter; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.MetricsConstants; import net.osmand.util.MapUtils; import java.util.ArrayList; @@ -193,7 +194,7 @@ public class WaypointHelper { return found; } - public AlarmInfo getMostImportantAlarm(OsmandSettings.SpeedConstants sc, boolean showCameras) { + public AlarmInfo getMostImportantAlarm(SpeedConstants sc, boolean showCameras) { Location lastProjection = app.getRoutingHelper().getLastProjection(); float mxspeed = route.getCurrentMaxSpeed(); float delta = app.getSettings().SPEED_LIMIT_EXCEED_KMH.get() / 3.6f; @@ -291,7 +292,7 @@ public class WaypointHelper { } public AlarmInfo calculateMostImportantAlarm(RouteDataObject ro, Location loc, MetricsConstants mc, - OsmandSettings.SpeedConstants sc, boolean showCameras) { + SpeedConstants sc, boolean showCameras) { float mxspeed = ro.getMaximumSpeed(ro.bearingVsRouteDirection(loc)); float delta = app.getSettings().SPEED_LIMIT_EXCEED_KMH.get() / 3.6f; AlarmInfo speedAlarm = createSpeedAlarm(sc, mxspeed, loc, delta); @@ -331,7 +332,7 @@ public class WaypointHelper { return null; } - private static AlarmInfo createSpeedAlarm(OsmandSettings.SpeedConstants sc, float mxspeed, Location loc, float delta) { + private static AlarmInfo createSpeedAlarm(SpeedConstants sc, float mxspeed, Location loc, float delta) { AlarmInfo speedAlarm = null; if (mxspeed != 0 && loc != null && loc.hasSpeed() && mxspeed != RouteDataObject.NONE_MAX_SPEED) { if (loc.getSpeed() > mxspeed + delta) { @@ -790,7 +791,7 @@ public class WaypointHelper { } else if (type == ALARMS) { //assign alarm list icons manually for now String typeString = ((AlarmInfo) point).getType().toString(); - OsmandSettings.DrivingRegion region = app.getSettings().DRIVING_REGION.get(); + DrivingRegion region = app.getSettings().DRIVING_REGION.get(); if (typeString.equals("SPEED_CAMERA")) { return AppCompatResources.getDrawable(uiCtx, R.drawable.mx_highway_speed_camera); } else if (typeString.equals("BORDER_CONTROL")) { diff --git a/OsmAnd/src/net/osmand/plus/helpers/enums/AngularConstants.java b/OsmAnd/src/net/osmand/plus/helpers/enums/AngularConstants.java new file mode 100644 index 0000000000..4041380fe1 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/enums/AngularConstants.java @@ -0,0 +1,27 @@ +package net.osmand.plus.helpers.enums; + +import android.content.Context; + +import net.osmand.plus.R; + +public enum AngularConstants { + DEGREES(R.string.shared_string_degrees, "°"), + DEGREES360(R.string.shared_string_degrees, "°"), + MILLIRADS(R.string.shared_string_milliradians, "mil"); + + private final int key; + private final String unit; + + AngularConstants(int key, String unit) { + this.key = key; + this.unit = unit; + } + + public String toHumanString(Context ctx) { + return ctx.getString(key); + } + + public String getUnitSymbol() { + return unit; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/helpers/enums/AutoZoomMap.java b/OsmAnd/src/net/osmand/plus/helpers/enums/AutoZoomMap.java new file mode 100644 index 0000000000..ad09424924 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/enums/AutoZoomMap.java @@ -0,0 +1,19 @@ +package net.osmand.plus.helpers.enums; + +import net.osmand.plus.R; + +public enum AutoZoomMap { + FARTHEST(R.string.auto_zoom_farthest, 1f, 15.5f), + FAR(R.string.auto_zoom_far, 1.4f, 17f), + CLOSE(R.string.auto_zoom_close, 2f, 19f); + + public final float coefficient; + public final int name; + public final float maxZoom; + + AutoZoomMap(int name, float coefficient, float maxZoom) { + this.name = name; + this.coefficient = coefficient; + this.maxZoom = maxZoom; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/helpers/enums/DayNightMode.java b/OsmAnd/src/net/osmand/plus/helpers/enums/DayNightMode.java new file mode 100644 index 0000000000..77f0eb8876 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/enums/DayNightMode.java @@ -0,0 +1,62 @@ +package net.osmand.plus.helpers.enums; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorManager; + +import androidx.annotation.DrawableRes; +import androidx.annotation.StringRes; + +import net.osmand.plus.R; + +public enum DayNightMode { + AUTO(R.string.daynight_mode_auto, R.drawable.ic_action_map_sunset), + DAY(R.string.daynight_mode_day, R.drawable.ic_action_map_day), + NIGHT(R.string.daynight_mode_night, R.drawable.ic_action_map_night), + SENSOR(R.string.daynight_mode_sensor, R.drawable.ic_action_map_light_sensor); + + private final int key; + @DrawableRes + private final int drawableRes; + + DayNightMode(@StringRes int key, @DrawableRes int drawableRes) { + this.key = key; + this.drawableRes = drawableRes; + } + + public String toHumanString(Context ctx) { + return ctx.getString(key); + } + + @DrawableRes + public int getIconRes() { + return drawableRes; + } + + public boolean isSensor() { + return this == SENSOR; + } + + public boolean isAuto() { + return this == AUTO; + } + + public boolean isDay() { + return this == DAY; + } + + public boolean isNight() { + return this == NIGHT; + } + + public static DayNightMode[] possibleValues(Context context) { + SensorManager mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + Sensor mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + boolean isLightSensorEnabled = mLight != null; + if (isLightSensorEnabled) { + return DayNightMode.values(); + } else { + return new DayNightMode[]{AUTO, DAY, NIGHT}; + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/helpers/enums/DrivingRegion.java b/OsmAnd/src/net/osmand/plus/helpers/enums/DrivingRegion.java new file mode 100644 index 0000000000..60902c46b5 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/enums/DrivingRegion.java @@ -0,0 +1,62 @@ +package net.osmand.plus.helpers.enums; + +import android.content.Context; + +import net.osmand.plus.R; + +import java.util.Locale; + +/** + * Class represents specific for driving region + * Signs, leftHandDriving + */ +public enum DrivingRegion { + + EUROPE_ASIA(R.string.driving_region_europe_asia, MetricsConstants.KILOMETERS_AND_METERS, false), + US(R.string.driving_region_us, MetricsConstants.MILES_AND_FEET, false), + CANADA(R.string.driving_region_canada, MetricsConstants.KILOMETERS_AND_METERS, false), + UK_AND_OTHERS(R.string.driving_region_uk, MetricsConstants.MILES_AND_METERS, true), + JAPAN(R.string.driving_region_japan, MetricsConstants.KILOMETERS_AND_METERS, true), + AUSTRALIA(R.string.driving_region_australia, MetricsConstants.KILOMETERS_AND_METERS, true); + + public final boolean leftHandDriving; + public final MetricsConstants defMetrics; + public final int name; + + DrivingRegion(int name, MetricsConstants def, boolean leftHandDriving) { + this.name = name; + defMetrics = def; + this.leftHandDriving = leftHandDriving; + } + + public boolean isAmericanTypeSigns() { + return this == DrivingRegion.AUSTRALIA || + this == DrivingRegion.US || + this == DrivingRegion.CANADA; + } + + public String getDescription(Context ctx) { + return ctx.getString(leftHandDriving ? R.string.left_side_navigation : R.string.right_side_navigation) + + ", " + + defMetrics.toHumanString(ctx).toLowerCase(); + } + + public static DrivingRegion getDrivingRegionByLocale() { + Locale df = Locale.getDefault(); + if (df == null) { + return DrivingRegion.EUROPE_ASIA; + } + if (df.getCountry().equalsIgnoreCase(Locale.US.getCountry())) { + return DrivingRegion.US; + } else if (df.getCountry().equalsIgnoreCase(Locale.CANADA.getCountry())) { + return DrivingRegion.CANADA; + } else if (df.getCountry().equalsIgnoreCase(Locale.JAPAN.getCountry())) { + return DrivingRegion.JAPAN; + } else if (df.getCountry().equalsIgnoreCase("au")) { + return DrivingRegion.AUSTRALIA; + } else if (df.getCountry().equalsIgnoreCase(Locale.UK.getCountry())) { + return DrivingRegion.UK_AND_OTHERS; + } + return DrivingRegion.EUROPE_ASIA; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/helpers/enums/MetricsConstants.java b/OsmAnd/src/net/osmand/plus/helpers/enums/MetricsConstants.java new file mode 100644 index 0000000000..20f3804a6a --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/enums/MetricsConstants.java @@ -0,0 +1,29 @@ +package net.osmand.plus.helpers.enums; + +import android.content.Context; + +import net.osmand.plus.R; + +public enum MetricsConstants { + KILOMETERS_AND_METERS(R.string.si_km_m, "km-m"), + MILES_AND_FEET(R.string.si_mi_feet, "mi-f"), + MILES_AND_METERS(R.string.si_mi_meters, "mi-m"), + MILES_AND_YARDS(R.string.si_mi_yard, "mi-y"), + NAUTICAL_MILES(R.string.si_nm, "nm"); + + private final int key; + private final String ttsString; + + MetricsConstants(int key, String ttsString) { + this.key = key; + this.ttsString = ttsString; + } + + public String toHumanString(Context ctx) { + return ctx.getString(key); + } + + public String toTTSString() { + return ttsString; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/helpers/enums/SpeedConstants.java b/OsmAnd/src/net/osmand/plus/helpers/enums/SpeedConstants.java new file mode 100644 index 0000000000..1a4c512b8c --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/enums/SpeedConstants.java @@ -0,0 +1,32 @@ +package net.osmand.plus.helpers.enums; + +import android.content.Context; + +import net.osmand.plus.R; + +public enum SpeedConstants { + KILOMETERS_PER_HOUR(R.string.km_h, R.string.si_kmh, false), + MILES_PER_HOUR(R.string.mile_per_hour, R.string.si_mph, true), + METERS_PER_SECOND(R.string.m_s, R.string.si_m_s, false), + MINUTES_PER_MILE(R.string.min_mile, R.string.si_min_m, true), + MINUTES_PER_KILOMETER(R.string.min_km, R.string.si_min_km, false), + NAUTICALMILES_PER_HOUR(R.string.nm_h, R.string.si_nm_h, true); + + public final int key; + public final int descr; + public final boolean imperial; + + SpeedConstants(int key, int descr, boolean imperial) { + this.key = key; + this.descr = descr; + this.imperial = imperial; + } + + public String toHumanString(Context ctx) { + return ctx.getString(descr); + } + + public String toShortString(Context ctx) { + return ctx.getString(key); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/helpers/enums/TracksSortByMode.java b/OsmAnd/src/net/osmand/plus/helpers/enums/TracksSortByMode.java new file mode 100644 index 0000000000..6b4177c701 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/enums/TracksSortByMode.java @@ -0,0 +1,38 @@ +package net.osmand.plus.helpers.enums; + +import androidx.annotation.DrawableRes; +import androidx.annotation.StringRes; + +import net.osmand.plus.R; + +public enum TracksSortByMode { + BY_DATE(R.string.sort_last_modified, R.drawable.ic_action_time_start), + BY_NAME_ASCENDING(R.string.sort_name_ascending, R.drawable.ic_action_sort_by_name_ascending), + BY_NAME_DESCENDING(R.string.sort_name_descending, R.drawable.ic_action_sort_by_name_descending); + + private final int iconId; + private final int nameId; + + TracksSortByMode(int nameId, int iconId) { + this.nameId = nameId; + this.iconId = iconId; + } + + public boolean isByName() { + return this == BY_NAME_ASCENDING || this == BY_NAME_DESCENDING; + } + + public boolean isByDate() { + return this == BY_DATE; + } + + @StringRes + public int getNameId() { + return nameId; + } + + @DrawableRes + public int getIconId() { + return iconId; + } +} diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java index ac7e2c77fb..543c3a819d 100644 --- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java +++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java @@ -2,6 +2,8 @@ package net.osmand.plus.inapp; import android.annotation.SuppressLint; import android.app.Activity; +import android.content.Context; +import android.content.Intent; import android.os.AsyncTask; import android.text.TextUtils; import android.util.Log; @@ -9,13 +11,6 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.billingclient.api.BillingClient.BillingResponseCode; -import com.android.billingclient.api.BillingClient.SkuType; -import com.android.billingclient.api.BillingResult; -import com.android.billingclient.api.Purchase; -import com.android.billingclient.api.SkuDetails; -import com.android.billingclient.api.SkuDetailsResponseListener; - import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils.OnRequestResultListener; import net.osmand.AndroidNetworkUtils.OnRequestsResultListener; @@ -23,19 +18,16 @@ import net.osmand.AndroidNetworkUtils.RequestResponse; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.inapp.InAppPurchases.InAppPurchase; import net.osmand.plus.inapp.InAppPurchases.InAppPurchase.PurchaseState; -import net.osmand.plus.inapp.InAppPurchases.InAppPurchaseLiveUpdatesOldSubscription; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; -import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionIntroductoryInfo; import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionList; -import net.osmand.plus.inapp.util.BillingManager; -import net.osmand.plus.inapp.util.BillingManager.BillingUpdatesListener; import net.osmand.plus.liveupdates.CountrySelectionFragment; import net.osmand.plus.liveupdates.CountrySelectionFragment.CountryItem; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import org.json.JSONArray; @@ -43,7 +35,6 @@ import org.json.JSONException; import org.json.JSONObject; import java.lang.ref.WeakReference; -import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -53,54 +44,31 @@ import java.util.List; import java.util.Map; import java.util.Set; -public class InAppPurchaseHelper { +public abstract class InAppPurchaseHelper { // Debug tag, for logging - private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class); + protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class); private static final String TAG = InAppPurchaseHelper.class.getSimpleName(); private boolean mDebugLog = false; public static final long SUBSCRIPTION_HOLDING_TIME_MSEC = 1000 * 60 * 60 * 24 * 3; // 3 days - private InAppPurchases purchases; - private long lastValidationCheckTime; - private boolean inventoryRequested; + protected InAppPurchases purchases; + protected long lastValidationCheckTime; + protected boolean inventoryRequested; private static final long PURCHASE_VALIDATION_PERIOD_MSEC = 1000 * 60 * 60 * 24; // daily - // (arbitrary) request code for the purchase flow - private static final int RC_REQUEST = 10001; - // The helper object - private BillingManager billingManager; - private List skuDetailsList; + protected boolean isDeveloperVersion; + protected String token = ""; + protected InAppPurchaseTaskType activeTask; + protected boolean processingTask = false; + protected boolean inventoryRequestPending = false; - private boolean isDeveloperVersion; - private String token = ""; - private InAppPurchaseTaskType activeTask; - private boolean processingTask = false; - private boolean inventoryRequestPending = false; - - private OsmandApplication ctx; - private InAppPurchaseListener uiActivity = null; - - /* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY - * (that you got from the Google Play developer console). This is not your - * developer public key, it's the *app-specific* public key. - * - * Instead of just storing the entire literal string here embedded in the - * program, construct the key at runtime from pieces or - * use bit manipulation (for example, XOR with some other string) to hide - * the actual key. The key itself is not secret information, but we don't - * want to make it easy for an attacker to replace the public key with one - * of their own and then fake messages from the server. - */ - private static final String BASE64_ENCODED_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgk8cEx" + - "UO4mfEwWFLkQnX1Tkzehr4SnXLXcm2Osxs5FTJPEgyTckTh0POKVMrxeGLn0KoTY2NTgp1U/inp" + - "wccWisPhVPEmw9bAVvWsOkzlyg1kv03fJdnAXRBSqDDPV6X8Z3MtkPVqZkupBsxyIllEILKHK06" + - "OCw49JLTsMR3oTRifGzma79I71X0spw0fM+cIRlkS2tsXN8GPbdkJwHofZKPOXS51pgC1zU8uWX" + - "I+ftJO46a1XkNh1dO2anUiQ8P/H4yOTqnMsXF7biyYuiwjXPOcy0OMhEHi54Dq6Mr3u5ZALOAkc" + - "YTjh1H/ZgqIHy5ZluahINuDE76qdLYMXrDMQIDAQAB"; + protected OsmandApplication ctx; + protected InAppPurchaseListener uiActivity = null; public interface InAppPurchaseListener { + void onError(InAppPurchaseTaskType taskType, String error); void onGetItems(); @@ -112,16 +80,62 @@ public class InAppPurchaseHelper { void dismissProgress(InAppPurchaseTaskType taskType); } + public interface InAppPurchaseInitCallback { + + void onSuccess(); + + void onFail(); + } + public enum InAppPurchaseTaskType { REQUEST_INVENTORY, PURCHASE_FULL_VERSION, PURCHASE_LIVE_UPDATES, - PURCHASE_DEPTH_CONTOURS + PURCHASE_DEPTH_CONTOURS, + PURCHASE_CONTOUR_LINES } - public interface InAppRunnable { + public abstract class InAppCommand { + + InAppCommandResultHandler resultHandler; + // return true if done and false if async task started - boolean run(InAppPurchaseHelper helper); + abstract void run(InAppPurchaseHelper helper); + + protected void commandDone() { + InAppCommandResultHandler resultHandler = this.resultHandler; + if (resultHandler != null) { + resultHandler.onCommandDone(this); + } + } + } + + public interface InAppCommandResultHandler { + void onCommandDone(@NonNull InAppCommand command); + } + + public static class PurchaseInfo { + private String sku; + private String orderId; + private String purchaseToken; + + public PurchaseInfo(String sku, String orderId, String purchaseToken) { + this.sku = sku; + this.orderId = orderId; + this.purchaseToken = purchaseToken; + } + + public String getSku() { + return sku; + } + + public String getOrderId() { + return orderId; + } + + public String getPurchaseToken() { + return purchaseToken; + } } public String getToken() { @@ -144,6 +158,10 @@ public class InAppPurchaseHelper { return Version.isDeveloperBuild(ctx) || ctx.getSettings().DEPTH_CONTOURS_PURCHASED.get(); } + public static boolean isContourLinesPurchased(@NonNull OsmandApplication ctx) { + return Version.isDeveloperBuild(ctx) || ctx.getSettings().CONTOUR_LINES_PURCHASED.get(); + } + public InAppPurchases getInAppPurchases() { return purchases; } @@ -176,9 +194,10 @@ public class InAppPurchaseHelper { public InAppPurchaseHelper(OsmandApplication ctx) { this.ctx = ctx; isDeveloperVersion = Version.isDeveloperVersion(ctx); - purchases = new InAppPurchases(ctx); } + public abstract void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback); + public boolean hasInventory() { return lastValidationCheckTime != 0; } @@ -194,12 +213,8 @@ public class InAppPurchaseHelper { return false; } - private BillingManager getBillingManager() { - return billingManager; - } - - private void exec(final @NonNull InAppPurchaseTaskType taskType, final @NonNull InAppRunnable runnable) { - if (isDeveloperVersion || !Version.isGooglePlayEnabled(ctx)) { + protected void exec(final @NonNull InAppPurchaseTaskType taskType, final @NonNull InAppCommand command) { + if (isDeveloperVersion || (!Version.isGooglePlayEnabled(ctx) && !Version.isHuawei(ctx))) { notifyDismissProgress(taskType); stop(true); return; @@ -222,117 +237,21 @@ public class InAppPurchaseHelper { try { processingTask = true; activeTask = taskType; - billingManager = new BillingManager(ctx, BASE64_ENCODED_PUBLIC_KEY, new BillingUpdatesListener() { - + command.resultHandler = new InAppCommandResultHandler() { @Override - public void onBillingClientSetupFinished() { - logDebug("Setup finished."); - - BillingManager billingManager = getBillingManager(); - // Have we been disposed of in the meantime? If so, quit. - if (billingManager == null) { - stop(true); - return; - } - - if (!billingManager.isServiceConnected()) { - // Oh noes, there was a problem. - //complain("Problem setting up in-app billing: " + result); - notifyError(taskType, billingManager.getBillingClientResponseMessage()); - stop(true); - return; - } - - processingTask = !runnable.run(InAppPurchaseHelper.this); + public void onCommandDone(@NonNull InAppCommand command) { + processingTask = false; } - - @Override - public void onConsumeFinished(String token, BillingResult billingResult) { - } - - @Override - public void onPurchasesUpdated(final List purchases) { - - BillingManager billingManager = getBillingManager(); - // Have we been disposed of in the meantime? If so, quit. - if (billingManager == null) { - stop(true); - return; - } - - if (activeTask == InAppPurchaseTaskType.REQUEST_INVENTORY) { - List skuInApps = new ArrayList<>(); - for (InAppPurchase purchase : getInAppPurchases().getAllInAppPurchases(false)) { - skuInApps.add(purchase.getSku()); - } - for (Purchase p : purchases) { - skuInApps.add(p.getSku()); - } - billingManager.querySkuDetailsAsync(SkuType.INAPP, skuInApps, new SkuDetailsResponseListener() { - @Override - public void onSkuDetailsResponse(BillingResult billingResult, final List skuDetailsListInApps) { - // Is it a failure? - if (billingResult.getResponseCode() != BillingResponseCode.OK) { - logError("Failed to query inapps sku details: " + billingResult.getResponseCode()); - notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage()); - stop(true); - return; - } - - List skuSubscriptions = new ArrayList<>(); - for (InAppSubscription subscription : getInAppPurchases().getAllInAppSubscriptions()) { - skuSubscriptions.add(subscription.getSku()); - } - for (Purchase p : purchases) { - skuSubscriptions.add(p.getSku()); - } - - BillingManager billingManager = getBillingManager(); - // Have we been disposed of in the meantime? If so, quit. - if (billingManager == null) { - stop(true); - return; - } - - billingManager.querySkuDetailsAsync(SkuType.SUBS, skuSubscriptions, new SkuDetailsResponseListener() { - @Override - public void onSkuDetailsResponse(BillingResult billingResult, final List skuDetailsListSubscriptions) { - // Is it a failure? - if (billingResult.getResponseCode() != BillingResponseCode.OK) { - logError("Failed to query subscriptipons sku details: " + billingResult.getResponseCode()); - notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage()); - stop(true); - return; - } - - List skuDetailsList = new ArrayList<>(skuDetailsListInApps); - skuDetailsList.addAll(skuDetailsListSubscriptions); - InAppPurchaseHelper.this.skuDetailsList = skuDetailsList; - - mSkuDetailsResponseListener.onSkuDetailsResponse(billingResult, skuDetailsList); - } - }); - } - }); - } - for (Purchase purchase : purchases) { - if (!purchase.isAcknowledged()) { - onPurchaseFinished(purchase); - } - } - } - - @Override - public void onPurchaseCanceled() { - stop(true); - } - }); + }; + execImpl(taskType, command); } catch (Exception e) { logError("exec Error", e); stop(true); } } + protected abstract void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand command); + public boolean needRequestInventory() { return !inventoryRequested && ((isSubscribedToLiveUpdates(ctx) && Algorithms.isEmpty(ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.get())) || System.currentTimeMillis() - lastValidationCheckTime > PURCHASE_VALIDATION_PERIOD_MSEC); @@ -343,322 +262,20 @@ public class InAppPurchaseHelper { new RequestInventoryTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } - public void purchaseFullVersion(final Activity activity) { - notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION); - exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, new InAppRunnable() { - @Override - public boolean run(InAppPurchaseHelper helper) { - try { - SkuDetails skuDetails = getSkuDetails(getFullVersion().getSku()); - if (skuDetails == null) { - throw new IllegalArgumentException("Cannot find sku details"); - } - BillingManager billingManager = getBillingManager(); - if (billingManager != null) { - billingManager.initiatePurchaseFlow(activity, skuDetails); - } else { - throw new IllegalStateException("BillingManager disposed"); - } - return false; - } catch (Exception e) { - complain("Cannot launch full version purchase!"); - logError("purchaseFullVersion Error", e); - stop(true); - } - return true; - } - }); - } + public abstract void purchaseFullVersion(@NonNull final Activity activity) throws UnsupportedOperationException; - public void purchaseLiveUpdates(Activity activity, String sku, String email, String userName, + public void purchaseLiveUpdates(@NonNull Activity activity, String sku, String email, String userName, String countryDownloadName, boolean hideUserName) { notifyShowProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES); new LiveUpdatesPurchaseTask(activity, sku, email, userName, countryDownloadName, hideUserName) .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } - public void purchaseDepthContours(final Activity activity) { - notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS); - exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, new InAppRunnable() { - @Override - public boolean run(InAppPurchaseHelper helper) { - try { - SkuDetails skuDetails = getSkuDetails(getDepthContours().getSku()); - if (skuDetails == null) { - throw new IllegalArgumentException("Cannot find sku details"); - } - BillingManager billingManager = getBillingManager(); - if (billingManager != null) { - billingManager.initiatePurchaseFlow(activity, skuDetails); - } else { - throw new IllegalStateException("BillingManager disposed"); - } - return false; - } catch (Exception e) { - complain("Cannot launch depth contours purchase!"); - logError("purchaseDepthContours Error", e); - stop(true); - } - return true; - } - }); - } + public abstract void purchaseDepthContours(@NonNull final Activity activity) throws UnsupportedOperationException; - @Nullable - private SkuDetails getSkuDetails(@NonNull String sku) { - List skuDetailsList = this.skuDetailsList; - if (skuDetailsList != null) { - for (SkuDetails details : skuDetailsList) { - if (details.getSku().equals(sku)) { - return details; - } - } - } - return null; - } + public abstract void purchaseContourLines(@NonNull final Activity activity) throws UnsupportedOperationException; - private boolean hasDetails(@NonNull String sku) { - return getSkuDetails(sku) != null; - } - - @Nullable - private Purchase getPurchase(@NonNull String sku) { - BillingManager billingManager = getBillingManager(); - if (billingManager != null) { - List purchases = billingManager.getPurchases(); - if (purchases != null) { - for (Purchase p : purchases) { - if (p.getSku().equals(sku)) { - return p; - } - } - } - } - return null; - } - - // Listener that's called when we finish querying the items and subscriptions we own - private SkuDetailsResponseListener mSkuDetailsResponseListener = new SkuDetailsResponseListener() { - - @NonNull - private List getAllOwnedSubscriptionSkus() { - List result = new ArrayList<>(); - BillingManager billingManager = getBillingManager(); - if (billingManager != null) { - for (Purchase p : billingManager.getPurchases()) { - if (getInAppPurchases().getInAppSubscriptionBySku(p.getSku()) != null) { - result.add(p.getSku()); - } - } - } - return result; - } - - @Override - public void onSkuDetailsResponse(BillingResult billingResult, List skuDetailsList) { - - logDebug("Query sku details finished."); - - // Have we been disposed of in the meantime? If so, quit. - if (getBillingManager() == null) { - stop(true); - return; - } - - // Is it a failure? - if (billingResult.getResponseCode() != BillingResponseCode.OK) { - logError("Failed to query inventory: " + billingResult.getResponseCode()); - notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage()); - stop(true); - return; - } - - logDebug("Query sku details was successful."); - - /* - * Check for items we own. Notice that for each purchase, we check - * the developer payload to see if it's correct! See - * verifyDeveloperPayload(). - */ - - List allOwnedSubscriptionSkus = getAllOwnedSubscriptionSkus(); - for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) { - if (hasDetails(s.getSku())) { - Purchase purchase = getPurchase(s.getSku()); - SkuDetails liveUpdatesDetails = getSkuDetails(s.getSku()); - if (liveUpdatesDetails != null) { - fetchInAppPurchase(s, liveUpdatesDetails, purchase); - } - allOwnedSubscriptionSkus.remove(s.getSku()); - } - } - for (String sku : allOwnedSubscriptionSkus) { - Purchase purchase = getPurchase(sku); - SkuDetails liveUpdatesDetails = getSkuDetails(sku); - if (liveUpdatesDetails != null) { - InAppSubscription s = getLiveUpdates().upgradeSubscription(sku); - if (s == null) { - s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesDetails); - } - fetchInAppPurchase(s, liveUpdatesDetails, purchase); - } - } - - InAppPurchase fullVersion = getFullVersion(); - if (hasDetails(fullVersion.getSku())) { - Purchase purchase = getPurchase(fullVersion.getSku()); - SkuDetails fullPriceDetails = getSkuDetails(fullVersion.getSku()); - if (fullPriceDetails != null) { - fetchInAppPurchase(fullVersion, fullPriceDetails, purchase); - } - } - - InAppPurchase depthContours = getDepthContours(); - if (hasDetails(depthContours.getSku())) { - Purchase purchase = getPurchase(depthContours.getSku()); - SkuDetails depthContoursDetails = getSkuDetails(depthContours.getSku()); - if (depthContoursDetails != null) { - fetchInAppPurchase(depthContours, depthContoursDetails, purchase); - } - } - - InAppPurchase contourLines = getContourLines(); - if (hasDetails(contourLines.getSku())) { - Purchase purchase = getPurchase(contourLines.getSku()); - SkuDetails contourLinesDetails = getSkuDetails(contourLines.getSku()); - if (contourLinesDetails != null) { - fetchInAppPurchase(contourLines, contourLinesDetails, purchase); - } - } - - Purchase fullVersionPurchase = getPurchase(fullVersion.getSku()); - boolean fullVersionPurchased = fullVersionPurchase != null; - if (fullVersionPurchased) { - ctx.getSettings().FULL_VERSION_PURCHASED.set(true); - } - - Purchase depthContoursPurchase = getPurchase(depthContours.getSku()); - boolean depthContoursPurchased = depthContoursPurchase != null; - if (depthContoursPurchased) { - ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true); - } - - // Do we have the live updates? - boolean subscribedToLiveUpdates = false; - List liveUpdatesPurchases = new ArrayList<>(); - for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) { - Purchase purchase = getPurchase(p.getSku()); - if (purchase != null) { - liveUpdatesPurchases.add(purchase); - if (!subscribedToLiveUpdates) { - subscribedToLiveUpdates = true; - } - } - } - OsmandPreference subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME; - if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) { - if (subscriptionCancelledTime.get() == 0) { - subscriptionCancelledTime.set(System.currentTimeMillis()); - ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.set(false); - ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false); - } else if (System.currentTimeMillis() - subscriptionCancelledTime.get() > SUBSCRIPTION_HOLDING_TIME_MSEC) { - ctx.getSettings().LIVE_UPDATES_PURCHASED.set(false); - if (!isDepthContoursPurchased(ctx)) { - ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(false); - } - } - } else if (subscribedToLiveUpdates) { - subscriptionCancelledTime.set(0L); - ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true); - } - - lastValidationCheckTime = System.currentTimeMillis(); - logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE") - + " live updates purchased."); - - OsmandSettings settings = ctx.getSettings(); - settings.INAPPS_READ.set(true); - - List tokensToSend = new ArrayList<>(); - if (liveUpdatesPurchases.size() > 0) { - List tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";")); - for (Purchase purchase : liveUpdatesPurchases) { - if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get())) - && !Algorithms.isEmpty(purchase.getDeveloperPayload())) { - String payload = purchase.getDeveloperPayload(); - if (!Algorithms.isEmpty(payload)) { - String[] arr = payload.split(" "); - if (arr.length > 0) { - settings.BILLING_USER_ID.set(arr[0]); - } - if (arr.length > 1) { - token = arr[1]; - settings.BILLING_USER_TOKEN.set(token); - } - } - } - if (!tokensSent.contains(purchase.getSku())) { - tokensToSend.add(purchase); - } - } - } - - final OnRequestResultListener listener = new OnRequestResultListener() { - @Override - public void onResult(String result) { - notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY); - notifyGetItems(); - stop(true); - logDebug("Initial inapp query finished"); - } - }; - - if (tokensToSend.size() > 0) { - sendTokens(tokensToSend, listener); - } else { - listener.onResult("OK"); - } - } - }; - - private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull SkuDetails skuDetails, @Nullable Purchase purchase) { - if (purchase != null) { - inAppPurchase.setPurchaseState(PurchaseState.PURCHASED); - inAppPurchase.setPurchaseTime(purchase.getPurchaseTime()); - } else { - inAppPurchase.setPurchaseState(PurchaseState.NOT_PURCHASED); - } - inAppPurchase.setPrice(skuDetails.getPrice()); - inAppPurchase.setPriceCurrencyCode(skuDetails.getPriceCurrencyCode()); - if (skuDetails.getPriceAmountMicros() > 0) { - inAppPurchase.setPriceValue(skuDetails.getPriceAmountMicros() / 1000000d); - } - String subscriptionPeriod = skuDetails.getSubscriptionPeriod(); - if (!Algorithms.isEmpty(subscriptionPeriod)) { - if (inAppPurchase instanceof InAppSubscription) { - try { - ((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod); - } catch (ParseException e) { - LOG.error(e); - } - } - } - if (inAppPurchase instanceof InAppSubscription) { - String introductoryPrice = skuDetails.getIntroductoryPrice(); - String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod(); - String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles(); - long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros(); - if (!Algorithms.isEmpty(introductoryPrice)) { - InAppSubscription s = (InAppSubscription) inAppPurchase; - try { - s.setIntroductoryInfo(new InAppSubscriptionIntroductoryInfo(s, introductoryPrice, - introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles)); - } catch (ParseException e) { - LOG.error(e); - } - } - } - } + public abstract void manageSubscription(@NonNull Context ctx, @Nullable String sku); @SuppressLint("StaticFieldLeak") private class LiveUpdatesPurchaseTask extends AsyncTask { @@ -746,31 +363,7 @@ public class InAppPurchaseHelper { if (!Algorithms.isEmpty(userId) && !Algorithms.isEmpty(token)) { logDebug("Launching purchase flow for live updates subscription for userId=" + userId); final String payload = userId + " " + token; - exec(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, new InAppRunnable() { - @Override - public boolean run(InAppPurchaseHelper helper) { - try { - Activity a = activity.get(); - SkuDetails skuDetails = getSkuDetails(sku); - if (a != null && skuDetails != null) { - BillingManager billingManager = getBillingManager(); - if (billingManager != null) { - billingManager.setPayload(payload); - billingManager.initiatePurchaseFlow(a, skuDetails); - } else { - throw new IllegalStateException("BillingManager disposed"); - } - return false; - } else { - stop(true); - } - } catch (Exception e) { - logError("launchPurchaseFlow Error", e); - stop(true); - } - return true; - } - }); + exec(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, getPurchaseLiveUpdatesCommand(activity, sku, payload)); } else { notifyError(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, "Empty userId"); stop(true); @@ -778,6 +371,9 @@ public class InAppPurchaseHelper { } } + protected abstract InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference activity, + final String sku, final String payload) throws UnsupportedOperationException; + @SuppressLint("StaticFieldLeak") private class RequestInventoryTask extends AsyncTask { @@ -808,38 +404,41 @@ public class InAppPurchaseHelper { try { JSONObject obj = new JSONObject(response); JSONArray names = obj.names(); - for (int i = 0; i < names.length(); i++) { - String skuType = names.getString(i); - JSONObject subObj = obj.getJSONObject(skuType); - String sku = subObj.getString("sku"); - if (!Algorithms.isEmpty(sku)) { - getLiveUpdates().upgradeSubscription(sku); + if (names != null) { + for (int i = 0; i < names.length(); i++) { + String skuType = names.getString(i); + JSONObject subObj = obj.getJSONObject(skuType); + String sku = subObj.getString("sku"); + if (!Algorithms.isEmpty(sku)) { + getLiveUpdates().upgradeSubscription(sku); + } } } } catch (JSONException e) { logError("Json parsing error", e); } } - exec(InAppPurchaseTaskType.REQUEST_INVENTORY, new InAppRunnable() { - @Override - public boolean run(InAppPurchaseHelper helper) { - logDebug("Setup successful. Querying inventory."); - try { - BillingManager billingManager = getBillingManager(); - if (billingManager != null) { - billingManager.queryPurchases(); - } else { - throw new IllegalStateException("BillingManager disposed"); - } - return false; - } catch (Exception e) { - logError("queryInventoryAsync Error", e); - notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY); - stop(true); - } - return true; - } - }); + exec(InAppPurchaseTaskType.REQUEST_INVENTORY, getRequestInventoryCommand()); + } + } + + protected abstract InAppCommand getRequestInventoryCommand() throws UnsupportedOperationException; + + protected void onSkuDetailsResponseDone(List purchaseInfoList) { + final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() { + @Override + public void onResult(String result) { + notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY); + notifyGetItems(); + stop(true); + logDebug("Initial inapp query finished"); + } + }; + + if (purchaseInfoList.size() > 0) { + sendTokens(purchaseInfoList, listener); + } else { + listener.onResult("OK"); } } @@ -852,25 +451,16 @@ public class InAppPurchaseHelper { parameters.put("aid", ctx.getUserAndroidId()); } - // Call when a purchase is finished - private void onPurchaseFinished(Purchase purchase) { - logDebug("Purchase finished: " + purchase); - - // if we were disposed of in the meantime, quit. - if (getBillingManager() == null) { - stop(true); - return; - } - + protected void onPurchaseDone(PurchaseInfo info) { logDebug("Purchase successful."); - InAppPurchase liveUpdatesPurchase = getLiveUpdates().getSubscriptionBySku(purchase.getSku()); + InAppPurchase liveUpdatesPurchase = getLiveUpdates().getSubscriptionBySku(info.getSku()); if (liveUpdatesPurchase != null) { // bought live updates logDebug("Live updates subscription purchased."); final String sku = liveUpdatesPurchase.getSku(); liveUpdatesPurchase.setPurchaseState(PurchaseState.PURCHASED); - sendTokens(Collections.singletonList(purchase), new OnRequestResultListener() { + sendTokens(Collections.singletonList(info), new OnRequestResultListener() { @Override public void onResult(String result) { boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get(); @@ -887,7 +477,7 @@ public class InAppPurchaseHelper { } }); - } else if (purchase.getSku().equals(getFullVersion().getSku())) { + } else if (info.getSku().equals(getFullVersion().getSku())) { // bought full version getFullVersion().setPurchaseState(PurchaseState.PURCHASED); logDebug("Full version purchased."); @@ -898,7 +488,7 @@ public class InAppPurchaseHelper { notifyItemPurchased(getFullVersion().getSku(), false); stop(true); - } else if (purchase.getSku().equals(getDepthContours().getSku())) { + } else if (info.getSku().equals(getDepthContours().getSku())) { // bought sea depth contours getDepthContours().setPurchaseState(PurchaseState.PURCHASED); logDebug("Sea depth contours purchased."); @@ -910,6 +500,17 @@ public class InAppPurchaseHelper { notifyItemPurchased(getDepthContours().getSku(), false); stop(true); + } else if (info.getSku().equals(getContourLines().getSku())) { + // bought contour lines + getContourLines().setPurchaseState(PurchaseState.PURCHASED); + logDebug("Contours lines purchased."); + showToast(ctx.getString(R.string.contour_lines_thanks)); + ctx.getSettings().CONTOUR_LINES_PURCHASED.set(true); + + notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_CONTOUR_LINES); + notifyItemPurchased(getContourLines().getSku(), false); + stop(true); + } else { notifyDismissProgress(activeTask); stop(true); @@ -921,17 +522,19 @@ public class InAppPurchaseHelper { stop(false); } - private void stop(boolean taskDone) { + protected abstract boolean isBillingManagerExists(); + + protected abstract void destroyBillingManager(); + + protected void stop(boolean taskDone) { logDebug("Destroying helper."); - BillingManager billingManager = getBillingManager(); - if (billingManager != null) { + if (isBillingManagerExists()) { if (taskDone) { processingTask = false; } if (!processingTask) { activeTask = null; - billingManager.destroy(); - this.billingManager = null; + destroyBillingManager(); } } else { processingTask = false; @@ -943,7 +546,7 @@ public class InAppPurchaseHelper { } } - private void sendTokens(@NonNull final List purchases, final OnRequestResultListener listener) { + protected void sendTokens(@NonNull final List purchaseInfoList, final OnRequestResultListener listener) { final String userId = ctx.getSettings().BILLING_USER_ID.get(); final String token = ctx.getSettings().BILLING_USER_TOKEN.get(); final String email = ctx.getSettings().BILLING_USER_EMAIL.get(); @@ -951,12 +554,12 @@ public class InAppPurchaseHelper { String url = "https://osmand.net/subscription/purchased"; String userOperation = "Sending purchase info..."; final List requests = new ArrayList<>(); - for (Purchase purchase : purchases) { + for (PurchaseInfo info : purchaseInfoList) { Map parameters = new HashMap<>(); parameters.put("userid", userId); - parameters.put("sku", purchase.getSku()); - parameters.put("orderId", purchase.getOrderId()); - parameters.put("purchaseToken", purchase.getPurchaseToken()); + parameters.put("sku", info.getSku()); + parameters.put("orderId", info.getOrderId()); + parameters.put("purchaseToken", info.getPurchaseToken()); parameters.put("email", email); parameters.put("token", token); addUserInfo(parameters); @@ -967,9 +570,9 @@ public class InAppPurchaseHelper { public void onResult(@NonNull List results) { for (RequestResponse rr : results) { String sku = rr.getRequest().getParameters().get("sku"); - Purchase purchase = getPurchase(sku); - if (purchase != null) { - updateSentTokens(purchase); + PurchaseInfo info = getPurchaseInfo(sku); + if (info != null) { + updateSentTokens(info); String result = rr.getResponse(); if (result != null) { try { @@ -979,13 +582,13 @@ public class InAppPurchaseHelper { } else { complain("SendToken Error: " + obj.getString("error") - + " (userId=" + userId + " token=" + token + " response=" + result + " google=" + purchase.toString() + ")"); + + " (userId=" + userId + " token=" + token + " response=" + result + " google=" + info.toString() + ")"); } } catch (JSONException e) { logError("SendToken", e); complain("SendToken Error: " + (e.getMessage() != null ? e.getMessage() : "JSONException") - + " (userId=" + userId + " token=" + token + " response=" + result + " google=" + purchase.toString() + ")"); + + " (userId=" + userId + " token=" + token + " response=" + result + " google=" + info.toString() + ")"); } } } @@ -995,10 +598,10 @@ public class InAppPurchaseHelper { } } - private void updateSentTokens(@NonNull Purchase purchase) { + private void updateSentTokens(@NonNull PurchaseInfo info) { String tokensSentStr = ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.get(); Set tokensSent = new HashSet<>(Arrays.asList(tokensSentStr.split(";"))); - tokensSent.add(purchase.getSku()); + tokensSent.add(info.getSku()); ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.set(TextUtils.join(";", tokensSent)); } @@ -1032,10 +635,10 @@ public class InAppPurchaseHelper { } @Nullable - private Purchase getPurchase(String sku) { - for (Purchase purchase : purchases) { - if (purchase.getSku().equals(sku)) { - return purchase; + private PurchaseInfo getPurchaseInfo(String sku) { + for (PurchaseInfo info : purchaseInfoList) { + if (info.getSku().equals(sku)) { + return info; } } return null; @@ -1049,31 +652,35 @@ public class InAppPurchaseHelper { } } - private void notifyError(InAppPurchaseTaskType taskType, String message) { + public boolean onActivityResult(@NonNull Activity activity, int requestCode, int resultCode, Intent data) { + return false; + } + + protected void notifyError(InAppPurchaseTaskType taskType, String message) { if (uiActivity != null) { uiActivity.onError(taskType, message); } } - private void notifyGetItems() { + protected void notifyGetItems() { if (uiActivity != null) { uiActivity.onGetItems(); } } - private void notifyItemPurchased(String sku, boolean active) { + protected void notifyItemPurchased(String sku, boolean active) { if (uiActivity != null) { uiActivity.onItemPurchased(sku, active); } } - private void notifyShowProgress(InAppPurchaseTaskType taskType) { + protected void notifyShowProgress(InAppPurchaseTaskType taskType) { if (uiActivity != null) { uiActivity.showProgress(taskType); } } - private void notifyDismissProgress(InAppPurchaseTaskType taskType) { + protected void notifyDismissProgress(InAppPurchaseTaskType taskType) { if (uiActivity != null) { uiActivity.dismissProgress(taskType); } @@ -1090,26 +697,26 @@ public class InAppPurchaseHelper { } } - private void complain(String message) { + protected void complain(String message) { logError("**** InAppPurchaseHelper Error: " + message); showToast(message); } - private void showToast(final String message) { + protected void showToast(final String message) { ctx.showToastMessage(message); } - private void logDebug(String msg) { + protected void logDebug(String msg) { if (mDebugLog) { Log.d(TAG, msg); } } - private void logError(String msg) { + protected void logError(String msg) { Log.e(TAG, msg); } - private void logError(String msg, Throwable e) { + protected void logError(String msg, Throwable e) { Log.e(TAG, "Error: " + msg, e); } diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java index b42b57f045..5004e97165 100644 --- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java +++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java @@ -11,14 +11,11 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.billingclient.api.SkuDetails; - import net.osmand.AndroidUtils; import net.osmand.Period; import net.osmand.Period.PeriodUnit; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.Version; import net.osmand.plus.helpers.FontCache; import net.osmand.plus.widgets.style.CustomTypefaceSpan; import net.osmand.util.Algorithms; @@ -33,64 +30,17 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -public class InAppPurchases { +public abstract class InAppPurchases { - private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion(); - private static final InAppPurchaseDepthContoursFull DEPTH_CONTOURS_FULL = new InAppPurchaseDepthContoursFull(); - private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree(); - private static final InAppPurchaseContourLinesFull CONTOUR_LINES_FULL = new InAppPurchaseContourLinesFull(); - private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree(); + protected InAppPurchase fullVersion; + protected InAppPurchase depthContours; + protected InAppPurchase contourLines; + protected InAppSubscription monthlyLiveUpdates; + protected InAppSubscription discountedMonthlyLiveUpdates; + protected InAppSubscriptionList liveUpdates; + protected InAppPurchase[] inAppPurchases; - private static final InAppSubscription[] LIVE_UPDATES_FULL = new InAppSubscription[]{ - new InAppPurchaseLiveUpdatesOldMonthlyFull(), - new InAppPurchaseLiveUpdatesMonthlyFull(), - new InAppPurchaseLiveUpdates3MonthsFull(), - new InAppPurchaseLiveUpdatesAnnualFull() - }; - - private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{ - new InAppPurchaseLiveUpdatesOldMonthlyFree(), - new InAppPurchaseLiveUpdatesMonthlyFree(), - new InAppPurchaseLiveUpdates3MonthsFree(), - new InAppPurchaseLiveUpdatesAnnualFree() - }; - - private InAppPurchase fullVersion; - private InAppPurchase depthContours; - private InAppPurchase contourLines; - private InAppSubscription monthlyLiveUpdates; - private InAppSubscription discountedMonthlyLiveUpdates; - private InAppSubscriptionList liveUpdates; - private InAppPurchase[] inAppPurchases; - - InAppPurchases(OsmandApplication ctx) { - fullVersion = FULL_VERSION; - if (Version.isFreeVersion(ctx)) { - liveUpdates = new LiveUpdatesInAppPurchasesFree(); - } else { - liveUpdates = new LiveUpdatesInAppPurchasesFull(); - } - for (InAppSubscription s : liveUpdates.getAllSubscriptions()) { - if (s instanceof InAppPurchaseLiveUpdatesMonthly) { - if (s.isDiscounted()) { - discountedMonthlyLiveUpdates = s; - } else { - monthlyLiveUpdates = s; - } - } - } - if (Version.isFreeVersion(ctx)) { - depthContours = DEPTH_CONTOURS_FREE; - } else { - depthContours = DEPTH_CONTOURS_FULL; - } - if (Version.isFreeVersion(ctx)) { - contourLines = CONTOUR_LINES_FREE; - } else { - contourLines = CONTOUR_LINES_FULL; - } - - inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines }; + protected InAppPurchases(OsmandApplication ctx) { } public InAppPurchase getFullVersion() { @@ -123,7 +73,7 @@ public class InAppPurchases { public InAppSubscription getPurchasedMonthlyLiveUpdates() { if (monthlyLiveUpdates.isAnyPurchased()) { return monthlyLiveUpdates; - } else if (discountedMonthlyLiveUpdates.isAnyPurchased()) { + } else if (discountedMonthlyLiveUpdates != null && discountedMonthlyLiveUpdates.isAnyPurchased()) { return discountedMonthlyLiveUpdates; } return null; @@ -158,31 +108,13 @@ public class InAppPurchases { return null; } - public boolean isFullVersion(String sku) { - return FULL_VERSION.getSku().equals(sku); - } + public abstract boolean isFullVersion(String sku); - public boolean isDepthContours(String sku) { - return DEPTH_CONTOURS_FULL.getSku().equals(sku) || DEPTH_CONTOURS_FREE.getSku().equals(sku); - } + public abstract boolean isDepthContours(String sku); - public boolean isContourLines(String sku) { - return CONTOUR_LINES_FULL.getSku().equals(sku) || CONTOUR_LINES_FREE.getSku().equals(sku); - } + public abstract boolean isContourLines(String sku); - public boolean isLiveUpdates(String sku) { - for (InAppPurchase p : LIVE_UPDATES_FULL) { - if (p.getSku().equals(sku)) { - return true; - } - } - for (InAppPurchase p : LIVE_UPDATES_FREE) { - if (p.getSku().equals(sku)) { - return true; - } - } - return false; - } + public abstract boolean isLiveUpdates(String sku); public abstract static class InAppSubscriptionList { @@ -260,20 +192,6 @@ public class InAppPurchases { } } - public static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList { - - public LiveUpdatesInAppPurchasesFree() { - super(LIVE_UPDATES_FREE); - } - } - - public static class LiveUpdatesInAppPurchasesFull extends InAppSubscriptionList { - - public LiveUpdatesInAppPurchasesFull() { - super(LIVE_UPDATES_FULL); - } - } - public abstract static class InAppPurchase { public enum PurchaseState { @@ -295,11 +213,11 @@ public class InAppPurchases { private NumberFormat currencyFormatter; - private InAppPurchase(@NonNull String sku) { + protected InAppPurchase(@NonNull String sku) { this.sku = sku; } - private InAppPurchase(@NonNull String sku, boolean discounted) { + protected InAppPurchase(@NonNull String sku, boolean discounted) { this(sku); this.discounted = discounted; } @@ -777,23 +695,9 @@ public class InAppPurchases { } } - public static class InAppPurchaseFullVersion extends InAppPurchase { - - private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price"; - - InAppPurchaseFullVersion() { - super(SKU_FULL_VERSION_PRICE); - } - - @Override - public String getDefaultPrice(Context ctx) { - return ctx.getString(R.string.full_version_price); - } - } - public static class InAppPurchaseDepthContours extends InAppPurchase { - private InAppPurchaseDepthContours(String sku) { + protected InAppPurchaseDepthContours(String sku) { super(sku); } @@ -803,27 +707,9 @@ public class InAppPurchases { } } - public static class InAppPurchaseDepthContoursFull extends InAppPurchaseDepthContours { - - private static final String SKU_DEPTH_CONTOURS_FULL = "net.osmand.seadepth_plus"; - - InAppPurchaseDepthContoursFull() { - super(SKU_DEPTH_CONTOURS_FULL); - } - } - - public static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours { - - private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth"; - - InAppPurchaseDepthContoursFree() { - super(SKU_DEPTH_CONTOURS_FREE); - } - } - public static class InAppPurchaseContourLines extends InAppPurchase { - private InAppPurchaseContourLines(String sku) { + protected InAppPurchaseContourLines(String sku) { super(sku); } @@ -833,25 +719,7 @@ public class InAppPurchases { } } - public static class InAppPurchaseContourLinesFull extends InAppPurchaseContourLines { - - private static final String SKU_CONTOUR_LINES_FULL = "net.osmand.contourlines_plus"; - - InAppPurchaseContourLinesFull() { - super(SKU_CONTOUR_LINES_FULL); - } - } - - public static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines { - - private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines"; - - InAppPurchaseContourLinesFree() { - super(SKU_CONTOUR_LINES_FREE); - } - } - - public static abstract class InAppPurchaseLiveUpdatesMonthly extends InAppSubscription { + protected static abstract class InAppPurchaseLiveUpdatesMonthly extends InAppSubscription { InAppPurchaseLiveUpdatesMonthly(String skuNoVersion, int version) { super(skuNoVersion, version); @@ -905,45 +773,7 @@ public class InAppPurchases { } } - public static class InAppPurchaseLiveUpdatesMonthlyFull extends InAppPurchaseLiveUpdatesMonthly { - - private static final String SKU_LIVE_UPDATES_MONTHLY_FULL = "osm_live_subscription_monthly_full"; - - InAppPurchaseLiveUpdatesMonthlyFull() { - super(SKU_LIVE_UPDATES_MONTHLY_FULL, 1); - } - - private InAppPurchaseLiveUpdatesMonthlyFull(@NonNull String sku) { - super(sku); - } - - @Nullable - @Override - protected InAppSubscription newInstance(@NonNull String sku) { - return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFull(sku) : null; - } - } - - public static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly { - - private static final String SKU_LIVE_UPDATES_MONTHLY_FREE = "osm_live_subscription_monthly_free"; - - InAppPurchaseLiveUpdatesMonthlyFree() { - super(SKU_LIVE_UPDATES_MONTHLY_FREE, 1); - } - - private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) { - super(sku); - } - - @Nullable - @Override - protected InAppSubscription newInstance(@NonNull String sku) { - return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null; - } - } - - public static abstract class InAppPurchaseLiveUpdates3Months extends InAppSubscription { + protected static abstract class InAppPurchaseLiveUpdates3Months extends InAppSubscription { InAppPurchaseLiveUpdates3Months(String skuNoVersion, int version) { super(skuNoVersion, version); @@ -986,45 +816,7 @@ public class InAppPurchases { } } - public static class InAppPurchaseLiveUpdates3MonthsFull extends InAppPurchaseLiveUpdates3Months { - - private static final String SKU_LIVE_UPDATES_3_MONTHS_FULL = "osm_live_subscription_3_months_full"; - - InAppPurchaseLiveUpdates3MonthsFull() { - super(SKU_LIVE_UPDATES_3_MONTHS_FULL, 1); - } - - private InAppPurchaseLiveUpdates3MonthsFull(@NonNull String sku) { - super(sku); - } - - @Nullable - @Override - protected InAppSubscription newInstance(@NonNull String sku) { - return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFull(sku) : null; - } - } - - public static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months { - - private static final String SKU_LIVE_UPDATES_3_MONTHS_FREE = "osm_live_subscription_3_months_free"; - - InAppPurchaseLiveUpdates3MonthsFree() { - super(SKU_LIVE_UPDATES_3_MONTHS_FREE, 1); - } - - private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) { - super(sku); - } - - @Nullable - @Override - protected InAppSubscription newInstance(@NonNull String sku) { - return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null; - } - } - - public static abstract class InAppPurchaseLiveUpdatesAnnual extends InAppSubscription { + protected static abstract class InAppPurchaseLiveUpdatesAnnual extends InAppSubscription { InAppPurchaseLiveUpdatesAnnual(String skuNoVersion, int version) { super(skuNoVersion, version); @@ -1067,44 +859,6 @@ public class InAppPurchases { } } - public static class InAppPurchaseLiveUpdatesAnnualFull extends InAppPurchaseLiveUpdatesAnnual { - - private static final String SKU_LIVE_UPDATES_ANNUAL_FULL = "osm_live_subscription_annual_full"; - - InAppPurchaseLiveUpdatesAnnualFull() { - super(SKU_LIVE_UPDATES_ANNUAL_FULL, 1); - } - - private InAppPurchaseLiveUpdatesAnnualFull(@NonNull String sku) { - super(sku); - } - - @Nullable - @Override - protected InAppSubscription newInstance(@NonNull String sku) { - return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFull(sku) : null; - } - } - - public static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual { - - private static final String SKU_LIVE_UPDATES_ANNUAL_FREE = "osm_live_subscription_annual_free"; - - InAppPurchaseLiveUpdatesAnnualFree() { - super(SKU_LIVE_UPDATES_ANNUAL_FREE, 1); - } - - private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) { - super(sku); - } - - @Nullable - @Override - protected InAppSubscription newInstance(@NonNull String sku) { - return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null; - } - } - public static class InAppPurchaseLiveUpdatesOldMonthly extends InAppPurchaseLiveUpdatesMonthly { InAppPurchaseLiveUpdatesOldMonthly(String sku) { @@ -1127,54 +881,5 @@ public class InAppPurchases { return null; } } - - public static class InAppPurchaseLiveUpdatesOldMonthlyFull extends InAppPurchaseLiveUpdatesOldMonthly { - - private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FULL = "osm_live_subscription_2"; - - InAppPurchaseLiveUpdatesOldMonthlyFull() { - super(SKU_LIVE_UPDATES_OLD_MONTHLY_FULL); - } - } - - public static class InAppPurchaseLiveUpdatesOldMonthlyFree extends InAppPurchaseLiveUpdatesOldMonthly { - - private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FREE = "osm_free_live_subscription_2"; - - InAppPurchaseLiveUpdatesOldMonthlyFree() { - super(SKU_LIVE_UPDATES_OLD_MONTHLY_FREE); - } - } - - public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription { - - private SkuDetails details; - - InAppPurchaseLiveUpdatesOldSubscription(@NonNull SkuDetails details) { - super(details.getSku(), true); - this.details = details; - } - - @Override - public String getDefaultPrice(Context ctx) { - return ""; - } - - @Override - public CharSequence getTitle(Context ctx) { - return details.getTitle(); - } - - @Override - public CharSequence getDescription(@NonNull Context ctx) { - return details.getDescription(); - } - - @Nullable - @Override - protected InAppSubscription newInstance(@NonNull String sku) { - return null; - } - } } diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java index d9824cce8f..c503162910 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java @@ -35,6 +35,8 @@ import androidx.fragment.app.FragmentManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.LocalIndexHelper; @@ -49,7 +51,6 @@ import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener; import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; import net.osmand.plus.resources.IncrementalChangesManager; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import java.io.File; @@ -312,7 +313,7 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc } public void add(LocalIndexInfo info) { - OsmandSettings.CommonPreference preference = preferenceLiveUpdatesOn( + CommonPreference preference = preferenceLiveUpdatesOn( info.getFileName(), app.getSettings()); if (preference.get()) { dataShouldUpdate.add(info); @@ -324,8 +325,8 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc public void notifyLiveUpdatesChanged() { Set changedSet = new HashSet<>(); for (LocalIndexInfo localIndexInfo : dataShouldUpdate) { - OsmandSettings.CommonPreference preference = - preferenceLiveUpdatesOn(localIndexInfo.getFileName(), app.getSettings()); + CommonPreference preference = + preferenceLiveUpdatesOn(localIndexInfo.getFileName(), getSettings()); if (!preference.get()) { changedSet.add(localIndexInfo); } @@ -334,8 +335,8 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc dataShouldNotUpdate.addAll(changedSet); changedSet.clear(); for (LocalIndexInfo localIndexInfo : dataShouldNotUpdate) { - OsmandSettings.CommonPreference preference = - preferenceLiveUpdatesOn(localIndexInfo.getFileName(), app.getSettings()); + CommonPreference preference = + preferenceLiveUpdatesOn(localIndexInfo.getFileName(), getSettings()); if (preference.get()) { changedSet.add(localIndexInfo); } @@ -473,10 +474,10 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc String fileName = li.getFileName(); PendingIntent alarmIntent = getPendingIntent(ctx, fileName); if (enable) { - final OsmandSettings.CommonPreference updateFrequencyPreference = - preferenceUpdateFrequency(fileName, app.getSettings()); - final OsmandSettings.CommonPreference timeOfDayPreference = - preferenceTimeOfDayToUpdate(fileName, app.getSettings()); + final CommonPreference updateFrequencyPreference = + preferenceUpdateFrequency(fileName, getSettings()); + final CommonPreference timeOfDayPreference = + preferenceTimeOfDayToUpdate(fileName, getSettings()); UpdateFrequency updateFrequency = UpdateFrequency.values()[updateFrequencyPreference.get()]; TimeOfDay timeOfDayToUpdate = TimeOfDay.values()[timeOfDayPreference.get()]; setAlarmForPendingIntent(alarmIntent, alarmMgr, updateFrequency, timeOfDayToUpdate); @@ -570,7 +571,7 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc public void bindLocalIndexInfo(@NonNull final String item, boolean isLastChild) { OsmandApplication context = fragment.getMyActivity().getMyApplication(); - final OsmandSettings.CommonPreference shouldUpdatePreference = + final CommonPreference shouldUpdatePreference = preferenceLiveUpdatesOn(item, fragment.getSettings()); IncrementalChangesManager changesManager = context.getResourceManager().getChangesManager(); @@ -600,7 +601,7 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc Algorithms.getFileNameWithoutExtension(new File(item)); final long timestamp = changesManager.getTimestamp(fileNameWithoutExtension); final long lastCheck = preferenceLastCheck(item, fragment.getSettings()).get(); - OsmandSettings.CommonPreference liveUpdateOn = preferenceLiveUpdatesOn(item, fragment.getSettings()); + CommonPreference liveUpdateOn = preferenceLiveUpdatesOn(item, fragment.getSettings()); if(liveUpdateOn.get() && lastCheck != DEFAULT_LAST_CHECK) { String lastCheckString = formatDateTime(fragment.getActivity(), lastCheck ); descriptionTextView.setText(context.getString(R.string.last_update, lastCheckString)); diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java index ec4afd6861..ce9d3d9c5b 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java @@ -8,6 +8,7 @@ import android.os.AsyncTask; import androidx.annotation.NonNull; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.OsmandActionBarActivity; @@ -32,7 +33,7 @@ public class LiveUpdatesHelper { public static final int DEFAULT_LAST_CHECK = -1; - private static OsmandSettings.CommonPreference checkPref(OsmandSettings.CommonPreference p) { + private static CommonPreference checkPref(CommonPreference p) { if (p.isSet()) { T vl = p.get(); p = p.makeGlobal(); @@ -44,37 +45,37 @@ public class LiveUpdatesHelper { } return p; } - public static OsmandSettings.CommonPreference preferenceForLocalIndex( + public static CommonPreference preferenceForLocalIndex( String fileName, OsmandSettings settings) { final String settingId = fileName + LIVE_UPDATES_ON_POSTFIX; return checkPref(settings.registerBooleanPreference(settingId, false)); } - public static OsmandSettings.CommonPreference preferenceLiveUpdatesOn( + public static CommonPreference preferenceLiveUpdatesOn( String fileName, OsmandSettings settings) { final String settingId = fileName + LIVE_UPDATES_ON_POSTFIX; return checkPref(settings.registerBooleanPreference(settingId, false)); } - public static OsmandSettings.CommonPreference preferenceDownloadViaWiFi( + public static CommonPreference preferenceDownloadViaWiFi( String fileName, OsmandSettings settings) { final String settingId = fileName + DOWNLOAD_VIA_WIFI_POSTFIX; return checkPref(settings.registerBooleanPreference(settingId, false)); } - public static OsmandSettings.CommonPreference preferenceUpdateFrequency( + public static CommonPreference preferenceUpdateFrequency( String fileName, OsmandSettings settings) { final String settingId = fileName + UPDATE_TIMES_POSTFIX; return checkPref(settings.registerIntPreference(settingId, UpdateFrequency.HOURLY.ordinal())); } - public static OsmandSettings.CommonPreference preferenceTimeOfDayToUpdate( + public static CommonPreference preferenceTimeOfDayToUpdate( String fileName, OsmandSettings settings) { final String settingId = fileName + TIME_OF_DAY_TO_UPDATE_POSTFIX; return checkPref(settings.registerIntPreference(settingId, TimeOfDay.NIGHT.ordinal())); } - public static OsmandSettings.CommonPreference preferenceLastCheck( + public static CommonPreference preferenceLastCheck( String fileName, OsmandSettings settings) { final String settingId = fileName + LAST_UPDATE_ATTEMPT_ON_POSTFIX; return checkPref(settings.registerLongPreference(settingId, DEFAULT_LAST_CHECK)); diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragment.java index d3f8f2ed4e..d27e733d7a 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragment.java @@ -23,6 +23,7 @@ import androidx.fragment.app.DialogFragment; import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.download.AbstractDownloadActivity; @@ -85,7 +86,7 @@ public class LiveUpdatesSettingsDialogFragment extends DialogFragment { final long lastCheck = preferenceLastCheck(fileName, getSettings()).get(); - OsmandSettings.CommonPreference preference = preferenceLiveUpdatesOn(fileName, + CommonPreference preference = preferenceLiveUpdatesOn(fileName, getSettings()); if (preference.get() && lastCheck != DEFAULT_LAST_CHECK) { String lastCheckString = formatDateTime(getActivity(), lastCheck); @@ -94,13 +95,13 @@ public class LiveUpdatesSettingsDialogFragment extends DialogFragment { lastUpdateTextView.setVisibility(View.GONE); } - final OsmandSettings.CommonPreference liveUpdatePreference = + final CommonPreference liveUpdatePreference = preferenceForLocalIndex(fileName, getSettings()); - final OsmandSettings.CommonPreference downloadViaWiFiPreference = + final CommonPreference downloadViaWiFiPreference = preferenceDownloadViaWiFi(fileName, getSettings()); - final OsmandSettings.CommonPreference updateFrequencyPreference = + final CommonPreference updateFrequencyPreference = preferenceUpdateFrequency(fileName, getSettings()); - final OsmandSettings.CommonPreference timeOfDayPreference = + final CommonPreference timeOfDayPreference = preferenceTimeOfDayToUpdate(fileName, getSettings()); downloadOverWiFiCheckBox.setChecked(!liveUpdatePreference.get() || downloadViaWiFiPreference.get()); diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java b/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java index c95ac1a2a6..4cebb245f9 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java @@ -9,6 +9,7 @@ import androidx.annotation.NonNull; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.download.AbstractDownloadActivity; @@ -50,7 +51,7 @@ public class PerformLiveUpdateAsyncTask activity.setSupportProgressBarIndeterminateVisibility(true); } final OsmandApplication myApplication = getMyApplication(); - OsmandSettings.CommonPreference lastCheckPreference = + CommonPreference lastCheckPreference = LiveUpdatesHelper.preferenceLastCheck(localIndexFileName, myApplication.getSettings()); lastCheckPreference.set(System.currentTimeMillis()); } @@ -148,7 +149,7 @@ public class PerformLiveUpdateAsyncTask public static void tryRescheduleDownload(@NonNull Context context, @NonNull OsmandSettings settings, @NonNull String localIndexFileName) { - final OsmandSettings.CommonPreference updateFrequencyPreference = + final CommonPreference updateFrequencyPreference = preferenceUpdateFrequency(localIndexFileName, settings); final Integer frequencyOrdinal = updateFrequencyPreference.get(); if (LiveUpdatesHelper.UpdateFrequency.values()[frequencyOrdinal] diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/CollapsableView.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/CollapsableView.java index 5daa14ba27..394547f570 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/CollapsableView.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/CollapsableView.java @@ -4,7 +4,7 @@ import android.view.View; import androidx.annotation.NonNull; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.mapcontextmenu.MenuBuilder.CollapseExpandListener; public class CollapsableView { diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java index 5f88a13607..7977a9ddc6 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java @@ -58,6 +58,8 @@ import net.osmand.plus.ContextMenuItem; import net.osmand.plus.LockableScrollView; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.MainContextMenuItemsSettings; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities.DialogButtonType; @@ -74,7 +76,6 @@ import net.osmand.plus.mapcontextmenu.MenuController.TitleProgressController; import net.osmand.plus.mapcontextmenu.controllers.TransportStopController; import net.osmand.plus.routepreparationmenu.ChooseRouteFragment; import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.transport.TransportStopRoute; import net.osmand.plus.views.AnimateDraggingMapThread; import net.osmand.plus.views.OsmandMapTileView; @@ -642,7 +643,7 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo // Action buttons ContextMenuAdapter adapter = menu.getActionsContextMenuAdapter(false); List items = adapter.getVisibleItems(); - List mainIds = ((OsmandSettings.MainContextMenuItemsSettings) mapActivity.getMyApplication() + List mainIds = ((MainContextMenuItemsSettings) mapActivity.getMyApplication() .getSettings().CONTEXT_MENU_ACTIONS_ITEMS.get()).getMainIds(); ContextMenuAdapter mainAdapter = new ContextMenuAdapter(requireMyApplication()); ContextMenuAdapter additionalAdapter = new ContextMenuAdapter(requireMyApplication()); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java index 5a4618ca4f..f0d33b68dd 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java @@ -36,8 +36,7 @@ import net.osmand.plus.mapcontextmenu.CollapsableView; import net.osmand.plus.mapcontextmenu.MenuBuilder; import net.osmand.plus.osmedit.OsmEditingPlugin; import net.osmand.plus.poi.PoiUIFilter; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.MetricsConstants; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.views.layers.POIMapLayer; import net.osmand.plus.widgets.TextViewEx; import net.osmand.plus.widgets.tools.ClickableSpanTouchListener; @@ -70,7 +69,7 @@ public class AmenityMenuBuilder extends MenuBuilder { private static final String WIKI_LINK = ".wikipedia.org/w"; public final static Log LOG = PlatformUtil.getLog(AmenityMenuBuilder.class); private final static DecimalFormat DF = new DecimalFormat("#.##"); - private OsmandSettings.MetricsConstants metricSystem; + private MetricsConstants metricSystem; private final Amenity amenity; @@ -714,10 +713,10 @@ public class AmenityMenuBuilder extends MenuBuilder { case "seamark_height": if (Algorithms.isFloat(value)) { double valueAsDouble = Double.valueOf(value); - if (metricSystem == OsmandSettings.MetricsConstants.MILES_AND_FEET) { + if (metricSystem == MetricsConstants.MILES_AND_FEET) { formattedValue = String.valueOf(DF.format(valueAsDouble * OsmAndFormatter.FEET_IN_ONE_METER)) + " " + mapActivity.getResources().getString(R.string.foot); - } else if (metricSystem == OsmandSettings.MetricsConstants.MILES_AND_YARDS) { + } else if (metricSystem == MetricsConstants.MILES_AND_YARDS) { formattedValue = String.valueOf(DF.format(valueAsDouble * OsmAndFormatter.YARDS_IN_ONE_METER)) + " " + mapActivity.getResources().getString(R.string.yard); } else { diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/IPFSImageCard.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/IPFSImageCard.java new file mode 100644 index 0000000000..45495ca8d0 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/IPFSImageCard.java @@ -0,0 +1,43 @@ +package net.osmand.plus.mapcontextmenu.builders.cards; + +import android.view.View; +import net.osmand.AndroidNetworkUtils; +import net.osmand.PlatformUtil; +import net.osmand.plus.activities.MapActivity; +import net.osmand.util.Algorithms; +import org.apache.commons.logging.Log; +import org.json.JSONException; +import org.json.JSONObject; + +public class IPFSImageCard extends ImageCard { + private static final String BASE_URL = "https://test.openplacereviews.org/api/ipfs/image-ipfs?cid="; + private static final Log LOG = PlatformUtil.getLog(IPFSImageCard.class); + + public IPFSImageCard(MapActivity mapActivity, JSONObject imageObject) { + super(mapActivity, imageObject); + String cid = ""; + try { + cid = (String) imageObject.get("cid"); + } catch (JSONException e) { + LOG.error(e); + } + url = BASE_URL + cid; + imageHiresUrl = BASE_URL + cid; + imageUrl = BASE_URL + cid; + if (!Algorithms.isEmpty(getUrl())) { + View.OnClickListener onClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + openUrl(getMapActivity(), getMyApplication(), getTitle(), getUrl(), + isExternalLink() || Algorithms.isEmpty(getImageHiresUrl()), + !Algorithms.isEmpty(getImageHiresUrl())); + } + }; + if (!Algorithms.isEmpty(buttonText)) { + onButtonClickListener = onClickListener; + } else { + this.onClickListener = onClickListener; + } + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java index c7cf92df0f..1afb883231 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java @@ -2,7 +2,9 @@ package net.osmand.plus.mapcontextmenu.builders.cards; import android.content.res.ColorStateList; import android.graphics.Bitmap; +import android.graphics.PointF; import android.graphics.drawable.Drawable; +import android.net.TrafficStats; import android.os.AsyncTask; import android.view.View; import android.view.View.OnClickListener; @@ -14,21 +16,28 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatButton; -import net.osmand.AndroidNetworkUtils; -import net.osmand.AndroidUtils; -import net.osmand.Location; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.osmand.*; import net.osmand.data.Amenity; +import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; +import net.osmand.data.RotatedTileBox; +import net.osmand.osm.PoiType; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.SettingsBaseActivity; import net.osmand.plus.mapcontextmenu.MenuBuilder; import net.osmand.plus.mapillary.MapillaryContributeCard; import net.osmand.plus.mapillary.MapillaryImageCard; +import net.osmand.plus.osmedit.OsmBugsLayer; +import net.osmand.plus.views.layers.ContextMenuLayer; import net.osmand.plus.wikimedia.WikiImageHelper; import net.osmand.util.Algorithms; +import org.apache.commons.logging.Log; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -36,18 +45,16 @@ import org.json.JSONObject; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; + +import static net.osmand.data.FavouritePoint.DEFAULT_BACKGROUND_TYPE; public abstract class ImageCard extends AbstractCard { public static String TYPE_MAPILLARY_PHOTO = "mapillary-photo"; public static String TYPE_MAPILLARY_CONTRIBUTE = "mapillary-contribute"; + private static final Log LOG = PlatformUtil.getLog(ImageCard.class); protected String type; // Image location protected LatLon location; @@ -191,6 +198,14 @@ public abstract class ImageCard extends AbstractCard { return imageCard; } + private static ImageCard createCardOpr(MapActivity mapActivity, JSONObject imageObject) { + ImageCard imageCard = null; + if (imageObject.has("cid")) { + imageCard = new IPFSImageCard(mapActivity, imageObject); + } + return imageCard; + } + public double getCa() { return ca; } @@ -403,6 +418,7 @@ public abstract class ImageCard extends AbstractCard { private Map params; private GetImageCardsListener listener; private List result; + private static final int GET_IMAGE_CARD_THREAD_ID = 10104; public interface GetImageCardsListener { void onPostProcess(List cardList); @@ -411,7 +427,7 @@ public abstract class ImageCard extends AbstractCard { } public GetImageCardsTask(@NonNull MapActivity mapActivity, LatLon latLon, - @Nullable Map params, GetImageCardsListener listener) { + @Nullable Map params, GetImageCardsListener listener) { this.mapActivity = mapActivity; this.app = mapActivity.getMyApplication(); this.latLon = latLon; @@ -421,7 +437,14 @@ public abstract class ImageCard extends AbstractCard { @Override protected List doInBackground(Void... params) { + TrafficStats.setThreadStatsTag(GET_IMAGE_CARD_THREAD_ID); List result = new ArrayList<>(); + Object o = mapActivity.getMapLayers().getContextMenuLayer().getSelectedObject(); + if (o instanceof Amenity) { + Amenity am = (Amenity) o; + long amenityId = am.getId() >> 1; + getPicturesForPlace(result, amenityId); + } try { final Map pms = new LinkedHashMap<>(); pms.put("lat", "" + (float) latLon.getLatitude()); @@ -482,6 +505,35 @@ public abstract class ImageCard extends AbstractCard { return result; } + private void getPicturesForPlace(List result, long l) { + String url = "https://test.openplacereviews.org/api/objects-by-index?type=opr.place&index=osmid&limit=1&key=" + l; + String response = AndroidNetworkUtils.sendRequest(app, url, Collections.emptyMap(), + "Requesting location images...", false, false); + try { + if (!Algorithms.isEmpty(response)) { + JSONArray obj = new JSONObject(response).getJSONArray("objects"); + JSONArray images = ((JSONObject) ((JSONObject) obj.get(0)).get("images")).getJSONArray("outdoor"); + if (images.length() > 0) { + for (int i = 0; i < images.length(); i++) { + try { + JSONObject imageObject = (JSONObject) images.get(i); + if (imageObject != JSONObject.NULL) { + ImageCard imageCard = ImageCard.createCardOpr(mapActivity, imageObject); + if (imageCard != null) { + result.add(imageCard); + } + } + } catch (JSONException e) { + LOG.error(e); + } + } + } + } + } catch (Exception e) { + LOG.error(e); + } + } + @Override protected void onPostExecute(List cardList) { result = cardList; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapMarkerMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapMarkerMenuController.java index 3cdf3398a6..b8f5789d47 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapMarkerMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapMarkerMenuController.java @@ -13,7 +13,7 @@ import androidx.core.content.ContextCompat; import net.osmand.data.PointDescription; import net.osmand.plus.MapMarkersHelper; import net.osmand.plus.MapMarkersHelper.MapMarker; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.MapMarkerDialogHelper; @@ -56,7 +56,7 @@ public class MapMarkerMenuController extends MenuController { public void buttonPressed() { MapActivity activity = getMapActivity(); if (activity != null) { - OsmandSettings.OsmandPreference indication + OsmandPreference indication = activity.getMyApplication().getSettings().MARKERS_DISTANCE_INDICATION_ENABLED; if (!indication.get()) { indication.set(true); diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputBottomSheetDialogFragment.java index cf769223f1..f3bf7cfb7a 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputBottomSheetDialogFragment.java @@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.View; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.base.MenuBottomSheetDialogFragment; @@ -83,7 +84,7 @@ public class CoordinateInputBottomSheetDialogFragment extends MenuBottomSheetDia @Override public void onClick(View v) { if (listener != null) { - OsmandSettings.CommonPreference pref = settings.COORDS_INPUT_TWO_DIGITS_LONGTITUDE; + CommonPreference pref = settings.COORDS_INPUT_TWO_DIGITS_LONGTITUDE; pref.set(!pref.get()); listener.onInputSettingsChanged(); } @@ -108,7 +109,7 @@ public class CoordinateInputBottomSheetDialogFragment extends MenuBottomSheetDia @Override public void onClick(View v) { if (listener != null) { - OsmandSettings.CommonPreference pref = settings.COORDS_INPUT_USE_RIGHT_SIDE; + CommonPreference pref = settings.COORDS_INPUT_USE_RIGHT_SIDE; pref.set(!pref.get()); listener.onHandChanged(); } diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java index a68bd4c714..aef217f347 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java @@ -67,6 +67,7 @@ import net.osmand.plus.MapMarkersHelper; import net.osmand.plus.OsmAndLocationProvider.OsmAndCompassListener; import net.osmand.plus.OsmAndLocationProvider.OsmAndLocationListener; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.Version; @@ -1023,7 +1024,7 @@ public class CoordinateInputDialogFragment extends DialogFragment implements Osm } private void changeOsmandKeyboardSetting() { - OsmandSettings.OsmandPreference pref = getMyApplication().getSettings().COORDS_INPUT_USE_OSMAND_KEYBOARD; + OsmandPreference pref = getMyApplication().getSettings().COORDS_INPUT_USE_OSMAND_KEYBOARD; pref.set(!pref.get()); } diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/DirectionIndicationDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/DirectionIndicationDialogFragment.java index 485f294c94..d624ec7947 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/DirectionIndicationDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/DirectionIndicationDialogFragment.java @@ -32,8 +32,7 @@ import com.github.ksoichiro.android.observablescrollview.ScrollState; import net.osmand.AndroidUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.MapMarkersMode; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersMode.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersMode.java new file mode 100644 index 0000000000..a9cf23ea75 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersMode.java @@ -0,0 +1,37 @@ +package net.osmand.plus.mapmarkers; + +import android.content.Context; + +import net.osmand.plus.R; + +public enum MapMarkersMode { + TOOLBAR(R.string.shared_string_topbar), + WIDGETS(R.string.shared_string_widgets), + NONE(R.string.shared_string_none); + + private final int key; + + MapMarkersMode(int key) { + this.key = key; + } + + public String toHumanString(Context ctx) { + return ctx.getString(key); + } + + public boolean isToolbar() { + return this == TOOLBAR; + } + + public boolean isWidgets() { + return this == WIDGETS; + } + + public boolean isNone() { + return this == NONE; + } + + public static MapMarkersMode[] possibleValues(Context context) { + return new MapMarkersMode[]{TOOLBAR, WIDGETS, NONE}; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/OptionsBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/OptionsBottomSheetDialogFragment.java index 487ee2073c..4627ed764b 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/OptionsBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/OptionsBottomSheetDialogFragment.java @@ -14,7 +14,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import net.osmand.AndroidUtils; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.base.BottomSheetDialogFragment; import net.osmand.plus.helpers.AndroidUiHelper; @@ -63,7 +62,7 @@ public class OptionsBottomSheetDialogFragment extends BottomSheetDialogFragment } ((ImageView) mainView.findViewById(R.id.sort_by_icon)).setImageDrawable(getContentIcon(R.drawable.ic_sort_waypoint_dark)); - OsmandSettings.MapMarkersMode mode = getMyApplication().getSettings().MAP_MARKERS_MODE.get(); + MapMarkersMode mode = getMyApplication().getSettings().MAP_MARKERS_MODE.get(); int displayedCount = getMyApplication().getSettings().DISPLAYED_MARKERS_WIDGETS_COUNT.get(); ImageView showDirectionIcon = (ImageView) mainView.findViewById(R.id.show_direction_icon); int imageResId = 0; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/ExitBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/ExitBottomSheetDialogFragment.java index df7b4ae9b7..1bdd3ffdf2 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/ExitBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/ExitBottomSheetDialogFragment.java @@ -1,7 +1,6 @@ package net.osmand.plus.measurementtool; import android.os.Bundle; -import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -9,9 +8,8 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import net.osmand.plus.R; -import net.osmand.plus.UiUtilities; +import net.osmand.plus.UiUtilities.DialogButtonType; import net.osmand.plus.base.MenuBottomSheetDialogFragment; -import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemButton; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.ShortDescriptionItem; @@ -35,39 +33,6 @@ public class ExitBottomSheetDialogFragment extends MenuBottomSheetDialogFragment items.add(new DividerSpaceItem(getContext(), getResources().getDimensionPixelSize(R.dimen.bottom_sheet_exit_button_margin))); - items.add(new BottomSheetItemButton.Builder() - .setButtonType(UiUtilities.DialogButtonType.SECONDARY) - .setTitle(getString(R.string.shared_string_exit)) - .setLayoutId(R.layout.bottom_sheet_button) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Fragment targetFragment = getTargetFragment(); - if (targetFragment != null) { - targetFragment.onActivityResult(REQUEST_CODE, EXIT_RESULT_CODE, null); - } - dismiss(); - } - }) - .create()); - - items.add(new DividerSpaceItem(getContext(), - getResources().getDimensionPixelSize(R.dimen.bottom_sheet_icon_margin))); - - items.add(new BottomSheetItemButton.Builder() - .setTitle(getString(R.string.shared_string_save)) - .setLayoutId(R.layout.bottom_sheet_button) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Fragment targetFragment = getTargetFragment(); - if (targetFragment != null) { - targetFragment.onActivityResult(REQUEST_CODE, SAVE_RESULT_CODE, null); - } - dismiss(); - } - }) - .create()); } @Override @@ -75,6 +40,44 @@ public class ExitBottomSheetDialogFragment extends MenuBottomSheetDialogFragment return R.string.shared_string_cancel; } + @Override + protected int getRightBottomButtonTextId() { + return R.string.shared_string_save; + } + + @Override + protected int getThirdBottomButtonTextId() { + return R.string.shared_string_exit; + } + + @Override + public int getSecondDividerHeight() { + return getResources().getDimensionPixelSize(R.dimen.bottom_sheet_icon_margin); + } + + @Override + protected void onRightBottomButtonClick() { + Fragment targetFragment = getTargetFragment(); + if (targetFragment != null) { + targetFragment.onActivityResult(REQUEST_CODE, SAVE_RESULT_CODE, null); + } + dismiss(); + } + + @Override + protected void onThirdBottomButtonClick() { + Fragment targetFragment = getTargetFragment(); + if (targetFragment != null) { + targetFragment.onActivityResult(REQUEST_CODE, EXIT_RESULT_CODE, null); + } + dismiss(); + } + + @Override + protected DialogButtonType getThirdBottomButtonType() { + return (DialogButtonType.SECONDARY); + } + public static void showInstance(@NonNull FragmentManager fragmentManager, @Nullable Fragment targetFragment) { if (!fragmentManager.isStateSaved()) { ExitBottomSheetDialogFragment fragment = new ExitBottomSheetDialogFragment(); @@ -82,4 +85,4 @@ public class ExitBottomSheetDialogFragment extends MenuBottomSheetDialogFragment fragment.show(fragmentManager, TAG); } } -} +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java index 7d6818a9d6..83ef171fda 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java @@ -2,7 +2,6 @@ package net.osmand.plus.measurementtool; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; @@ -38,7 +37,6 @@ import net.osmand.AndroidUtils; import net.osmand.FileUtils; import net.osmand.GPXUtilities; import net.osmand.GPXUtilities.GPXFile; -import net.osmand.GPXUtilities.Track; import net.osmand.GPXUtilities.TrkSegment; import net.osmand.GPXUtilities.WptPt; import net.osmand.LocationsHolder; @@ -83,13 +81,9 @@ import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControll import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType; import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarView; import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation; -import net.osmand.util.Algorithms; import java.io.File; -import java.lang.ref.WeakReference; -import java.text.MessageFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; @@ -151,13 +145,16 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route private MeasurementEditingContext editingCtx = new MeasurementEditingContext(); private LatLon initialPoint; + private OsmandApplication app; + private MapActivity mapActivity; + private MeasurementToolLayer measurementToolLayer; - private enum SaveType { + enum SaveType { ROUTE_POINT, LINE } - private enum FinalSaveAction { + enum FinalSaveAction { SHOW_SNACK_BAR_AND_CLOSE, SHOW_TOAST, SHOW_IS_SAVED_FRAGMENT @@ -171,7 +168,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route this.initialPoint = initialPoint; } - private void setMode(int mode, boolean on) { + void setMode(int mode, boolean on) { int modes = this.modes; if (on) { modes |= mode; @@ -181,7 +178,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route this.modes = modes; } - private boolean isPlanRouteMode() { + boolean isPlanRouteMode() { return (this.modes & PLAN_ROUTE_MODE) == PLAN_ROUTE_MODE; } @@ -1062,7 +1059,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } @Nullable - private GpxData setupGpxData(@Nullable GPXFile gpxFile) { + GpxData setupGpxData(@Nullable GPXFile gpxFile) { GpxData gpxData = null; if (gpxFile != null) { QuadRect rect = gpxFile.getRect(); @@ -1284,7 +1281,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route editingCtx.splitSegments(editingCtx.getBeforePoints().size() + editingCtx.getAfterPoints().size()); } - private void cancelModes() { + void cancelModes() { if (editingCtx.getOriginalPointToMove() != null) { cancelMovePointMode(); } else if (editingCtx.isInAddPointMode()) { @@ -1545,247 +1542,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route final SaveType saveType, final FinalSaveAction finalSaveAction) { - new AsyncTask() { - - private ProgressDialog progressDialog; - private File backupFile; - private File outFile; - private GPXFile savedGpxFile; - - @Override - protected void onPreExecute() { - cancelModes(); - MapActivity activity = getMapActivity(); - if (activity != null) { - progressDialog = new ProgressDialog(activity); - progressDialog.setMessage(getString(R.string.saving_gpx_tracks)); - progressDialog.show(); - } - } - - @Override - protected Exception doInBackground(Void... voids) { - MeasurementToolLayer measurementLayer = getMeasurementLayer(); - OsmandApplication app = getMyApplication(); - if (app == null) { - return null; - } - List points = editingCtx.getPoints(); - TrkSegment before = editingCtx.getBeforeTrkSegmentLine(); - TrkSegment after = editingCtx.getAfterTrkSegmentLine(); - if (gpxFile == null) { - outFile = new File(dir, fileName); - String trackName = fileName.substring(0, fileName.length() - GPX_FILE_EXT.length()); - GPXFile gpx = new GPXFile(Version.getFullVersion(app)); - if (measurementLayer != null) { - if (saveType == SaveType.LINE) { - TrkSegment segment = new TrkSegment(); - if (editingCtx.hasRoute()) { - segment.points.addAll(editingCtx.getRoutePoints()); - } else { - segment.points.addAll(before.points); - segment.points.addAll(after.points); - } - Track track = new Track(); - track.name = trackName; - track.segments.add(segment); - gpx.tracks.add(track); - } else if (saveType == SaveType.ROUTE_POINT) { - if (editingCtx.hasRoute()) { - GPXFile newGpx = editingCtx.exportRouteAsGpx(trackName); - if (newGpx != null) { - gpx = newGpx; - } - } - gpx.addRoutePoints(points); - } - } - Exception res = GPXUtilities.writeGpxFile(outFile, gpx); - gpx.path = outFile.getAbsolutePath(); - savedGpxFile = gpx; - if (showOnMap) { - showGpxOnMap(app, gpx, true); - } - return res; - } else { - GPXFile gpx = gpxFile; - outFile = new File(gpx.path); - backupFile = FileUtils.backupFile(app, outFile); - String trackName = Algorithms.getFileNameWithoutExtension(outFile); - if (measurementLayer != null) { - if (isPlanRouteMode()) { - if (saveType == SaveType.LINE) { - TrkSegment segment = new TrkSegment(); - if (editingCtx.hasRoute()) { - segment.points.addAll(editingCtx.getRoutePoints()); - } else { - segment.points.addAll(before.points); - segment.points.addAll(after.points); - } - Track track = new Track(); - track.name = trackName; - track.segments.add(segment); - gpx.tracks.add(track); - } else if (saveType == SaveType.ROUTE_POINT) { - if (editingCtx.hasRoute()) { - GPXFile newGpx = editingCtx.exportRouteAsGpx(trackName); - if (newGpx != null) { - gpx = newGpx; - } - } - gpx.addRoutePoints(points); - } - } else if (actionType != null) { - GpxData gpxData = editingCtx.getGpxData(); - switch (actionType) { - case ADD_SEGMENT: { - List snappedPoints = new ArrayList<>(); - snappedPoints.addAll(before.points); - snappedPoints.addAll(after.points); - gpx.addTrkSegment(snappedPoints); - break; - } - case ADD_ROUTE_POINTS: { - gpx.replaceRoutePoints(points); - break; - } - case EDIT_SEGMENT: { - if (gpxData != null) { - TrkSegment segment = new TrkSegment(); - segment.points.addAll(points); - gpx.replaceSegment(gpxData.getTrkSegment(), segment); - } - break; - } - case OVERWRITE_SEGMENT: { - if (gpxData != null) { - List snappedPoints = new ArrayList<>(); - snappedPoints.addAll(before.points); - snappedPoints.addAll(after.points); - TrkSegment segment = new TrkSegment(); - segment.points.addAll(snappedPoints); - gpx.replaceSegment(gpxData.getTrkSegment(), segment); - } - break; - } - } - } else { - gpx.addRoutePoints(points); - } - } - Exception res = null; - if (!gpx.showCurrentTrack) { - res = GPXUtilities.writeGpxFile(outFile, gpx); - } - savedGpxFile = gpx; - if (showOnMap) { - showGpxOnMap(app, gpx, false); - } - return res; - } - } - - private void showGpxOnMap(OsmandApplication app, GPXFile gpx, boolean isNewGpx) { - SelectedGpxFile sf = app.getSelectedGpxHelper().selectGpxFile(gpx, true, false); - if (sf != null && !isNewGpx) { - if (actionType == ActionType.ADD_SEGMENT || actionType == ActionType.EDIT_SEGMENT) { - sf.processPoints(getMyApplication()); - } - } - } - - @Override - protected void onPostExecute(Exception warning) { - onGpxSaved(warning); - } - - private void onGpxSaved(Exception warning) { - MapActivity mapActivity = getMapActivity(); - if (mapActivity == null) { - return; - } - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); - } - mapActivity.refreshMap(); - if (warning == null) { - if (editingCtx.isNewData() && savedGpxFile != null) { - QuadRect rect = savedGpxFile.getRect(); - TrkSegment segment = savedGpxFile.getNonEmptyTrkSegment(); - GpxData gpxData = new GpxData(savedGpxFile, rect, ActionType.EDIT_SEGMENT, segment); - editingCtx.setGpxData(gpxData); - updateToolbar(); - } - if (isInEditMode()) { - editingCtx.setChangesSaved(); - dismiss(mapActivity); - } else { - switch (finalSaveAction) { - case SHOW_SNACK_BAR_AND_CLOSE: - final WeakReference mapActivityRef = new WeakReference<>(mapActivity); - snackbar = Snackbar.make(mapActivity.getLayout(), - MessageFormat.format(getString(R.string.gpx_saved_sucessfully), outFile.getName()), - Snackbar.LENGTH_LONG) - .setAction(R.string.shared_string_undo, new OnClickListener() { - @Override - public void onClick(View view) { - MapActivity mapActivity = mapActivityRef.get(); - if (mapActivity != null) { - if (outFile != null) { - OsmandApplication app = mapActivity.getMyApplication(); - FileUtils.removeGpxFile(app, outFile); - if (backupFile != null) { - FileUtils.renameGpxFile(app, backupFile, outFile); - GPXFile gpx = GPXUtilities.loadGPXFile(outFile); - setupGpxData(gpx); - if (showOnMap) { - showGpxOnMap(app, gpx, false); - } - } else { - setupGpxData(null); - } - } - setMode(UNDO_MODE, true); - MeasurementToolFragment.showInstance(mapActivity.getSupportFragmentManager(), - editingCtx, modes); - } - } - }) - .addCallback(new Snackbar.Callback() { - @Override - public void onDismissed(Snackbar transientBottomBar, int event) { - if (event != DISMISS_EVENT_ACTION) { - editingCtx.setChangesSaved(); - } - super.onDismissed(transientBottomBar, event); - } - }); - snackbar.getView().findViewById(com.google.android.material.R.id.snackbar_action) - .setAllCaps(false); - UiUtilities.setupSnackbar(snackbar, nightMode); - snackbar.show(); - dismiss(mapActivity, false); - break; - case SHOW_IS_SAVED_FRAGMENT: - editingCtx.setChangesSaved(); - SavedTrackBottomSheetDialogFragment.showInstance(mapActivity.getSupportFragmentManager(), - outFile.getAbsolutePath()); - dismiss(mapActivity); - break; - case SHOW_TOAST: - editingCtx.setChangesSaved(); - if (!savedGpxFile.showCurrentTrack) { - Toast.makeText(mapActivity, - MessageFormat.format(getString(R.string.gpx_saved_sucessfully), outFile.getAbsolutePath()), - Toast.LENGTH_LONG).show(); - } - } - } - } else { - Toast.makeText(mapActivity, warning.getMessage(), Toast.LENGTH_LONG).show(); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new SaveGPX(this, gpxFile, dir, fileName, saveType, showOnMap, actionType, finalSaveAction, app, mapActivity, measurementToolLayer, nightMode).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void updateUndoRedoButton(boolean enable, View view) { @@ -1818,7 +1575,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route updateToolbar(); } - private void updateToolbar() { + void updateToolbar() { MapActivity mapActivity = getMapActivity(); if (mapActivity == null) { return; @@ -1949,7 +1706,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } } - private void dismiss(@NonNull MapActivity mapActivity) { + void dismiss(@NonNull MapActivity mapActivity) { dismiss(mapActivity, true); } @@ -2171,4 +1928,5 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route public boolean isNightModeForMapControls() { return nightMode; } + } diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/SaveGPX.java b/OsmAnd/src/net/osmand/plus/measurementtool/SaveGPX.java new file mode 100644 index 0000000000..de47191ab5 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/SaveGPX.java @@ -0,0 +1,297 @@ +package net.osmand.plus.measurementtool; + +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.android.material.snackbar.Snackbar; + +import net.osmand.FileUtils; +import net.osmand.GPXUtilities; +import net.osmand.data.QuadRect; +import net.osmand.plus.GpxSelectionHelper; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.Version; +import net.osmand.plus.activities.MapActivity; +import net.osmand.util.Algorithms; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import static net.osmand.IndexConstants.GPX_FILE_EXT; + +class SaveGPX extends AsyncTask { + + private final MeasurementToolFragment measurementToolFragment; + private final GPXUtilities.GPXFile gpxFile; + private final File dir; + private final String fileName; + private final MeasurementToolFragment.SaveType saveType; + private final boolean showOnMap; + private final GpxData.ActionType actionType; + private final MeasurementToolFragment.FinalSaveAction finalSaveAction; + private final OsmandApplication app; + @SuppressLint("StaticFieldLeak") + private final MapActivity mapActivity; + private MeasurementToolLayer measurementToolLayer; + private boolean nightMode; + private ProgressDialog progressDialog; + private File backupFile; + private File outFile; + private GPXUtilities.GPXFile savedGpxFile; + public MeasurementEditingContext editingCtx = new MeasurementEditingContext(); + private static final int UNDO_MODE = 0x8; + + public SaveGPX(MeasurementToolFragment measurementToolFragment, GPXUtilities.GPXFile gpxFile, File dir, String fileName, MeasurementToolFragment.SaveType saveType, boolean showOnMap, GpxData.ActionType actionType, MeasurementToolFragment.FinalSaveAction finalSaveAction, OsmandApplication app, MapActivity mapActivity, MeasurementToolLayer measurementToolLayer, boolean nightMode) { + this.measurementToolFragment = measurementToolFragment; + this.gpxFile = gpxFile; + this.dir = dir; + this.fileName = fileName; + this.saveType = saveType; + this.showOnMap = showOnMap; + this.actionType = actionType; + this.finalSaveAction = finalSaveAction; + this.app = app; + this.mapActivity = mapActivity; + this.measurementToolLayer = measurementToolLayer; + this.nightMode = nightMode; + } + + @Override + protected void onPreExecute() { + measurementToolFragment.cancelModes(); + if (mapActivity != null) { + progressDialog = new ProgressDialog(mapActivity); + progressDialog.setMessage(measurementToolFragment.getString(R.string.saving_gpx_tracks)); + progressDialog.show(); + } + } + + @Override + protected Exception doInBackground(Void... voids) { + if (app == null) { + return null; + } + List points = editingCtx.getPoints(); + GPXUtilities.TrkSegment before = editingCtx.getBeforeTrkSegmentLine(); + GPXUtilities.TrkSegment after = editingCtx.getAfterTrkSegmentLine(); + if (gpxFile == null) { + outFile = new File(dir, fileName); + String trackName = fileName.substring(0, fileName.length() - GPX_FILE_EXT.length()); + GPXUtilities.GPXFile gpx = new GPXUtilities.GPXFile(Version.getFullVersion(app)); + if (measurementToolLayer != null) { + if (saveType == MeasurementToolFragment.SaveType.LINE) { + GPXUtilities.TrkSegment segment = new GPXUtilities.TrkSegment(); + if (editingCtx.hasRoute()) { + segment.points.addAll(editingCtx.getRoutePoints()); + } else { + segment.points.addAll(before.points); + segment.points.addAll(after.points); + } + GPXUtilities.Track track = new GPXUtilities.Track(); + track.name = trackName; + track.segments.add(segment); + gpx.tracks.add(track); + } else if (saveType == MeasurementToolFragment.SaveType.ROUTE_POINT) { + if (editingCtx.hasRoute()) { + GPXUtilities.GPXFile newGpx = editingCtx.exportRouteAsGpx(trackName); + if (newGpx != null) { + gpx = newGpx; + } + } + gpx.addRoutePoints(points); + } + } + Exception res = GPXUtilities.writeGpxFile(outFile, gpx); + gpx.path = outFile.getAbsolutePath(); + savedGpxFile = gpx; + if (showOnMap) { + showGpxOnMap(app, gpx, true); + } + return res; + } else { + GPXUtilities.GPXFile gpx = gpxFile; + outFile = new File(gpx.path); + backupFile = FileUtils.backupFile(app, outFile); + String trackName = Algorithms.getFileNameWithoutExtension(outFile); + if (measurementToolLayer != null) { + if (measurementToolFragment.isPlanRouteMode()) { + if (saveType == MeasurementToolFragment.SaveType.LINE) { + GPXUtilities.TrkSegment segment = new GPXUtilities.TrkSegment(); + if (editingCtx.hasRoute()) { + segment.points.addAll(editingCtx.getRoutePoints()); + } else { + segment.points.addAll(before.points); + segment.points.addAll(after.points); + } + GPXUtilities.Track track = new GPXUtilities.Track(); + track.name = trackName; + track.segments.add(segment); + gpx.tracks.add(track); + } else if (saveType == MeasurementToolFragment.SaveType.ROUTE_POINT) { + if (editingCtx.hasRoute()) { + GPXUtilities.GPXFile newGpx = editingCtx.exportRouteAsGpx(trackName); + if (newGpx != null) { + gpx = newGpx; + } + } + gpx.addRoutePoints(points); + } + } else if (actionType != null) { + GpxData gpxData = editingCtx.getGpxData(); + switch (actionType) { + case ADD_SEGMENT: { + List snappedPoints = new ArrayList<>(); + snappedPoints.addAll(before.points); + snappedPoints.addAll(after.points); + gpx.addTrkSegment(snappedPoints); + break; + } + case ADD_ROUTE_POINTS: { + gpx.replaceRoutePoints(points); + break; + } + case EDIT_SEGMENT: { + if (gpxData != null) { + GPXUtilities.TrkSegment segment = new GPXUtilities.TrkSegment(); + segment.points.addAll(points); + gpx.replaceSegment(gpxData.getTrkSegment(), segment); + } + break; + } + case OVERWRITE_SEGMENT: { + if (gpxData != null) { + List snappedPoints = new ArrayList<>(); + snappedPoints.addAll(before.points); + snappedPoints.addAll(after.points); + GPXUtilities.TrkSegment segment = new GPXUtilities.TrkSegment(); + segment.points.addAll(snappedPoints); + gpx.replaceSegment(gpxData.getTrkSegment(), segment); + } + break; + } + } + } else { + gpx.addRoutePoints(points); + } + } + Exception res = null; + if (!gpx.showCurrentTrack) { + res = GPXUtilities.writeGpxFile(outFile, gpx); + } + savedGpxFile = gpx; + if (showOnMap) { + showGpxOnMap(app, gpx, false); + } + return res; + } + } + + private void showGpxOnMap(OsmandApplication app, GPXUtilities.GPXFile gpx, boolean isNewGpx) { + GpxSelectionHelper.SelectedGpxFile sf = app.getSelectedGpxHelper().selectGpxFile(gpx, true, false); + if (sf != null && !isNewGpx) { + if (actionType == GpxData.ActionType.ADD_SEGMENT || actionType == GpxData.ActionType.EDIT_SEGMENT) { + sf.processPoints(app); + } + } + } + + @Override + protected void onPostExecute(Exception warning) { + onGpxSaved(warning); + } + + private void onGpxSaved(Exception warning) { + if (mapActivity == null) { + return; + } + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + mapActivity.refreshMap(); + if (warning == null) { + if (editingCtx.isNewData() && savedGpxFile != null) { + QuadRect rect = savedGpxFile.getRect(); + GPXUtilities.TrkSegment segment = savedGpxFile.getNonEmptyTrkSegment(); + GpxData gpxData = new GpxData(savedGpxFile, rect, GpxData.ActionType.EDIT_SEGMENT, segment); + editingCtx.setGpxData(gpxData); + measurementToolFragment.updateToolbar(); + } + if (measurementToolFragment.isInEditMode()) { + editingCtx.setChangesSaved(); + measurementToolFragment.dismiss(mapActivity); + } else { + switch (finalSaveAction) { + case SHOW_SNACK_BAR_AND_CLOSE: + final WeakReference mapActivityRef = new WeakReference<>(mapActivity); + Snackbar snackbar = Snackbar.make(mapActivity.getLayout(), + MessageFormat.format(measurementToolFragment.getString(R.string.gpx_saved_sucessfully), outFile.getName()), + Snackbar.LENGTH_LONG) + .setAction(R.string.shared_string_undo, new View.OnClickListener() { + @Override + public void onClick(View view) { + MapActivity mapActivity = mapActivityRef.get(); + if (mapActivity != null) { + if (outFile != null) { + OsmandApplication app = mapActivity.getMyApplication(); + FileUtils.removeGpxFile(app, outFile); + if (backupFile != null) { + FileUtils.renameGpxFile(app, backupFile, outFile); + GPXUtilities.GPXFile gpx = GPXUtilities.loadGPXFile(outFile); + measurementToolFragment.setupGpxData(gpx); + if (showOnMap) { + showGpxOnMap(app, gpx, false); + } + } else { + measurementToolFragment.setupGpxData(null); + } + } + measurementToolFragment.setMode(UNDO_MODE, true); + MeasurementToolFragment.showInstance(mapActivity.getSupportFragmentManager() + ); + } + } + }) + .addCallback(new Snackbar.Callback() { + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + if (event != DISMISS_EVENT_ACTION) { + editingCtx.setChangesSaved(); + } + super.onDismissed(transientBottomBar, event); + } + }); + snackbar.getView().findViewById(com.google.android.material.R.id.snackbar_action) + .setAllCaps(false); + UiUtilities.setupSnackbar(snackbar, nightMode); + snackbar.show(); + measurementToolFragment.dismiss(mapActivity); + break; + case SHOW_IS_SAVED_FRAGMENT: + editingCtx.setChangesSaved(); + SavedTrackBottomSheetDialogFragment.showInstance(mapActivity.getSupportFragmentManager(), + outFile.getAbsolutePath()); + measurementToolFragment.dismiss(mapActivity); + break; + case SHOW_TOAST: + editingCtx.setChangesSaved(); + if (!savedGpxFile.showCurrentTrack) { + Toast.makeText(mapActivity, + MessageFormat.format(measurementToolFragment.getString(R.string.gpx_saved_sucessfully), outFile.getAbsolutePath()), + Toast.LENGTH_LONG).show(); + } + } + } + } else { + Toast.makeText(mapActivity, warning.getMessage(), Toast.LENGTH_LONG).show(); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/SelectFileBottomSheet.java b/OsmAnd/src/net/osmand/plus/measurementtool/SelectFileBottomSheet.java index 24d3ec8282..c155ec493c 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/SelectFileBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/SelectFileBottomSheet.java @@ -25,6 +25,7 @@ import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.helpers.GpxTrackAdapter; import net.osmand.plus.helpers.GpxTrackAdapter.OnItemClickListener; import net.osmand.plus.helpers.GpxUiHelper.GPXInfo; +import net.osmand.plus.helpers.enums.TracksSortByMode; import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter; import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionAdapterListener; @@ -38,7 +39,6 @@ import java.util.Map; import static net.osmand.plus.SimplePopUpMenuItemAdapter.*; import static net.osmand.plus.helpers.GpxUiHelper.getSortedGPXFilesInfo; -import static net.osmand.plus.settings.backend.OsmandSettings.*; import static net.osmand.util.Algorithms.collectDirs; public class SelectFileBottomSheet extends BottomSheetBehaviourDialogFragment { diff --git a/OsmAnd/src/net/osmand/plus/monitoring/LiveMonitoringHelper.java b/OsmAnd/src/net/osmand/plus/monitoring/LiveMonitoringHelper.java index 169507b35a..9ec8a3081c 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/LiveMonitoringHelper.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/LiveMonitoringHelper.java @@ -131,7 +131,14 @@ public class LiveMonitoringHelper { } public void sendData(LiveMonitoringData data) { - String urlStr = getLiveUrl(data); + String baseUrl = app.getSettings().LIVE_MONITORING_URL.get(); + String urlStr; + try { + urlStr = getLiveUrl(baseUrl, data); + } catch (IllegalArgumentException e) { + log.error("Could not construct live url from base url: " + baseUrl, e); + return; + } try { // Parse the URL and let the URI constructor handle proper encoding of special characters such as spaces URL url = new URL(urlStr); @@ -172,12 +179,11 @@ public class LiveMonitoringHelper { } } - private String getLiveUrl(LiveMonitoringData data) { - String st = app.getSettings().LIVE_MONITORING_URL.get(); + private String getLiveUrl(String baseUrl, LiveMonitoringData data) { List prm = new ArrayList(); int maxLen = 0; for (int i = 0; i < 7; i++) { - boolean b = st.contains("{"+i+"}"); + boolean b = baseUrl.contains("{"+i+"}"); if(b) { maxLen = i; } @@ -210,6 +216,6 @@ public class LiveMonitoringHelper { break; } } - return MessageFormat.format(st, prm.toArray()); + return MessageFormat.format(baseUrl, prm.toArray()); } } diff --git a/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java b/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java index eeaa98ff8c..901348a6d3 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java @@ -13,7 +13,8 @@ import androidx.preference.Preference; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.FontCache; @@ -288,11 +289,11 @@ public class MonitoringSettingsFragment extends BaseSettingsFragment @Override public void onApplyPreferenceChange(String prefId, boolean applyToAllProfiles, Object newValue) { if (SAVE_GLOBAL_TRACK_INTERVAL.equals(prefId)) { - OsmandSettings.OsmandPreference pref = settings.getPreference(prefId); + OsmandPreference pref = settings.getPreference(prefId); if (newValue instanceof Boolean) { applyPreference(settings.SAVE_GLOBAL_TRACK_REMEMBER.getId(), applyToAllProfiles, false); - } else if (pref instanceof OsmandSettings.CommonPreference - && !((OsmandSettings.CommonPreference) pref).hasDefaultValueForMode(getSelectedAppMode())) { + } else if (pref instanceof CommonPreference + && !((CommonPreference) pref).hasDefaultValueForMode(getSelectedAppMode())) { applyPreference(SAVE_GLOBAL_TRACK_INTERVAL, applyToAllProfiles, newValue); applyPreference(settings.SAVE_GLOBAL_TRACK_REMEMBER.getId(), applyToAllProfiles, true); } diff --git a/OsmAnd/src/net/osmand/plus/monitoring/SettingsMonitoringActivity.java b/OsmAnd/src/net/osmand/plus/monitoring/SettingsMonitoringActivity.java index d048bbedd5..7ace6377ad 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/SettingsMonitoringActivity.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/SettingsMonitoringActivity.java @@ -8,6 +8,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; @@ -23,11 +24,11 @@ import android.view.Window; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndTaskManager.OsmAndTaskRunnable; import net.osmand.plus.OsmandApplication; @@ -35,6 +36,8 @@ import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.activities.SettingsBaseActivity; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.util.Algorithms; import java.util.Map; @@ -172,8 +175,20 @@ public class SettingsMonitoringActivity extends SettingsBaseActivity { cat.setTitle(R.string.live_monitoring_m); grp.addPreference(cat); - cat.addPreference(createEditTextPreference(settings.LIVE_MONITORING_URL, R.string.live_monitoring_url, - R.string.live_monitoring_url_descr)); + EditTextPreference urlPreference = createEditTextPreference(settings.LIVE_MONITORING_URL, R.string.live_monitoring_url, + R.string.live_monitoring_url_descr); + urlPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (Algorithms.isValidMessageFormat((String) newValue)) { + return SettingsMonitoringActivity.super.onPreferenceChange(preference, newValue); + } else { + Toast.makeText(SettingsMonitoringActivity.this, R.string.wrong_format, Toast.LENGTH_SHORT).show(); + return false; + } + } + }); + cat.addPreference(urlPreference); final CheckBoxPreference liveMonitoring = createCheckBoxPreference(settings.LIVE_MONITORING, R.string.live_monitoring_m, R.string.live_monitoring_m_descr); cat.addPreference(liveMonitoring); diff --git a/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java index fae190ea01..31cb5375fe 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java @@ -84,7 +84,7 @@ import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; import net.osmand.plus.osmedit.OsmEditingPlugin; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.TracksSortByMode; +import net.osmand.plus.helpers.enums.TracksSortByMode; import java.io.File; import java.text.DateFormat; diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java index 3d9e065a5e..4451354fc8 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java @@ -50,14 +50,14 @@ import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.dialogs.GpxAppearanceAdapter; import net.osmand.plus.measurementtool.GpxData; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; import net.osmand.plus.track.GpxSplitType; import net.osmand.plus.track.SplitTrackAsyncTask; import net.osmand.plus.track.SplitTrackAsyncTask.SplitTrackListener; diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java index 427831de54..f12f18c852 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java @@ -30,6 +30,7 @@ import net.osmand.plus.ContextMenuAdapter.ItemClickListener; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.EnumAdapter; @@ -355,7 +356,7 @@ public class OsmEditingPlugin extends OsmandPlugin { @Override public boolean onContextMenuClick(ArrayAdapter adapter, int itemId, int pos, boolean isChecked, int[] viewCoordinates) { if (itemId == R.string.layer_osm_bugs) { - OsmandSettings.OsmandPreference showOsmBugs = settings.SHOW_OSM_BUGS; + OsmandPreference showOsmBugs = settings.SHOW_OSM_BUGS; showOsmBugs.set(isChecked); adapter.getItem(pos).setColorRes(showOsmBugs.get() ? R.color.osmand_orange : ContextMenuItem.INVALID_ID); @@ -378,7 +379,7 @@ public class OsmEditingPlugin extends OsmandPlugin { @Override public boolean onContextMenuClick(ArrayAdapter adapter, int itemId, int pos, boolean isChecked, int[] viewCoordinates) { if (itemId == R.string.layer_osm_edits) { - OsmandSettings.OsmandPreference showOsmEdits = settings.SHOW_OSM_EDITS; + OsmandPreference showOsmEdits = settings.SHOW_OSM_EDITS; showOsmEdits.set(isChecked); adapter.getItem(pos).setColorRes(showOsmEdits.get() ? R.color.osmand_orange : ContextMenuItem.INVALID_ID); adapter.notifyDataSetChanged(); diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmNotesMenu.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmNotesMenu.java index 80fa80a7f0..3c7c387a36 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmNotesMenu.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmNotesMenu.java @@ -14,8 +14,8 @@ import net.osmand.plus.DialogListItemAdapter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; diff --git a/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java b/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java index 038f9c9830..d131721376 100644 --- a/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java +++ b/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.format.DateFormat; -import android.text.format.Time; import android.view.View; import android.widget.ArrayAdapter; import android.widget.CheckBox; @@ -27,6 +26,8 @@ import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -34,20 +35,15 @@ import net.osmand.plus.dashboard.tools.DashFragmentData; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.quickaction.QuickActionType; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; import net.osmand.plus.views.AnimateDraggingMapThread; import net.osmand.plus.views.layers.MapInfoLayer; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; -import java.util.Date; import java.util.List; -import java.util.Locale; import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_MARK_AS_PARKING_LOC; diff --git a/OsmAnd/src/net/osmand/plus/quickaction/actions/DayNightModeAction.java b/OsmAnd/src/net/osmand/plus/quickaction/actions/DayNightModeAction.java index 0be5c0f715..8314341371 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/actions/DayNightModeAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/actions/DayNightModeAction.java @@ -6,8 +6,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.DayNightMode; +import net.osmand.plus.helpers.enums.DayNightMode; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.quickaction.QuickAction; @@ -27,9 +26,9 @@ public class DayNightModeAction extends QuickAction { @Override public void execute(MapActivity activity) { if (activity.getMyApplication().getDaynightHelper().isNightMode()) { - activity.getMyApplication().getSettings().DAYNIGHT_MODE.set(OsmandSettings.DayNightMode.DAY); + activity.getMyApplication().getSettings().DAYNIGHT_MODE.set(DayNightMode.DAY); } else { - activity.getMyApplication().getSettings().DAYNIGHT_MODE.set(OsmandSettings.DayNightMode.NIGHT); + activity.getMyApplication().getSettings().DAYNIGHT_MODE.set(DayNightMode.NIGHT); } } diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/LayerTransparencySeekbarMode.java b/OsmAnd/src/net/osmand/plus/rastermaps/LayerTransparencySeekbarMode.java new file mode 100644 index 0000000000..1fd8b2f91d --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/rastermaps/LayerTransparencySeekbarMode.java @@ -0,0 +1,22 @@ +package net.osmand.plus.rastermaps; + +import android.content.Context; + +import net.osmand.plus.R; + +public enum LayerTransparencySeekbarMode { + OVERLAY(R.string.overlay_transparency), + UNDERLAY(R.string.map_transparency), + OFF(R.string.shared_string_off), + UNDEFINED(R.string.shared_string_none); + + private final int key; + + LayerTransparencySeekbarMode(int key) { + this.key = key; + } + + public String toHumanString(Context ctx) { + return ctx.getString(key); + } +} diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java index 575196f8a2..7972b264aa 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java @@ -16,7 +16,6 @@ import com.google.gson.reflect.TypeToken; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.LayerTransparencySeekbarMode; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -137,7 +136,7 @@ public class MapOverlayAction extends SwitchableAction> { if (settings.LAYER_TRANSPARENCY_SEEKBAR_MODE.get() == LayerTransparencySeekbarMode.UNDEFINED) { settings.LAYER_TRANSPARENCY_SEEKBAR_MODE.set(LayerTransparencySeekbarMode.OVERLAY); } - if (settings.LAYER_TRANSPARENCY_SEEKBAR_MODE.get() == OsmandSettings.LayerTransparencySeekbarMode.OVERLAY) { + if (settings.LAYER_TRANSPARENCY_SEEKBAR_MODE.get() == LayerTransparencySeekbarMode.OVERLAY) { activity.getMapLayers().getMapControlsLayer().showTransparencyBar(settings.MAP_OVERLAY_TRANSPARENCY, true); } } else { diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java index 12f6f909cf..ca79cb4895 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java @@ -15,8 +15,8 @@ import com.google.gson.reflect.TypeToken; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.LayerTransparencySeekbarMode; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -144,7 +144,7 @@ public class MapUnderlayAction extends SwitchableAction> { } - final OsmandSettings.CommonPreference hidePolygonsPref = + final CommonPreference hidePolygonsPref = activity.getMyApplication().getSettings().getCustomRenderBooleanProperty("noPolygons"); hidePolygonsPref.set(hasUnderlay); diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java b/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java index da7a7882d3..942a34cbf1 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java @@ -29,6 +29,8 @@ import net.osmand.plus.ContextMenuItem; import net.osmand.plus.DialogListItemAdapter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.DownloadTilesDialog; @@ -38,9 +40,6 @@ import net.osmand.plus.activities.MapActivityLayers; import net.osmand.plus.dashboard.DashboardOnMap.DashboardType; import net.osmand.plus.dialogs.RasterMapMenu; import net.osmand.plus.quickaction.QuickActionType; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings.LayerTransparencySeekbarMode; import net.osmand.plus.views.MapTileLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.util.Algorithms; @@ -559,10 +558,10 @@ public class OsmandRasterMapsPlugin extends OsmandPlugin { } MapActivityLayers mapLayers = mapActivity.getMapLayers(); ITileSource map = layer.getMap(); - final OsmandSettings.LayerTransparencySeekbarMode currentMapTypeSeekbarMode = type == + final LayerTransparencySeekbarMode currentMapTypeSeekbarMode = type == OsmandRasterMapsPlugin.RasterMapType.OVERLAY - ? OsmandSettings.LayerTransparencySeekbarMode.OVERLAY - : OsmandSettings.LayerTransparencySeekbarMode.UNDERLAY; + ? LayerTransparencySeekbarMode.OVERLAY + : LayerTransparencySeekbarMode.UNDERLAY; if (map != null) { mapTypePreference.set(null); if (callback != null) { diff --git a/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java b/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java index 1f2750cff5..8bc8d1b51a 100644 --- a/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java +++ b/OsmAnd/src/net/osmand/plus/render/MapRenderRepositories.java @@ -43,7 +43,7 @@ import net.osmand.plus.settings.backend.OsmAndAppCustomization.OsmAndAppCustomiz import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.development.OsmandDevelopmentPlugin; import net.osmand.plus.render.OsmandRenderer.RenderingContext; diff --git a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java index 6da4f7ecab..702028d851 100644 --- a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java +++ b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java @@ -18,7 +18,7 @@ import net.osmand.data.MapObject; import net.osmand.data.QuadRect; import net.osmand.data.QuadTree; import net.osmand.data.Street; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.resources.ResourceManager.BinaryMapReaderResource; import net.osmand.plus.resources.ResourceManager.BinaryMapReaderResourceType; import net.osmand.util.MapUtils; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java index 9d7a8f3ab1..78773b5171 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java @@ -20,7 +20,7 @@ import androidx.fragment.app.Fragment; import net.osmand.AndroidUtils; import net.osmand.data.LatLon; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -292,7 +292,7 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr GeneralRouter.RoutingParameter parameter = routingOptionsHelper.getRoutingPrefsForAppModeById(app.getRoutingHelper().getAppMode(), parameterId); if (parameter != null) { boolean checked = entry.getValue(); - OsmandSettings.CommonPreference preference = app.getSettings().getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); + CommonPreference preference = app.getSettings().getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); preference.setModeValue(app.getRoutingHelper().getAppMode(), checked); } } @@ -318,7 +318,7 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr List avoidParameters = routingOptionsHelper.getAvoidRoutingPrefsForAppMode(app.getRoutingHelper().getAppMode()); for (GeneralRouter.RoutingParameter parameter : avoidParameters) { - OsmandSettings.CommonPreference preference = app.getSettings().getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); + CommonPreference preference = app.getSettings().getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); res.put(parameter.getId(), preference.getModeValue(app.getRoutingHelper().getAppMode())); } diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java index 69bed394cc..927a085c8c 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java @@ -54,6 +54,9 @@ import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.MapMarkersHelper.MapMarker; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; @@ -102,9 +105,6 @@ import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.routing.TransportRoutingHelper; import net.osmand.plus.search.QuickSearchHelper; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; import net.osmand.plus.widgets.TextViewExProgress; import net.osmand.router.GeneralRouter; import net.osmand.router.GeneralRouter.RoutingParameter; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java index c2681b2f7b..f9f8f5ce7b 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java @@ -27,6 +27,9 @@ import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.DialogListItemAdapter; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; @@ -192,7 +195,7 @@ public class RoutingOptionsHelper { public void applyVoiceProvider(MapActivity mapActivity, String provider, boolean applyAllModes) { OsmandApplication app = mapActivity.getMyApplication(); ApplicationMode selectedAppMode = app.getRoutingHelper().getAppMode(); - OsmandSettings.OsmandPreference VP = app.getSettings().VOICE_PROVIDER; + OsmandPreference VP = app.getSettings().VOICE_PROVIDER; if (applyAllModes) { for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { VP.setModeValue(mode, provider); @@ -668,7 +671,7 @@ public class RoutingOptionsHelper { } public boolean isSelected(OsmandSettings settings) { - final OsmandSettings.CommonPreference property = + final CommonPreference property = settings.getCustomRoutingBooleanProperty(routingParameter.getId(), routingParameter.getDefaultBoolean()); if (am != null) { return property.getModeValue(am); @@ -678,7 +681,7 @@ public class RoutingOptionsHelper { } public void setSelected(OsmandSettings settings, boolean isChecked) { - final OsmandSettings.CommonPreference property = + final CommonPreference property = settings.getCustomRoutingBooleanProperty(routingParameter.getId(), routingParameter.getDefaultBoolean()); if (am != null) { property.setModeValue(am, isChecked); diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java index eb7765e9af..a21b8898e6 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java +++ b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java @@ -22,19 +22,18 @@ 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.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.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; import net.osmand.router.GeneralRouter; import net.osmand.router.GeneralRouter.RoutingParameter; import net.osmand.router.GeneralRouter.RoutingParameterType; import net.osmand.router.PrecalculatedRouteDirection; -import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RouteExporter; import net.osmand.router.RouteImporter; import net.osmand.router.RoutePlannerFrontEnd; diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index 4fd5b1dd65..e92c8c0504 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -21,6 +21,7 @@ import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.notifications.OsmandNotification.NotificationType; import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo; import net.osmand.plus.routing.RouteProvider.GPXRouteParamsBuilder; @@ -408,8 +409,8 @@ public class RoutingHelper { } else if (mode.getRouteService() == RouteService.DIRECT_TO) { return -1.0f; } else if (mode.getRouteService() == RouteService.STRAIGHT) { - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.getModeValue(mode); - if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS || mc == OsmandSettings.MetricsConstants.MILES_AND_METERS) { + MetricsConstants mc = settings.METRIC_SYSTEM.getModeValue(mode); + if (mc == MetricsConstants.KILOMETERS_AND_METERS || mc == MetricsConstants.MILES_AND_METERS) { return 500.f; } else { // 1/4 mile diff --git a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java index b883a56bd1..4fa70f4c9f 100644 --- a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java @@ -16,6 +16,7 @@ import net.osmand.osm.edit.Node; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.render.NativeOsmandLibrary; @@ -478,7 +479,7 @@ public class TransportRoutingHelper { GeneralRouter.RoutingParameter pr = e.getValue(); String vl; if(pr.getType() == GeneralRouter.RoutingParameterType.BOOLEAN) { - OsmandSettings.CommonPreference pref = settings.getCustomRoutingBooleanProperty(key, pr.getDefaultBoolean()); + CommonPreference pref = settings.getCustomRoutingBooleanProperty(key, pr.getDefaultBoolean()); Boolean bool = pref.getModeValue(params.mode); vl = bool ? "true" : null; } else { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/BooleanAccessibilityPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/BooleanAccessibilityPreference.java new file mode 100644 index 0000000000..145a566b77 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/BooleanAccessibilityPreference.java @@ -0,0 +1,28 @@ +package net.osmand.plus.settings.backend; + +class BooleanAccessibilityPreference extends BooleanPreference { + + BooleanAccessibilityPreference(OsmandSettings osmandSettings, String id, boolean defaultValue) { + super(osmandSettings, id, defaultValue); + } + + @Override + public Boolean get() { + return getContext().accessibilityEnabled() ? super.get() : getDefaultValue(); + } + + @Override + public Boolean getModeValue(ApplicationMode mode) { + return getContext().accessibilityEnabledForMode(mode) ? super.getModeValue(mode) : getDefaultValue(); + } + + @Override + public boolean set(Boolean obj) { + return getContext().accessibilityEnabled() && super.set(obj); + } + + @Override + public boolean setModeValue(ApplicationMode mode, Boolean obj) { + return getContext().accessibilityEnabledForMode(mode) && super.setModeValue(mode, obj); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/BooleanPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/BooleanPreference.java new file mode 100644 index 0000000000..9ff4d3069d --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/BooleanPreference.java @@ -0,0 +1,23 @@ +package net.osmand.plus.settings.backend; + +public class BooleanPreference extends CommonPreference { + + BooleanPreference(OsmandSettings settings, String id, boolean defaultValue) { + super(settings, id, defaultValue); + } + + @Override + protected Boolean getValue(Object prefs, Boolean defaultValue) { + return getSettingsAPI().getBoolean(prefs, getId(), defaultValue); + } + + @Override + protected boolean setValue(Object prefs, Boolean val) { + return getSettingsAPI().edit(prefs).putBoolean(getId(), val).commit(); + } + + @Override + public Boolean parseString(String s) { + return Boolean.parseBoolean(s); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/BooleanStringPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/BooleanStringPreference.java new file mode 100644 index 0000000000..1928fa60a7 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/BooleanStringPreference.java @@ -0,0 +1,25 @@ +package net.osmand.plus.settings.backend; + +public class BooleanStringPreference extends BooleanPreference { + + public BooleanStringPreference(OsmandSettings osmandSettings, String id, boolean defaultValue) { + super(osmandSettings, id, defaultValue); + } + + @Override + protected Boolean getValue(Object prefs, Boolean defaultValue) { + Boolean value; + try { + value = parseString(getSettingsAPI().getString(prefs, getId(), defaultValue.toString())); + } catch (ClassCastException e) { + value = getSettingsAPI().getBoolean(prefs, getId(), defaultValue); + setValue(prefs, value); + } + return value; + } + + @Override + protected boolean setValue(Object prefs, Boolean val) { + return getSettingsAPI().edit(prefs).putString(getId(), val != null ? val.toString() : null).commit(); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/CommonPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/CommonPreference.java new file mode 100644 index 0000000000..62ce3a2450 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/CommonPreference.java @@ -0,0 +1,249 @@ +package net.osmand.plus.settings.backend; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.api.SettingsAPI; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.LinkedHashMap; +import java.util.Map; + +public abstract class CommonPreference extends PreferenceWithListener { + + private OsmandSettings settings; + private Object cachedPreference; + + private final String id; + + private T cachedValue; + private T defaultValue; + private Map defaultValues; + + private boolean cache; + private boolean global; + + + public CommonPreference(OsmandSettings settings, String id, T defaultValue) { + this.settings = settings; + this.id = id; + this.defaultValue = defaultValue; + settings.registerInternalPreference(id, this); + } + + // Methods to possibly override + protected abstract T getValue(Object prefs, T defaultValue); + + protected abstract boolean setValue(Object prefs, T val); + + public abstract T parseString(String s); + + protected String toString(T o) { + return o == null ? null : o.toString(); + } + + protected SettingsAPI getSettingsAPI() { + return settings.getSettingsAPI(); + } + + protected ApplicationMode getApplicationMode() { + return settings.getApplicationMode(); + } + + protected OsmandApplication getContext() { + return settings.getContext(); + } + + // common methods + + public final CommonPreference makeGlobal() { + global = true; + return this; + } + + public final CommonPreference cache() { + cache = true; + return this; + } + + public final CommonPreference makeProfile() { + global = false; + return this; + } + + protected final Object getPreferences() { + return settings.getPreferences(global); + + } + + public final void setModeDefaultValue(ApplicationMode mode, T defValue) { + if (defaultValues == null) { + defaultValues = new LinkedHashMap(); + } + defaultValues.put(mode, defValue); + } + + // TODO final + @Override + public boolean setModeValue(ApplicationMode mode, T obj) { + if (global) { + return set(obj); + } + + Object profilePrefs = settings.getProfilePreferences(mode); + boolean valueSaved = setValue(profilePrefs, obj); + if (valueSaved && cache && cachedPreference == profilePrefs) { + cachedValue = obj; + } + fireEvent(obj); + + return valueSaved; + } + + // TODO final + public T getProfileDefaultValue(ApplicationMode mode) { + if (global) { + return defaultValue; + } + if (defaultValues != null && defaultValues.containsKey(mode)) { + return defaultValues.get(mode); + } + ApplicationMode pt = mode.getParent(); + if (pt != null) { + return getProfileDefaultValue(pt); + } + return defaultValue; + } + + public final boolean hasDefaultValues() { + return defaultValues != null && !defaultValues.isEmpty(); + } + + public final boolean hasDefaultValueForMode(ApplicationMode mode) { + return defaultValues != null && defaultValues.containsKey(mode); + } + + // TODO final + protected T getDefaultValue() { + return getProfileDefaultValue(settings.APPLICATION_MODE.get()); + } + + @Override + public final void overrideDefaultValue(T newDefaultValue) { + this.defaultValue = newDefaultValue; + } + + // TODO final + @Override + public T getModeValue(ApplicationMode mode) { + if (global) { + return get(); + } + T defaultV = getProfileDefaultValue(mode); + return getValue(settings.getProfilePreferences(mode), defaultV); + } + + // TODO final + @Override + public T get() { + if (cache && cachedValue != null && cachedPreference == getPreferences()) { + return cachedValue; + } + cachedPreference = getPreferences(); + cachedValue = getValue(cachedPreference, getProfileDefaultValue(settings.APPLICATION_MODE.get())); + return cachedValue; + } + + @Override + public final String getId() { + return id; + } + + @Override + public final void resetToDefault() { + T o = getProfileDefaultValue(settings.APPLICATION_MODE.get()); + set(o); + } + + @Override + public final void resetModeToDefault(ApplicationMode mode) { + if (global) { + resetToDefault(); + } else { + T o = getProfileDefaultValue(mode); + setModeValue(mode, o); + } + } + + // TODO final + @Override + public boolean set(T obj) { + Object prefs = getPreferences(); + if (setValue(prefs, obj)) { + cachedValue = obj; + cachedPreference = prefs; + fireEvent(obj); + return true; + } + return false; + } + + public final boolean isSet() { + return settings.isSet(global, getId()); + } + + public boolean isSetForMode(ApplicationMode mode) { + return settings.isSet(mode, getId()); + } + + public final boolean isGlobal() { + return global; + } + + // TODO final + @Override + public boolean writeToJson(JSONObject json, ApplicationMode appMode) throws JSONException { + if (appMode != null) { + if (!global) { + String value = asStringModeValue(appMode); + if (value != null) { + json.put(getId(), value); + } + return true; + } + } else if (global) { + String value = asString(); + if (value != null) { + json.put(getId(), value); + } + return true; + } + return false; + } + + // TODO final + @Override + public void readFromJson(JSONObject json, ApplicationMode appMode) throws JSONException { + if (appMode != null) { + if (!global) { + String modeValue = json.getString(getId()); + setModeValue(appMode, parseString(modeValue)); + } + } else if (global) { + String globalValue = json.getString(getId()); + set(parseString(globalValue)); + } + } + + @Override + public final String asString() { + T o = get(); + return toString(o); + } + + @Override + public final String asStringModeValue(ApplicationMode m) { + T v = getModeValue(m); + return toString(v); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ContextMenuItemsPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/ContextMenuItemsPreference.java new file mode 100644 index 0000000000..12a83ea229 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ContextMenuItemsPreference.java @@ -0,0 +1,46 @@ +package net.osmand.plus.settings.backend; + +import androidx.annotation.NonNull; + +public class ContextMenuItemsPreference extends CommonPreference { + @NonNull + private String idScheme; + + ContextMenuItemsPreference(OsmandSettings settings, String id, @NonNull String idScheme, @NonNull ContextMenuItemsSettings defValue) { + super(settings, id, defValue); + this.idScheme = idScheme; + } + + @Override + protected ContextMenuItemsSettings getValue(Object prefs, ContextMenuItemsSettings defaultValue) { + String s = getSettingsAPI().getString(prefs, getId(), ""); + return readValue(s); + } + + @Override + protected boolean setValue(Object prefs, ContextMenuItemsSettings val) { + return getSettingsAPI().edit(prefs).putString(getId(), val.writeToJsonString(idScheme)).commit(); + } + + + @Override + protected String toString(ContextMenuItemsSettings o) { + return o.writeToJsonString(idScheme); + } + + @Override + public ContextMenuItemsSettings parseString(String s) { + return readValue(s); + } + + private ContextMenuItemsSettings readValue(String s) { + ContextMenuItemsSettings value = getDefaultValue().newInstance(); + value.readFromJsonString(s, idScheme); + return value; + } + + @NonNull + public String getIdScheme() { + return idScheme; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ContextMenuItemsSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/ContextMenuItemsSettings.java new file mode 100644 index 0000000000..2c51f2607d --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ContextMenuItemsSettings.java @@ -0,0 +1,102 @@ +package net.osmand.plus.settings.backend; + +import androidx.annotation.NonNull; + +import net.osmand.PlatformUtil; +import net.osmand.util.Algorithms; + +import org.apache.commons.logging.Log; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ContextMenuItemsSettings implements Serializable { + + private static final Log LOG = PlatformUtil.getLog(ContextMenuItemsSettings.class.getName()); + + private static final String HIDDEN = "hidden"; + private static final String ORDER = "order"; + + private List hiddenIds = new ArrayList<>(); + private List orderIds = new ArrayList<>(); + + public ContextMenuItemsSettings() { + + } + + public ContextMenuItemsSettings(@NonNull List hiddenIds, @NonNull List orderIds) { + this.hiddenIds = hiddenIds; + this.orderIds = orderIds; + } + + public ContextMenuItemsSettings newInstance() { + return new ContextMenuItemsSettings(); + } + + public void readFromJsonString(String jsonString, @NonNull String idScheme) { + if (Algorithms.isEmpty(jsonString)) { + return; + } + try { + JSONObject json = new JSONObject(jsonString); + readFromJson(json, idScheme); + } catch (JSONException e) { + LOG.error("Error converting to json string: " + e); + } + } + + public void readFromJson(JSONObject json, String idScheme) { + hiddenIds = readIdsList(json.optJSONArray(HIDDEN), idScheme); + orderIds = readIdsList(json.optJSONArray(ORDER), idScheme); + } + + protected List readIdsList(JSONArray jsonArray, @NonNull String idScheme) { + List list = new ArrayList<>(); + if (jsonArray != null) { + for (int i = 0; i < jsonArray.length(); i++) { + String id = jsonArray.optString(i); + list.add(idScheme + id); + } + } + return list; + } + + public String writeToJsonString(@NonNull String idScheme) { + try { + JSONObject json = new JSONObject(); + writeToJson(json, idScheme); + return json.toString(); + } catch (JSONException e) { + LOG.error("Error converting to json string: " + e); + } + return ""; + } + + public void writeToJson(JSONObject json, String idScheme) throws JSONException { + json.put(HIDDEN, getJsonArray(hiddenIds, idScheme)); + json.put(ORDER, getJsonArray(orderIds, idScheme)); + } + + protected JSONArray getJsonArray(List ids, @NonNull String idScheme) { + JSONArray jsonArray = new JSONArray(); + if (ids != null && !ids.isEmpty()) { + for (String id : ids) { + jsonArray.put(id.replace(idScheme, "")); + } + } + return jsonArray; + } + + public List getHiddenIds() { + return Collections.unmodifiableList(hiddenIds); + } + + public List getOrderIds() { + return Collections.unmodifiableList(orderIds); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/EnumStringPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/EnumStringPreference.java new file mode 100644 index 0000000000..d51deca152 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/EnumStringPreference.java @@ -0,0 +1,47 @@ +package net.osmand.plus.settings.backend; + +public class EnumStringPreference> extends CommonPreference { + + private final E[] values; + + EnumStringPreference(OsmandSettings settings, String id, E defaultValue, E[] values) { + super(settings, id, defaultValue); + this.values = values; + } + + @Override + protected E getValue(Object prefs, E defaultValue) { + try { + String name = getSettingsAPI().getString(prefs, getId(), defaultValue.name()); + E value = parseString(name); + return value != null ? value : defaultValue; + } catch (ClassCastException ex) { + setValue(prefs, defaultValue); + } + return defaultValue; + } + + @Override + public boolean setValue(Object prefs, E val) { + return getSettingsAPI().edit(prefs).putString(getId(), val.name()).commit(); + } + + @Override + protected String toString(E o) { + return o.name(); + } + + @Override + public E parseString(String s) { + for (E value : values) { + if (value.name().equals(s)) { + return value; + } + } + return null; + } + + public E[] getValues() { + return values; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java new file mode 100644 index 0000000000..bda48d389f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java @@ -0,0 +1,11 @@ +package net.osmand.plus.settings.backend; + +public enum ExportSettingsType { + PROFILE, + QUICK_ACTIONS, + POI_TYPES, + MAP_SOURCES, + CUSTOM_RENDER_STYLE, + CUSTOM_ROUTING, + AVOID_ROADS +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/FloatPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/FloatPreference.java new file mode 100644 index 0000000000..d9a98f7a01 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/FloatPreference.java @@ -0,0 +1,23 @@ +package net.osmand.plus.settings.backend; + +public class FloatPreference extends CommonPreference { + + FloatPreference(OsmandSettings settings, String id, float defaultValue) { + super(settings, id, defaultValue); + } + + @Override + protected Float getValue(Object prefs, Float defaultValue) { + return getSettingsAPI().getFloat(prefs, getId(), defaultValue); + } + + @Override + protected boolean setValue(Object prefs, Float val) { + return getSettingsAPI().edit(prefs).putFloat(getId(), val).commit(); + } + + @Override + public Float parseString(String s) { + return Float.parseFloat(s); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ImpassableRoadsStorage.java b/OsmAnd/src/net/osmand/plus/settings/backend/ImpassableRoadsStorage.java new file mode 100644 index 0000000000..80242478e5 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ImpassableRoadsStorage.java @@ -0,0 +1,193 @@ +package net.osmand.plus.settings.backend; + +import net.osmand.data.LatLon; +import net.osmand.data.PointDescription; +import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + +class ImpassableRoadsStorage extends SettingsMapPointsStorage { + + protected String roadsIdsKey; + protected String appModeKey; + + public ImpassableRoadsStorage(OsmandSettings osmandSettings) { + super(osmandSettings); + pointsKey = OsmandSettings.IMPASSABLE_ROAD_POINTS; + descriptionsKey = OsmandSettings.IMPASSABLE_ROADS_DESCRIPTIONS; + roadsIdsKey = OsmandSettings.IMPASSABLE_ROADS_IDS; + appModeKey = OsmandSettings.IMPASSABLE_ROADS_APP_MODE_KEYS; + } + + public List getRoadIds(int size) { + List list = new ArrayList<>(); + String roadIds = getSettingsAPI().getString(getOsmandSettings().getGlobalPreferences(), roadsIdsKey, ""); + if (roadIds.trim().length() > 0) { + StringTokenizer tok = new StringTokenizer(roadIds, ","); + while (tok.hasMoreTokens() && list.size() <= size) { + list.add(Long.parseLong(tok.nextToken())); + } + } + while (list.size() < size) { + list.add(0L); + } + return list; + } + + public List getAppModeKeys(int size) { + List list = new ArrayList<>(); + String roadIds = getSettingsAPI().getString(getOsmandSettings().getGlobalPreferences(), appModeKey, ""); + if (roadIds.trim().length() > 0) { + StringTokenizer tok = new StringTokenizer(roadIds, ","); + while (tok.hasMoreTokens() && list.size() <= size) { + list.add(tok.nextToken()); + } + } + while (list.size() < size) { + list.add(""); + } + return list; + } + + public List getImpassableRoadsInfo() { + List points = getPoints(); + List roadIds = getRoadIds(points.size()); + List appModeKeys = getAppModeKeys(points.size()); + List descriptions = getPointDescriptions(points.size()); + + List avoidRoadsInfo = new ArrayList<>(); + + for (int i = 0; i < points.size(); i++) { + LatLon latLon = points.get(i); + PointDescription description = PointDescription.deserializeFromString(descriptions.get(i), null); + + AvoidRoadInfo avoidRoadInfo = new AvoidRoadInfo(); + avoidRoadInfo.id = roadIds.get(i); + avoidRoadInfo.latitude = latLon.getLatitude(); + avoidRoadInfo.longitude = latLon.getLongitude(); + avoidRoadInfo.name = description.getName(); + avoidRoadInfo.appModeKey = appModeKeys.get(i); + avoidRoadsInfo.add(avoidRoadInfo); + } + + return avoidRoadsInfo; + } + + public boolean addImpassableRoadInfo(AvoidRoadInfo avoidRoadInfo) { + List points = getPoints(); + List roadIds = getRoadIds(points.size()); + List appModeKeys = getAppModeKeys(points.size()); + List descriptions = getPointDescriptions(points.size()); + + roadIds.add(0, avoidRoadInfo.id); + points.add(0, new LatLon(avoidRoadInfo.latitude, avoidRoadInfo.longitude)); + appModeKeys.add(0, avoidRoadInfo.appModeKey); + descriptions.add(0, PointDescription.serializeToString(new PointDescription("", avoidRoadInfo.name))); + + return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); + } + + public boolean updateImpassableRoadInfo(AvoidRoadInfo avoidRoadInfo) { + List points = getPoints(); + + int index = points.indexOf(new LatLon(avoidRoadInfo.latitude, avoidRoadInfo.longitude)); + if (index != -1) { + List roadIds = getRoadIds(points.size()); + List appModeKeys = getAppModeKeys(points.size()); + List descriptions = getPointDescriptions(points.size()); + + roadIds.set(index, avoidRoadInfo.id); + appModeKeys.set(index, avoidRoadInfo.appModeKey); + descriptions.set(index, PointDescription.serializeToString(new PointDescription("", avoidRoadInfo.name))); + return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); + } + return false; + } + + @Override + public boolean deletePoint(int index) { + List points = getPoints(); + List roadIds = getRoadIds(points.size()); + List appModeKeys = getAppModeKeys(points.size()); + List descriptions = getPointDescriptions(points.size()); + + if (index < points.size()) { + points.remove(index); + roadIds.remove(index); + appModeKeys.remove(index); + descriptions.remove(index); + return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); + } + return false; + } + + @Override + public boolean deletePoint(LatLon latLon) { + List points = getPoints(); + List roadIds = getRoadIds(points.size()); + List appModeKeys = getAppModeKeys(points.size()); + List descriptions = getPointDescriptions(points.size()); + + int index = points.indexOf(latLon); + if (index != -1) { + points.remove(index); + roadIds.remove(index); + appModeKeys.remove(index); + descriptions.remove(index); + return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); + } + return false; + } + + @Override + public boolean movePoint(LatLon latLonEx, LatLon latLonNew) { + List points = getPoints(); + List roadIds = getRoadIds(points.size()); + List appModeKeys = getAppModeKeys(points.size()); + List descriptions = getPointDescriptions(points.size()); + + int i = points.indexOf(latLonEx); + if (i != -1) { + points.set(i, latLonNew); + return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); + } else { + return false; + } + } + + public boolean saveAvoidRoadData(List points, List descriptions, + List roadIds, List appModeKeys) { + return savePoints(points, descriptions) && saveRoadIds(roadIds) && saveAppModeKeys(appModeKeys); + } + + public boolean saveRoadIds(List roadIds) { + StringBuilder stringBuilder = new StringBuilder(); + Iterator iterator = roadIds.iterator(); + while (iterator.hasNext()) { + stringBuilder.append(iterator.next()); + if (iterator.hasNext()) { + stringBuilder.append(","); + } + } + return getSettingsAPI().edit(getOsmandSettings().getGlobalPreferences()) + .putString(roadsIdsKey, stringBuilder.toString()) + .commit(); + } + + public boolean saveAppModeKeys(List appModeKeys) { + StringBuilder stringBuilder = new StringBuilder(); + Iterator iterator = appModeKeys.iterator(); + while (iterator.hasNext()) { + stringBuilder.append(iterator.next()); + if (iterator.hasNext()) { + stringBuilder.append(","); + } + } + return getSettingsAPI().edit(getOsmandSettings().getGlobalPreferences()) + .putString(appModeKey, stringBuilder.toString()) + .commit(); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/IntPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/IntPreference.java new file mode 100644 index 0000000000..327a97e4da --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/IntPreference.java @@ -0,0 +1,23 @@ +package net.osmand.plus.settings.backend; + +public class IntPreference extends CommonPreference { + + IntPreference(OsmandSettings settings, String id, int defaultValue) { + super(settings, id, defaultValue); + } + + @Override + protected Integer getValue(Object prefs, Integer defaultValue) { + return getSettingsAPI().getInt(prefs, getId(), defaultValue); + } + + @Override + protected boolean setValue(Object prefs, Integer val) { + return getSettingsAPI().edit(prefs).putInt(getId(), val).commit(); + } + + @Override + public Integer parseString(String s) { + return Integer.parseInt(s); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/IntermediatePointsStorage.java b/OsmAnd/src/net/osmand/plus/settings/backend/IntermediatePointsStorage.java new file mode 100644 index 0000000000..e9dda9b8ea --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/IntermediatePointsStorage.java @@ -0,0 +1,21 @@ +package net.osmand.plus.settings.backend; + +import net.osmand.data.LatLon; + +import java.util.List; + +class IntermediatePointsStorage extends SettingsMapPointsStorage { + + public IntermediatePointsStorage(OsmandSettings osmandSettings) { + super(osmandSettings); + pointsKey = OsmandSettings.INTERMEDIATE_POINTS; + descriptionsKey = OsmandSettings.INTERMEDIATE_POINTS_DESCRIPTION; + } + + @Override + public boolean savePoints(List ps, List ds) { + boolean res = super.savePoints(ps, ds); + getOsmandSettings().backupTargetPoints(); + return res; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ListStringPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/ListStringPreference.java new file mode 100644 index 0000000000..98f702746c --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ListStringPreference.java @@ -0,0 +1,123 @@ +package net.osmand.plus.settings.backend; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ListStringPreference extends StringPreference { + + private String delimiter; + + ListStringPreference(OsmandSettings osmandSettings, String id, String defaultValue, String delimiter) { + super(osmandSettings, id, defaultValue); + this.delimiter = delimiter; + } + + public boolean addValue(String res) { + return addModeValue(getApplicationMode(), res); + } + + public boolean addModeValue(ApplicationMode appMode, String res) { + String vl = getModeValue(appMode); + if (vl == null || vl.isEmpty()) { + vl = res + delimiter; + } else { + vl = vl + res + delimiter; + } + setModeValue(appMode, vl); + return true; + } + + public void clearAll() { + clearAllForProfile(getApplicationMode()); + } + + public void clearAllForProfile(ApplicationMode appMode) { + setModeValue(appMode, ""); + } + + public boolean containsValue(String res) { + return containsValue(getApplicationMode(), res); + } + + public boolean containsValue(ApplicationMode appMode, String res) { + String vl = getModeValue(appMode); + String r = res + delimiter; + return vl.startsWith(r) || vl.contains(delimiter + r); + } + + public boolean removeValue(String res) { + return removeValueForProfile(getApplicationMode(), res); + } + + public boolean removeValueForProfile(ApplicationMode appMode, String res) { + String vl = getModeValue(appMode); + String r = res + delimiter; + if (vl != null) { + if (vl.startsWith(r)) { + vl = vl.substring(r.length()); + } else { + int it = vl.indexOf(delimiter + r); + if (it >= 0) { + vl = vl.substring(0, it + delimiter.length()) + vl.substring(it + delimiter.length() + r.length()); + } + } + setModeValue(appMode, vl); + return true; + } + return false; + } + + public List getStringsList() { + return getStringsListForProfile(getApplicationMode()); + } + + public List getStringsListForProfile(ApplicationMode appMode) { + final String listAsString = getModeValue(appMode); + if (listAsString != null) { + if (listAsString.contains(delimiter)) { + return Arrays.asList(listAsString.split(delimiter)); + } else { + return new ArrayList() { + { + add(listAsString); + } + }; + } + } + return null; + } + + public void setStringsList(List values) { + setStringsListForProfile(getApplicationMode(), values); + } + + public void setStringsListForProfile(ApplicationMode appMode, List values) { + if (values == null || values.size() == 0) { + setModeValue(appMode, null); + return; + } + clearAllForProfile(appMode); + for (String value : values) { + addModeValue(appMode, value); + } + } + + public boolean setModeValues(ApplicationMode mode, List values) { + if (values == null || values.size() == 0) { + setModeValue(mode, null); + return false; + } + clearAll(); + String vl = get(); + for (String value : values) { + addValue(value); + if (vl == null || vl.isEmpty()) { + vl = value + delimiter; + } else { + vl = vl + value + delimiter; + } + } + return setModeValue(mode, vl); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/LongPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/LongPreference.java new file mode 100644 index 0000000000..521e0ad942 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/LongPreference.java @@ -0,0 +1,23 @@ +package net.osmand.plus.settings.backend; + +public class LongPreference extends CommonPreference { + + LongPreference(OsmandSettings settings, String id, long defaultValue) { + super(settings, id, defaultValue); + } + + @Override + protected Long getValue(Object prefs, Long defaultValue) { + return getSettingsAPI().getLong(prefs, getId(), defaultValue); + } + + @Override + protected boolean setValue(Object prefs, Long val) { + return getSettingsAPI().edit(prefs).putLong(getId(), val).commit(); + } + + @Override + public Long parseString(String s) { + return Long.parseLong(s); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/MainContextMenuItemsSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/MainContextMenuItemsSettings.java new file mode 100644 index 0000000000..ddabb46508 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/MainContextMenuItemsSettings.java @@ -0,0 +1,47 @@ +package net.osmand.plus.settings.backend; + +import androidx.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MainContextMenuItemsSettings extends ContextMenuItemsSettings { + + private static final String MAIN = "main"; + + private List mainIds = new ArrayList<>(); + + public MainContextMenuItemsSettings() { + + } + + public MainContextMenuItemsSettings(@NonNull List mainIds, @NonNull List hiddenIds, @NonNull List orderIds) { + super(hiddenIds, orderIds); + this.mainIds = mainIds; + } + + @Override + public ContextMenuItemsSettings newInstance() { + return new MainContextMenuItemsSettings(); + } + + @Override + public void readFromJson(JSONObject json, String idScheme) { + super.readFromJson(json, idScheme); + mainIds = readIdsList(json.optJSONArray(MAIN), idScheme); + } + + @Override + public void writeToJson(JSONObject json, String idScheme) throws JSONException { + super.writeToJson(json, idScheme); + json.put(MAIN, getJsonArray(mainIds, idScheme)); + } + + public List getMainIds() { + return Collections.unmodifiableList(mainIds); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmAndPreferencesDataStore.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmAndPreferencesDataStore.java new file mode 100644 index 0000000000..6e493ad34f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmAndPreferencesDataStore.java @@ -0,0 +1,126 @@ +package net.osmand.plus.settings.backend; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceDataStore; + +import java.util.Set; + +public class OsmAndPreferencesDataStore extends PreferenceDataStore { + + private OsmandSettings osmandSettings; + private ApplicationMode appMode; + + public OsmAndPreferencesDataStore(OsmandSettings settings, @NonNull ApplicationMode appMode) { + this.osmandSettings = settings; + this.appMode = appMode; + } + + @Override + public void putString(String key, @Nullable String value) { + osmandSettings.setPreference(key, value, appMode); + } + + @Override + public void putStringSet(String key, @Nullable Set values) { + osmandSettings.setPreference(key, values, appMode); + } + + @Override + public void putInt(String key, int value) { + osmandSettings.setPreference(key, value, appMode); + } + + @Override + public void putLong(String key, long value) { + osmandSettings.setPreference(key, value, appMode); + } + + @Override + public void putFloat(String key, float value) { + osmandSettings.setPreference(key, value, appMode); + } + + @Override + public void putBoolean(String key, boolean value) { + if (osmandSettings.DISABLE_COMPLEX_ROUTING.getId().equals(key)) { + osmandSettings.setPreference(key, !value, appMode); + } else { + osmandSettings.setPreference(key, value, appMode); + } + } + + public void putValue(String key, Object value) { + osmandSettings.setPreference(key, value, appMode); + } + + @Nullable + @Override + public String getString(String key, @Nullable String defValue) { + OsmandPreference preference = osmandSettings.getPreference(key); + if (preference instanceof StringPreference) { + return ((StringPreference) preference).getModeValue(appMode); + } else { + Object value = preference.getModeValue(appMode); + if (value != null) { + return value.toString(); + } + } + return defValue; + } + + @Nullable + @Override + public Set getStringSet(String key, @Nullable Set defValues) { + return super.getStringSet(key, defValues); + } + + @Override + public int getInt(String key, int defValue) { + OsmandPreference preference = osmandSettings.getPreference(key); + if (preference instanceof IntPreference) { + return ((IntPreference) preference).getModeValue(appMode); + } + return defValue; + } + + @Override + public long getLong(String key, long defValue) { + OsmandPreference preference = osmandSettings.getPreference(key); + if (preference instanceof LongPreference) { + return ((LongPreference) preference).getModeValue(appMode); + } + return defValue; + } + + @Override + public float getFloat(String key, float defValue) { + OsmandPreference preference = osmandSettings.getPreference(key); + if (preference instanceof FloatPreference) { + return ((FloatPreference) preference).getModeValue(appMode); + } + return defValue; + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + OsmandPreference preference = osmandSettings.getPreference(key); + if (preference instanceof BooleanPreference) { + BooleanPreference booleanPreference = (BooleanPreference) preference; + if (osmandSettings.DISABLE_COMPLEX_ROUTING.getId().equals(booleanPreference.getId())) { + return !booleanPreference.getModeValue(appMode); + } + return booleanPreference.getModeValue(appMode); + } + return defValue; + } + + @Nullable + public Object getValue(String key, Object defValue) { + OsmandPreference preference = osmandSettings.getPreference(key); + if (preference != null) { + return preference.getModeValue(appMode); + } + return defValue; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandPreference.java new file mode 100644 index 0000000000..275d81129b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandPreference.java @@ -0,0 +1,42 @@ +package net.osmand.plus.settings.backend; + +import net.osmand.StateChangedListener; + +import org.json.JSONException; +import org.json.JSONObject; + +public interface OsmandPreference { + T get(); + + boolean set(T obj); + + boolean setModeValue(ApplicationMode m, T obj); + + T getModeValue(ApplicationMode m); + + String getId(); + + void resetToDefault(); + + void resetModeToDefault(ApplicationMode m); + + void overrideDefaultValue(T newDefaultValue); + + void addListener(StateChangedListener listener); + + void removeListener(StateChangedListener listener); + + boolean isSet(); + + boolean isSetForMode(ApplicationMode m); + + boolean writeToJson(JSONObject json, ApplicationMode appMode) throws JSONException; + + void readFromJson(JSONObject json, ApplicationMode appMode) throws JSONException; + + String asString(); + + String asStringModeValue(ApplicationMode m); + + T parseString(String s); +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index efa7147e6c..c05a583701 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -6,29 +6,19 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.hardware.Sensor; -import android.hardware.SensorManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Build; import android.os.Environment; -import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.StringRes; import androidx.core.util.Pair; -import androidx.preference.PreferenceDataStore; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; +import net.osmand.FileUtils; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; -import net.osmand.StateChangedListener; import net.osmand.ValueHolder; -import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.map.ITileSource; @@ -36,7 +26,6 @@ import net.osmand.map.TileSourceManager; import net.osmand.map.TileSourceManager.TileSourceTemplate; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.io.NetworkUtils; -import net.osmand.plus.FavouritesDbHelper; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.SQLiteTileSource; @@ -45,32 +34,39 @@ import net.osmand.plus.access.RelativeDirectionStyle; import net.osmand.plus.api.SettingsAPI; import net.osmand.plus.api.SettingsAPI.SettingsEditor; import net.osmand.plus.api.SettingsAPIImpl; +import net.osmand.plus.audionotes.NotesSortByMode; import net.osmand.plus.dialogs.RateUsBottomSheetDialogFragment.RateUsState; +import net.osmand.plus.helpers.enums.AngularConstants; +import net.osmand.plus.helpers.enums.AutoZoomMap; import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; +import net.osmand.plus.helpers.enums.DayNightMode; +import net.osmand.plus.helpers.enums.DrivingRegion; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.helpers.SearchHistoryHelper; +import net.osmand.plus.helpers.enums.SpeedConstants; +import net.osmand.plus.helpers.enums.TracksSortByMode; import net.osmand.plus.mapillary.MapillaryPlugin; import net.osmand.plus.mapmarkers.CoordinateInputFormats.Format; +import net.osmand.plus.mapmarkers.MapMarkersMode; import net.osmand.plus.profiles.LocationIcon; import net.osmand.plus.profiles.NavigationIcon; import net.osmand.plus.profiles.ProfileIconColors; +import net.osmand.plus.rastermaps.LayerTransparencySeekbarMode; import net.osmand.plus.render.RendererRegistry; import net.osmand.plus.routing.RouteProvider.RouteService; -import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; -import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBuilder; +import net.osmand.plus.srtmplugin.TerrainMode; +import net.osmand.plus.views.layers.RulerControlLayer.RulerMode; import net.osmand.plus.voice.CommandPlayer; +import net.osmand.plus.wikipedia.WikiArticleShowImages; import net.osmand.render.RenderingRulesStorage; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; -import java.io.Serializable; -import java.lang.ref.WeakReference; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -80,9 +76,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; @@ -97,83 +91,6 @@ public class OsmandSettings { public static final int VERSION = 1; - public interface OsmandPreference { - T get(); - - boolean set(T obj); - - boolean setModeValue(ApplicationMode m, T obj); - - T getModeValue(ApplicationMode m); - - String getId(); - - void resetToDefault(); - - void resetModeToDefault(ApplicationMode m); - - void overrideDefaultValue(T newDefaultValue); - - void addListener(StateChangedListener listener); - - void removeListener(StateChangedListener listener); - - boolean isSet(); - - boolean isSetForMode(ApplicationMode m); - - boolean writeToJson(JSONObject json, ApplicationMode appMode) throws JSONException; - - void readFromJson(JSONObject json, ApplicationMode appMode) throws JSONException; - - String asString(); - - String asStringModeValue(ApplicationMode m); - - T parseString(String s); - } - - private abstract class PreferenceWithListener implements OsmandPreference { - private List>> l = null; - - @Override - public synchronized void addListener(StateChangedListener listener) { - if (l == null) { - l = new LinkedList>>(); - } - if (!l.contains(new WeakReference>(listener))) { - l.add(new WeakReference>(listener)); - } - } - - public synchronized void fireEvent(T value) { - if (l != null) { - Iterator>> it = l.iterator(); - while (it.hasNext()) { - StateChangedListener t = it.next().get(); - if (t == null) { - it.remove(); - } else { - t.stateChanged(value); - } - } - } - } - - @Override - public synchronized void removeListener(StateChangedListener listener) { - if (l != null) { - Iterator>> it = l.iterator(); - while (it.hasNext()) { - StateChangedListener t = it.next().get(); - if (t == listener) { - it.remove(); - } - } - } - } - } - // These settings are stored in SharedPreferences private static final String CUSTOM_SHARED_PREFERENCES_PREFIX = "net.osmand.customsettings."; private static final String SHARED_PREFERENCES_NAME = "net.osmand.settings"; @@ -188,13 +105,24 @@ public class OsmandSettings { private Object globalPreferences; private Object profilePreferences; private ApplicationMode currentMode; - private Map> registeredPreferences = - new LinkedHashMap>(); + private Map> registeredPreferences = new LinkedHashMap<>(); // cache variables private long lastTimeInternetConnectionChecked = 0; private boolean internetConnectionAvailable = true; + // TODO variable + private Map> customRoutingProps = new LinkedHashMap>(); + private Map> customRendersProps = new LinkedHashMap>(); + private Map> customBooleanRoutingProps = new LinkedHashMap>(); + private Map> customBooleanRendersProps = new LinkedHashMap>(); + + private ImpassableRoadsStorage impassableRoadsStorage = new ImpassableRoadsStorage(this); + private IntermediatePointsStorage intermediatePointsStorage = new IntermediatePointsStorage(this); + + private Object objectToShow; + private boolean editObjectToShow; + private String searchRequestToShow; protected OsmandSettings(OsmandApplication clientContext, SettingsAPI settinsAPI) { ctx = clientContext; @@ -248,8 +176,8 @@ public class OsmandSettings { return settingsAPI; } - public PreferencesDataStore getDataStore(@Nullable ApplicationMode appMode) { - return new PreferencesDataStore(appMode != null ? appMode : APPLICATION_MODE.get()); + public OsmAndPreferencesDataStore getDataStore(@Nullable ApplicationMode appMode) { + return new OsmAndPreferencesDataStore(this, appMode != null ? appMode : APPLICATION_MODE.get()); } public static String getSharedPreferencesName(ApplicationMode mode) { @@ -274,153 +202,33 @@ public class OsmandSettings { return globalPreferences != null && globalPreferences.getBoolean(SETTING_CUSTOMIZED_ID, false); } - public void migratePreferences() { - migrateEnumPreferences(); - SharedPreferences globalSharedPreferences = (SharedPreferences) globalPreferences; - Map globalPrefsMap = globalSharedPreferences.getAll(); - for (String key : globalPrefsMap.keySet()) { - OsmandPreference pref = getPreference(key); - if (pref instanceof CommonPreference) { - CommonPreference commonPreference = (CommonPreference) pref; - if (!commonPreference.global) { - for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { - if (!commonPreference.isSetForMode(mode) && !commonPreference.hasDefaultValueForMode(mode)) { - setPreference(key, globalPrefsMap.get(key), mode); - } - } - } - } - } - SharedPreferences defaultProfilePreferences = (SharedPreferences) getProfilePreferences(ApplicationMode.DEFAULT); - Map defaultPrefsMap = defaultProfilePreferences.getAll(); - for (String key : defaultPrefsMap.keySet()) { - OsmandPreference pref = getPreference(key); - if (pref instanceof CommonPreference) { - CommonPreference commonPreference = (CommonPreference) pref; - if (commonPreference.global && !commonPreference.isSet()) { - setPreference(key, defaultPrefsMap.get(key)); - } - } - } - for (OsmandPreference pref : generalPrefs) { - if (pref instanceof CommonPreference) { - CommonPreference commonPref = (CommonPreference) pref; - Object defaultVal = commonPref.getModeValue(ApplicationMode.DEFAULT); - for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { - if (!commonPref.isSetForMode(mode) && !commonPref.hasDefaultValueForMode(mode)) { - setPreference(commonPref.getId(), defaultVal, mode); - } - } - } - } - - String json = settingsAPI.getString(globalPreferences, "custom_app_profiles", ""); - if (!Algorithms.isEmpty(json)) { - Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); - Type t = new TypeToken>() { - }.getType(); - List customProfiles = gson.fromJson(json, t); - if (!Algorithms.isEmpty(customProfiles)) { - for (ApplicationModeBean modeBean : customProfiles) { - ApplicationModeBuilder builder = ApplicationMode.fromModeBean(ctx, modeBean); - ApplicationMode.saveProfile(builder, ctx); - } - } - } - } - - public void migrateQuickActionStates() { - String quickActionsJson = settingsAPI.getString(globalPreferences, "quick_action_new", ""); - if (!Algorithms.isEmpty(quickActionsJson)) { - Gson gson = new GsonBuilder().create(); - Type type = new TypeToken>() { - }.getType(); - HashMap quickActions = gson.fromJson(quickActionsJson, type); - if (!Algorithms.isEmpty(quickActions)) { - for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { - if (!QUICK_ACTION.isSetForMode(mode)) { - Boolean actionState = quickActions.get(mode.getStringKey()); - if (actionState == null) { - actionState = QUICK_ACTION.getDefaultValue(); - } - setPreference(QUICK_ACTION.getId(), actionState, mode); - } - } - } - } - } - - public void migrateEnumPreferences() { - for (OsmandPreference pref : registeredPreferences.values()) { - if (pref instanceof EnumStringPreference) { - EnumStringPreference enumPref = (EnumStringPreference) pref; - if (enumPref.isGlobal()) { - migrateEnumPref(enumPref, (SharedPreferences) globalPreferences); - } else { - for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { - migrateEnumPref(enumPref, (SharedPreferences) getProfilePreferences(mode)); - } - } - } - } - } - - private void migrateEnumPref(EnumStringPreference enumPref, SharedPreferences sharedPreferences) { - Object value = sharedPreferences.getAll().get(enumPref.getId()); - if (value instanceof Integer) { - int enumIndex = (int) value; - if (enumIndex >= 0 && enumIndex < enumPref.values.length) { - Enum savedValue = enumPref.values[enumIndex]; - enumPref.setValue(sharedPreferences, savedValue); - } - } - } - - public void migrateHomeWorkParkingToFavorites() { - FavouritesDbHelper favorites = ctx.getFavorites(); - - LatLon homePoint = null; - float lat = settingsAPI.getFloat(globalPreferences, "home_point_lat", 0); - float lon = settingsAPI.getFloat(globalPreferences, "home_point_lon", 0); - if (lat != 0 || lon != 0) { - homePoint = new LatLon(lat, lon); - } - LatLon workPoint = null; - lat = settingsAPI.getFloat(globalPreferences, "work_point_lat", 0); - lon = settingsAPI.getFloat(globalPreferences, "work_point_lon", 0); - if (lat != 0 || lon != 0) { - workPoint = new LatLon(lat, lon); - } - if (homePoint != null) { - favorites.setSpecialPoint(homePoint, FavouritePoint.SpecialPointType.HOME, null); - } - if (workPoint != null) { - favorites.setSpecialPoint(workPoint, FavouritePoint.SpecialPointType.WORK, null); - } - } - + // TODO doesn't look correct package visibility public Object getProfilePreferences(ApplicationMode mode) { return settingsAPI.getPreferenceObject(getSharedPreferencesName(mode)); } - public Object getProfilePreferences(String modeKey) { + // TODO doesn't look correct package visibility + Object getProfilePreferences(String modeKey) { return settingsAPI.getPreferenceObject(getSharedPreferencesNameForKey(modeKey)); } - public OsmandPreference getPreference(String key) { + public OsmandPreference getPreference(String key) { return registeredPreferences.get(key); } + // TODO doesn't look correct public void setPreferenceForAllModes(String key, Object value) { for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { setPreference(key, value, mode); } } + // TODO doesn't look correct public boolean setPreference(String key, Object value) { return setPreference(key, value, APPLICATION_MODE.get()); } + // TODO doesn't look correct @SuppressWarnings("unchecked") public boolean setPreference(String key, Object value, ApplicationMode mode) { OsmandPreference preference = registeredPreferences.get(key); @@ -509,7 +317,7 @@ public class OsmandSettings { } else if (preference instanceof EnumStringPreference) { EnumStringPreference enumPref = (EnumStringPreference) preference; if (value instanceof String) { - Enum enumValue = enumPref.parseString((String) value); + Enum enumValue = enumPref.parseString((String) value); if (enumValue != null) { return enumPref.setModeValue(mode, enumValue); } @@ -518,8 +326,8 @@ public class OsmandSettings { return enumPref.setModeValue(mode, value); } else if (value instanceof Integer) { int newVal = (Integer) value; - if (enumPref.values.length > newVal) { - Enum enumValue = enumPref.values[newVal]; + if (enumPref.getValues().length > newVal) { + Enum enumValue = enumPref.getValues()[newVal]; return enumPref.setModeValue(mode, enumValue); } return false; @@ -566,7 +374,7 @@ public class OsmandSettings { } private boolean prefCanBeCopiedOrReset(OsmandPreference pref) { - return pref instanceof CommonPreference && !((CommonPreference) pref).global + return pref instanceof CommonPreference && !((CommonPreference) pref).isGlobal() && !APP_MODE_ORDER.getId().equals(pref.getId()); } @@ -741,720 +549,99 @@ public class OsmandSettings { } } - - /////////////// PREFERENCES classes //////////////// - - public abstract class CommonPreference extends PreferenceWithListener { - private final String id; - private boolean global; - private T cachedValue; - private Object cachedPreference; - private boolean cache; - private Map defaultValues; - private T defaultValue; - - - public CommonPreference(String id, T defaultValue) { - this.id = id; - this.defaultValue = defaultValue; - registeredPreferences.put(id, this); - } - - // Methods to possibly override - protected abstract T getValue(Object prefs, T defaultValue); - - protected abstract boolean setValue(Object prefs, T val); - - public abstract T parseString(String s); - - protected String toString(T o) { - return o == null ? null : o.toString(); - } - - - // common methods - - public final CommonPreference makeGlobal() { - global = true; - return this; - } - - public final CommonPreference cache() { - cache = true; - return this; - } - - public final CommonPreference makeProfile() { - global = false; - return this; - } - - protected final Object getPreferences() { - return global ? globalPreferences : profilePreferences; - } - - public final void setModeDefaultValue(ApplicationMode mode, T defValue) { - if (defaultValues == null) { - defaultValues = new LinkedHashMap(); - } - defaultValues.put(mode, defValue); - } - - // TODO final - @Override - public boolean setModeValue(ApplicationMode mode, T obj) { - if (global) { - return set(obj); - } - - Object profilePrefs = getProfilePreferences(mode); - boolean valueSaved = setValue(profilePrefs, obj); - if (valueSaved && cache && cachedPreference == profilePrefs) { - cachedValue = obj; - } - fireEvent(obj); - - return valueSaved; - } - - // TODO final - public T getProfileDefaultValue(ApplicationMode mode) { - if (global) { - return defaultValue; - } - if (defaultValues != null && defaultValues.containsKey(mode)) { - return defaultValues.get(mode); - } - ApplicationMode pt = mode.getParent(); - if (pt != null) { - return getProfileDefaultValue(pt); - } - return defaultValue; - } - - public final boolean hasDefaultValues() { - return defaultValues != null && !defaultValues.isEmpty(); - } - - public final boolean hasDefaultValueForMode(ApplicationMode mode) { - return defaultValues != null && defaultValues.containsKey(mode); - } - - // TODO final - protected T getDefaultValue() { - return getProfileDefaultValue(currentMode); - } - - @Override - public final void overrideDefaultValue(T newDefaultValue) { - this.defaultValue = newDefaultValue; - } - - - - // TODO final - @Override - public T getModeValue(ApplicationMode mode) { - if (global) { - return get(); - } - T defaultV = getProfileDefaultValue(mode); - return getValue(getProfilePreferences(mode), defaultV); - } - - // TODO final - @Override - public T get() { - if (cache && cachedValue != null && cachedPreference == getPreferences()) { - return cachedValue; - } - cachedPreference = getPreferences(); - cachedValue = getValue(cachedPreference, getProfileDefaultValue(currentMode)); - return cachedValue; - } - - @Override - public final String getId() { - return id; - } - - @Override - public final void resetToDefault() { - T o = getProfileDefaultValue(currentMode); - set(o); - } - - @Override - public final void resetModeToDefault(ApplicationMode mode) { - if (global) { - resetToDefault(); - } else { - T o = getProfileDefaultValue(mode); - setModeValue(mode, o); - } - } - - // TODO final - @Override - public boolean set(T obj) { - Object prefs = getPreferences(); - if (setValue(prefs, obj)) { - cachedValue = obj; - cachedPreference = prefs; - fireEvent(obj); - return true; - } - return false; - } - - public final boolean isSet() { - return settingsAPI.contains(getPreferences(), getId()); - } - - public boolean isSetForMode(ApplicationMode mode) { - return settingsAPI.contains(getProfilePreferences(mode), getId()); - } - - public final boolean isGlobal() { - return global; - } - - // TODO final - @Override - public boolean writeToJson(JSONObject json, ApplicationMode appMode) throws JSONException { - if (appMode != null) { - if (!global) { - String value = asStringModeValue(appMode); - if (value != null) { - json.put(getId(), value); - } - return true; - } - } else if (global) { - String value = asString(); - if (value != null) { - json.put(getId(), value); - } - return true; - } - return false; - } - // TODO final - @Override - public void readFromJson(JSONObject json, ApplicationMode appMode) throws JSONException { - if (appMode != null) { - if (!global) { - String modeValue = json.getString(getId()); - setModeValue(appMode, parseString(modeValue)); - } - } else if (global) { - String globalValue = json.getString(getId()); - set(parseString(globalValue)); - } - } - - @Override - public final String asString() { - T o = get(); - return toString(o); - } - - @Override - public final String asStringModeValue(ApplicationMode m) { - T v = getModeValue(m); - return toString(v); - } + void registerInternalPreference(String id, CommonPreference tCommonPreference) { + registeredPreferences.put(id, tCommonPreference); } - public class BooleanPreference extends CommonPreference { - - private BooleanPreference(String id, boolean defaultValue) { - super(id, defaultValue); - } - - @Override - protected Boolean getValue(Object prefs, Boolean defaultValue) { - return settingsAPI.getBoolean(prefs, getId(), defaultValue); - } - - @Override - protected boolean setValue(Object prefs, Boolean val) { - return settingsAPI.edit(prefs).putBoolean(getId(), val).commit(); - } - - @Override - public Boolean parseString(String s) { - return Boolean.parseBoolean(s); - } + boolean isSet(boolean global, String id) { + return settingsAPI.contains(getPreferences(global), id); } - public class BooleanStringPreference extends BooleanPreference { - - public BooleanStringPreference(String id, boolean defaultValue) { - super(id, defaultValue); - } - - @Override - protected Boolean getValue(Object prefs, Boolean defaultValue) { - Boolean value; - try { - value = parseString(settingsAPI.getString(prefs, getId(), defaultValue.toString())); - } catch (ClassCastException e) { - value = settingsAPI.getBoolean(prefs, getId(), defaultValue); - setValue(prefs, value); - } - return value; - } - - @Override - protected boolean setValue(Object prefs, Boolean val) { - return settingsAPI.edit(prefs).putString(getId(), val != null ? val.toString() : null).commit(); - } + boolean isSet(ApplicationMode m, String id) { + return settingsAPI.contains(getProfilePreferences(m), id); } - private class BooleanAccessibilityPreference extends BooleanPreference { - - private BooleanAccessibilityPreference(String id, boolean defaultValue) { - super(id, defaultValue); - } - - @Override - public Boolean get() { - return ctx.accessibilityEnabled() ? super.get() : getDefaultValue(); - } - - @Override - public Boolean getModeValue(ApplicationMode mode) { - return ctx.accessibilityEnabledForMode(mode) ? super.getModeValue(mode) : getDefaultValue(); - } - - @Override - public boolean set(Boolean obj) { - return ctx.accessibilityEnabled() && super.set(obj); - } - - @Override - public boolean setModeValue(ApplicationMode mode, Boolean obj) { - return ctx.accessibilityEnabledForMode(mode) && super.setModeValue(mode, obj); - } + Object getPreferences(boolean global) { + return global ? globalPreferences : profilePreferences; } - private class IntPreference extends CommonPreference { - - private IntPreference(String id, int defaultValue) { - super(id, defaultValue); - } - - @Override - protected Integer getValue(Object prefs, Integer defaultValue) { - return settingsAPI.getInt(prefs, getId(), defaultValue); - } - - @Override - protected boolean setValue(Object prefs, Integer val) { - return settingsAPI.edit(prefs).putInt(getId(), val).commit(); - } - - @Override - public Integer parseString(String s) { - return Integer.parseInt(s); + @SuppressWarnings("unchecked") + public CommonPreference registerBooleanPreference(String id, boolean defValue) { + if (registeredPreferences.containsKey(id)) { + return (CommonPreference) registeredPreferences.get(id); } + BooleanPreference p = new BooleanPreference(this, id, defValue); + registeredPreferences.put(id, p); + return p; } - private class LongPreference extends CommonPreference { - - - private LongPreference(String id, long defaultValue) { - super(id, defaultValue); - } - - @Override - protected Long getValue(Object prefs, Long defaultValue) { - return settingsAPI.getLong(prefs, getId(), defaultValue); - } - - @Override - protected boolean setValue(Object prefs, Long val) { - return settingsAPI.edit(prefs).putLong(getId(), val).commit(); - } - - @Override - public Long parseString(String s) { - return Long.parseLong(s); + @SuppressWarnings("unchecked") + public CommonPreference registerBooleanAccessibilityPreference(String id, boolean defValue) { + if (registeredPreferences.containsKey(id)) { + return (CommonPreference) registeredPreferences.get(id); } + BooleanPreference p = new BooleanAccessibilityPreference(this, id, defValue); + registeredPreferences.put(id, p); + return p; } - public class FloatPreference extends CommonPreference { - - - private FloatPreference(String id, float defaultValue) { - super(id, defaultValue); - } - - @Override - protected Float getValue(Object prefs, Float defaultValue) { - return settingsAPI.getFloat(prefs, getId(), defaultValue); - } - - @Override - protected boolean setValue(Object prefs, Float val) { - return settingsAPI.edit(prefs).putFloat(getId(), val).commit(); - } - - @Override - public Float parseString(String s) { - return Float.parseFloat(s); + @SuppressWarnings("unchecked") + public CommonPreference registerStringPreference(String id, String defValue) { + if (registeredPreferences.containsKey(id)) { + return (CommonPreference) registeredPreferences.get(id); } + StringPreference p = new StringPreference(this, id, defValue); + registeredPreferences.put(id, p); + return p; } - public class StringPreference extends CommonPreference { - - private StringPreference(String id, String defaultValue) { - super(id, defaultValue); - } - - @Override - protected String getValue(Object prefs, String defaultValue) { - return settingsAPI.getString(prefs, getId(), defaultValue); - } - - @Override - protected boolean setValue(Object prefs, String val) { - return settingsAPI.edit(prefs).putString(getId(), (val != null) ? val.trim() : val).commit(); - } - - @Override - public String parseString(String s) { - return s; - } - } - - public class ListStringPreference extends StringPreference { - - private String delimiter; - - private ListStringPreference(String id, String defaultValue, String delimiter) { - super(id, defaultValue); - this.delimiter = delimiter; - } - - public boolean addValue(String res) { - return addModeValue(getApplicationMode(), res); - } - - public boolean addModeValue(ApplicationMode appMode, String res) { - String vl = getModeValue(appMode); - if (vl == null || vl.isEmpty()) { - vl = res + delimiter; - } else { - vl = vl + res + delimiter; - } - setModeValue(appMode, vl); - return true; - } - - public void clearAll() { - clearAllForProfile(getApplicationMode()); - } - - public void clearAllForProfile(ApplicationMode appMode) { - setModeValue(appMode, ""); - } - - public boolean containsValue(String res) { - return containsValue(getApplicationMode(), res); - } - - public boolean containsValue(ApplicationMode appMode, String res) { - String vl = getModeValue(appMode); - String r = res + delimiter; - return vl.startsWith(r) || vl.contains(delimiter + r); - } - - public boolean removeValue(String res) { - return removeValueForProfile(getApplicationMode(), res); - } - - public boolean removeValueForProfile(ApplicationMode appMode, String res) { - String vl = getModeValue(appMode); - String r = res + delimiter; - if(vl != null) { - if(vl.startsWith(r)) { - vl = vl.substring(r.length()); - setModeValue(appMode, vl); - return true; - } else { - int it = vl.indexOf(delimiter + r); - if(it >= 0) { - vl = vl.substring(0, it + delimiter.length()) + vl.substring(it + delimiter.length() + r.length()); - } - setModeValue(appMode, vl); - return true; - } - } - return false; - } - - public List getStringsList() { - return getStringsListForProfile(getApplicationMode()); - } - - public List getStringsListForProfile(ApplicationMode appMode) { - final String listAsString = getModeValue(appMode); - if (listAsString != null) { - if (listAsString.contains(delimiter)) { - return Arrays.asList(listAsString.split(delimiter)); - } else { - return new ArrayList() { - {add(listAsString);} - }; - } - } - return null; - } - - public void setStringsList(List values) { - setStringsListForProfile(getApplicationMode(), values); - } - - public void setStringsListForProfile(ApplicationMode appMode, List values) { - if (values == null || values.size() == 0) { - setModeValue(appMode, null); - return; - } - clearAllForProfile(appMode); - for (String value : values) { - addModeValue(appMode, value); - } - } - - public boolean setModeValues(ApplicationMode mode, List values) { - if (values == null || values.size() == 0) { - setModeValue(mode,null); - return false; - } - clearAll(); - String vl = get(); - for (String value : values) { - addValue(value); - if (vl == null || vl.isEmpty()) { - vl = value + delimiter; - } else { - vl = vl + value + delimiter; - } - } - return setModeValue(mode, vl); + @SuppressWarnings("unchecked") + public CommonPreference registerIntPreference(String id, int defValue) { + if (registeredPreferences.containsKey(id)) { + return (CommonPreference) registeredPreferences.get(id); } + IntPreference p = new IntPreference(this, id, defValue); + registeredPreferences.put(id, p); + return p; } - public class ContextMenuItemsPreference extends CommonPreference { - @NonNull - private String idScheme; - - private ContextMenuItemsPreference(String id, @NonNull String idScheme, @NonNull ContextMenuItemsSettings defValue) { - super(id, defValue); - this.idScheme = idScheme; - } - - @Override - protected ContextMenuItemsSettings getValue(Object prefs, ContextMenuItemsSettings defaultValue) { - String s = settingsAPI.getString(prefs, getId(), ""); - return readValue(s); - } - - @Override - protected boolean setValue(Object prefs, ContextMenuItemsSettings val) { - return settingsAPI.edit(prefs).putString(getId(), val.writeToJsonString(idScheme)).commit(); - } - - - @Override - protected String toString(ContextMenuItemsSettings o) { - return o.writeToJsonString(idScheme); - } - - @Override - public ContextMenuItemsSettings parseString(String s) { - return readValue(s); - } - - private ContextMenuItemsSettings readValue(String s) { - ContextMenuItemsSettings value = getDefaultValue().newInstance(); - value.readFromJsonString(s, idScheme); - return value; - } - - @NonNull - public String getIdScheme() { - return idScheme; + @SuppressWarnings("unchecked") + public CommonPreference registerLongPreference(String id, long defValue) { + if (registeredPreferences.containsKey(id)) { + return (CommonPreference) registeredPreferences.get(id); } + LongPreference p = new LongPreference(this, id, defValue); + registeredPreferences.put(id, p); + return p; } - public static class ContextMenuItemsSettings implements Serializable { - private static final String HIDDEN = "hidden"; - private static final String ORDER = "order"; - private List hiddenIds = new ArrayList<>(); - private List orderIds = new ArrayList<>(); - - public ContextMenuItemsSettings() { - - } - - public ContextMenuItemsSettings(@NonNull List hiddenIds, @NonNull List orderIds) { - this.hiddenIds = hiddenIds; - this.orderIds = orderIds; - } - - public ContextMenuItemsSettings newInstance() { - return new ContextMenuItemsSettings(); - } - - public void readFromJsonString(String jsonString, @NonNull String idScheme) { - if (Algorithms.isEmpty(jsonString)) { - return; - } - try { - JSONObject json = new JSONObject(jsonString); - readFromJson(json, idScheme); - } catch (JSONException e) { - LOG.error("Error converting to json string: " + e); - } - } - - public void readFromJson(JSONObject json, String idScheme) { - hiddenIds = readIdsList(json.optJSONArray(HIDDEN), idScheme); - orderIds = readIdsList(json.optJSONArray(ORDER), idScheme); - } - - protected List readIdsList(JSONArray jsonArray, @NonNull String idScheme) { - List list = new ArrayList<>(); - if (jsonArray != null) { - for (int i = 0; i < jsonArray.length(); i++) { - String id = jsonArray.optString(i); - list.add(idScheme + id); - } - } - return list; - } - - public String writeToJsonString(@NonNull String idScheme) { - try { - JSONObject json = new JSONObject(); - writeToJson(json, idScheme); - return json.toString(); - } catch (JSONException e) { - LOG.error("Error converting to json string: " + e); - } - return ""; - } - - public void writeToJson(JSONObject json, String idScheme) throws JSONException { - json.put(HIDDEN, getJsonArray(hiddenIds, idScheme)); - json.put(ORDER, getJsonArray(orderIds, idScheme)); - } - - protected JSONArray getJsonArray(List ids, @NonNull String idScheme) { - JSONArray jsonArray = new JSONArray(); - if (ids != null && !ids.isEmpty()) { - for (String id : ids) { - jsonArray.put(id.replace(idScheme, "")); - } - } - return jsonArray; - } - - public List getHiddenIds() { - return Collections.unmodifiableList(hiddenIds); - } - - public List getOrderIds() { - return Collections.unmodifiableList(orderIds); + @SuppressWarnings("unchecked") + public CommonPreference registerFloatPreference(String id, float defValue) { + if (registeredPreferences.containsKey(id)) { + return (CommonPreference) registeredPreferences.get(id); } + FloatPreference p = new FloatPreference(this, id, defValue); + registeredPreferences.put(id, p); + return p; } - public static class MainContextMenuItemsSettings extends ContextMenuItemsSettings { - private static final String MAIN = "main"; - private List mainIds = new ArrayList<>(); - - public MainContextMenuItemsSettings() { - - } - - public MainContextMenuItemsSettings(@NonNull List mainIds, @NonNull List hiddenIds, @NonNull List orderIds) { - super(hiddenIds, orderIds); - this.mainIds = mainIds; - } - - @Override - public ContextMenuItemsSettings newInstance() { - return new MainContextMenuItemsSettings(); - } - - @Override - public void readFromJson(JSONObject json, String idScheme) { - super.readFromJson(json, idScheme); - mainIds = readIdsList(json.optJSONArray(MAIN), idScheme); - } - - @Override - public void writeToJson(JSONObject json, String idScheme) throws JSONException { - super.writeToJson(json, idScheme); - json.put(MAIN, getJsonArray(mainIds, idScheme)); - } - - public List getMainIds() { - return Collections.unmodifiableList(mainIds); + @SuppressWarnings("unchecked") + public CommonPreference registerEnumIntPreference(String id, Enum defaultValue, Enum[] values, Class clz) { + if (registeredPreferences.containsKey(id)) { + return (CommonPreference) registeredPreferences.get(id); } + EnumStringPreference p = new EnumStringPreference(this, id, defaultValue, values); + registeredPreferences.put(id, p); + return p; } - public class EnumStringPreference> extends CommonPreference { - - private final E[] values; - - private EnumStringPreference(String id, E defaultValue, E[] values) { - super(id, defaultValue); - this.values = values; - } - - @Override - protected E getValue(Object prefs, E defaultValue) { - try { - String name = settingsAPI.getString(prefs, getId(), defaultValue.name()); - E value = parseString(name); - return value != null ? value : defaultValue; - } catch (ClassCastException ex) { - setValue(prefs, defaultValue); - } - return defaultValue; - } - - @Override - protected boolean setValue(Object prefs, E val) { - return settingsAPI.edit(prefs).putString(getId(), val.name()).commit(); - } - - @Override - protected String toString(E o) { - return o.name(); - } - - @Override - public E parseString(String s) { - for (E value : values) { - if (value.name().equals(s)) { - return value; - } - } - return null; - } - } - ///////////// PREFERENCES classes //////////////// + ///////////////////// PREFERENCES //////////////// public static final String NUMBER_OF_FREE_DOWNLOADS_ID = "free_downloads_v3"; // this value string is synchronized with settings_pref.xml preference name - private final OsmandPreference PLUGINS = new StringPreference("enabled_plugins", MapillaryPlugin.ID).makeGlobal(); + private final OsmandPreference PLUGINS = new StringPreference(this, "enabled_plugins", MapillaryPlugin.ID).makeGlobal(); public Set getEnabledPlugins() { String plugs = PLUGINS.get(); @@ -1502,124 +689,53 @@ public class OsmandSettings { return false; } + public final CommonPreference RULER_MODE = new EnumStringPreference<>(this, "ruler_mode", RulerMode.FIRST, RulerMode.values()).makeGlobal(); - @SuppressWarnings("unchecked") - public CommonPreference registerBooleanPreference(String id, boolean defValue) { - if (registeredPreferences.containsKey(id)) { - return (CommonPreference) registeredPreferences.get(id); - } - BooleanPreference p = new BooleanPreference(id, defValue); - registeredPreferences.put(id, p); - return p; - } + public final OsmandPreference SHOW_COMPASS_CONTROL_RULER = new BooleanPreference(this, "show_compass_ruler", true).makeGlobal(); - @SuppressWarnings("unchecked") - public CommonPreference registerBooleanAccessibilityPreference(String id, boolean defValue) { - if (registeredPreferences.containsKey(id)) { - return (CommonPreference) registeredPreferences.get(id); - } - BooleanPreference p = new BooleanAccessibilityPreference(id, defValue); - registeredPreferences.put(id, p); - return p; - } + public final CommonPreference SHOW_LINES_TO_FIRST_MARKERS = new BooleanPreference(this, "show_lines_to_first_markers", false).makeProfile(); + public final CommonPreference SHOW_ARROWS_TO_FIRST_MARKERS = new BooleanPreference(this, "show_arrows_to_first_markers", false).makeProfile(); - @SuppressWarnings("unchecked") - public CommonPreference registerStringPreference(String id, String defValue) { - if (registeredPreferences.containsKey(id)) { - return (CommonPreference) registeredPreferences.get(id); - } - StringPreference p = new StringPreference(id, defValue); - registeredPreferences.put(id, p); - return p; - } + public final CommonPreference WIKI_ARTICLE_SHOW_IMAGES_ASKED = new BooleanPreference(this, "wikivoyage_show_images_asked", false).makeGlobal(); + public final CommonPreference WIKI_ARTICLE_SHOW_IMAGES = new EnumStringPreference<>(this, "wikivoyage_show_imgs", WikiArticleShowImages.OFF, WikiArticleShowImages.values()).makeGlobal(); + public final CommonPreference GLOBAL_WIKIPEDIA_POI_ENABLED = new BooleanPreference(this, "global_wikipedia_poi_enabled", false).makeProfile(); + public final ListStringPreference WIKIPEDIA_POI_ENABLED_LANGUAGES = (ListStringPreference) new ListStringPreference(this, "wikipedia_poi_enabled_languages", null, ",").makeProfile().cache(); - @SuppressWarnings("unchecked") - public CommonPreference registerIntPreference(String id, int defValue) { - if (registeredPreferences.containsKey(id)) { - return (CommonPreference) registeredPreferences.get(id); - } - IntPreference p = new IntPreference(id, defValue); - registeredPreferences.put(id, p); - return p; - } + public final CommonPreference SELECT_MARKER_ON_SINGLE_TAP = new BooleanPreference(this, "select_marker_on_single_tap", false).makeProfile(); + public final CommonPreference KEEP_PASSED_MARKERS_ON_MAP = new BooleanPreference(this, "keep_passed_markers_on_map", true).makeProfile(); - @SuppressWarnings("unchecked") - public CommonPreference registerLongPreference(String id, long defValue) { - if (registeredPreferences.containsKey(id)) { - return (CommonPreference) registeredPreferences.get(id); - } - LongPreference p = new LongPreference(id, defValue); - registeredPreferences.put(id, p); - return p; - } + public final CommonPreference COORDS_INPUT_USE_RIGHT_SIDE = new BooleanPreference(this, "coords_input_use_right_side", true).makeGlobal(); + public final OsmandPreference COORDS_INPUT_FORMAT = new EnumStringPreference<>(this, "coords_input_format", Format.DD_MM_MMM, Format.values()).makeGlobal(); + public final CommonPreference COORDS_INPUT_USE_OSMAND_KEYBOARD = new BooleanPreference(this, "coords_input_use_osmand_keyboard", Build.VERSION.SDK_INT >= 16).makeGlobal(); + public final CommonPreference COORDS_INPUT_TWO_DIGITS_LONGTITUDE = new BooleanPreference(this, "coords_input_two_digits_longitude", false).makeGlobal(); - @SuppressWarnings("unchecked") - public CommonPreference registerFloatPreference(String id, float defValue) { - if (registeredPreferences.containsKey(id)) { - return (CommonPreference) registeredPreferences.get(id); - } - FloatPreference p = new FloatPreference(id, defValue); - registeredPreferences.put(id, p); - return p; - } + public final CommonPreference USE_MAPILLARY_FILTER = new BooleanPreference(this, "use_mapillary_filters", false).makeGlobal(); + public final CommonPreference MAPILLARY_FILTER_USER_KEY = new StringPreference(this, "mapillary_filter_user_key", "").makeGlobal(); + public final CommonPreference MAPILLARY_FILTER_USERNAME = new StringPreference(this, "mapillary_filter_username", "").makeGlobal(); + public final CommonPreference MAPILLARY_FILTER_FROM_DATE = new LongPreference(this, "mapillary_filter_from_date", 0).makeGlobal(); + public final CommonPreference MAPILLARY_FILTER_TO_DATE = new LongPreference(this, "mapillary_filter_to_date", 0).makeGlobal(); + public final CommonPreference MAPILLARY_FILTER_PANO = new BooleanPreference(this, "mapillary_filter_pano", false).makeGlobal(); - @SuppressWarnings("unchecked") - public CommonPreference registerEnumIntPreference(String id, Enum defaultValue, Enum[] values, Class clz) { - if (registeredPreferences.containsKey(id)) { - return (CommonPreference) registeredPreferences.get(id); - } - EnumStringPreference p = new EnumStringPreference(id, defaultValue, values); - registeredPreferences.put(id, p); - return p; - } + public final CommonPreference USE_FAST_RECALCULATION = new BooleanPreference(this, "use_fast_recalculation", true).makeProfile().cache(); + public final CommonPreference FORCE_PRIVATE_ACCESS_ROUTING_ASKED = new BooleanPreference(this, "force_private_access_routing", false).makeProfile().cache(); - public final CommonPreference RULER_MODE = new EnumStringPreference<>("ruler_mode", RulerMode.FIRST, RulerMode.values()).makeGlobal(); + public final CommonPreference SHOW_CARD_TO_CHOOSE_DRAWER = new BooleanPreference(this, "show_card_to_choose_drawer", false).makeGlobal(); + public final CommonPreference SHOW_DASHBOARD_ON_START = new BooleanPreference(this, "should_show_dashboard_on_start", false).makeGlobal(); + public final CommonPreference SHOW_DASHBOARD_ON_MAP_SCREEN = new BooleanPreference(this, "show_dashboard_on_map_screen", false).makeGlobal(); + public final CommonPreference SHOW_OSMAND_WELCOME_SCREEN = new BooleanPreference(this, "show_osmand_welcome_screen", true).makeGlobal(); - public final OsmandPreference SHOW_COMPASS_CONTROL_RULER = new BooleanPreference("show_compass_ruler", true).makeGlobal(); + public final CommonPreference API_NAV_DRAWER_ITEMS_JSON = new StringPreference(this, "api_nav_drawer_items_json", "{}").makeGlobal(); + public final CommonPreference API_CONNECTED_APPS_JSON = new StringPreference(this, "api_connected_apps_json", "[]").makeGlobal(); + public final CommonPreference NAV_DRAWER_LOGO = new StringPreference(this, "drawer_logo", "").makeProfile(); + public final CommonPreference NAV_DRAWER_URL = new StringPreference(this, "drawer_url", "").makeProfile(); - public final CommonPreference SHOW_LINES_TO_FIRST_MARKERS = new BooleanPreference("show_lines_to_first_markers", false).makeProfile(); - public final CommonPreference SHOW_ARROWS_TO_FIRST_MARKERS = new BooleanPreference("show_arrows_to_first_markers", false).makeProfile(); + public final CommonPreference NUMBER_OF_STARTS_FIRST_XMAS_SHOWN = new IntPreference(this, "number_of_starts_first_xmas_shown", 0).makeGlobal(); - public final CommonPreference WIKI_ARTICLE_SHOW_IMAGES_ASKED = new BooleanPreference("wikivoyage_show_images_asked", false).makeGlobal(); - public final CommonPreference WIKI_ARTICLE_SHOW_IMAGES = new EnumStringPreference<>("wikivoyage_show_imgs", WikiArticleShowImages.OFF, WikiArticleShowImages.values()).makeGlobal(); - public final CommonPreference GLOBAL_WIKIPEDIA_POI_ENABLED = new BooleanPreference("global_wikipedia_poi_enabled", false).makeProfile(); - public final ListStringPreference WIKIPEDIA_POI_ENABLED_LANGUAGES = (ListStringPreference) new ListStringPreference("wikipedia_poi_enabled_languages", null, ",").makeProfile().cache(); + public final OsmandPreference AVAILABLE_APP_MODES = new StringPreference(this, "available_application_modes", "car,bicycle,pedestrian,public_transport,").makeGlobal().cache(); - public final CommonPreference SELECT_MARKER_ON_SINGLE_TAP = new BooleanPreference("select_marker_on_single_tap", false).makeProfile(); - public final CommonPreference KEEP_PASSED_MARKERS_ON_MAP = new BooleanPreference("keep_passed_markers_on_map", true).makeProfile(); + public final OsmandPreference LAST_FAV_CATEGORY_ENTERED = new StringPreference(this, "last_fav_category", "").makeGlobal(); - public final CommonPreference COORDS_INPUT_USE_RIGHT_SIDE = new BooleanPreference("coords_input_use_right_side", true).makeGlobal(); - public final OsmandPreference COORDS_INPUT_FORMAT = new EnumStringPreference<>("coords_input_format", Format.DD_MM_MMM, Format.values()).makeGlobal(); - public final CommonPreference COORDS_INPUT_USE_OSMAND_KEYBOARD = new BooleanPreference("coords_input_use_osmand_keyboard", Build.VERSION.SDK_INT >= 16).makeGlobal(); - public final CommonPreference COORDS_INPUT_TWO_DIGITS_LONGTITUDE = new BooleanPreference("coords_input_two_digits_longitude", false).makeGlobal(); - - public final CommonPreference USE_MAPILLARY_FILTER = new BooleanPreference("use_mapillary_filters", false).makeGlobal(); - public final CommonPreference MAPILLARY_FILTER_USER_KEY = new StringPreference("mapillary_filter_user_key", "").makeGlobal(); - public final CommonPreference MAPILLARY_FILTER_USERNAME = new StringPreference("mapillary_filter_username", "").makeGlobal(); - public final CommonPreference MAPILLARY_FILTER_FROM_DATE = new LongPreference("mapillary_filter_from_date", 0).makeGlobal(); - public final CommonPreference MAPILLARY_FILTER_TO_DATE = new LongPreference("mapillary_filter_to_date", 0).makeGlobal(); - public final CommonPreference MAPILLARY_FILTER_PANO = new BooleanPreference("mapillary_filter_pano", false).makeGlobal(); - - public final CommonPreference USE_FAST_RECALCULATION = new BooleanPreference("use_fast_recalculation", true).makeGlobal().cache(); - public final CommonPreference FORCE_PRIVATE_ACCESS_ROUTING_ASKED = new BooleanPreference("force_private_access_routing", false).makeProfile().cache(); - - public final CommonPreference SHOW_CARD_TO_CHOOSE_DRAWER = new BooleanPreference("show_card_to_choose_drawer", false).makeGlobal(); - public final CommonPreference SHOW_DASHBOARD_ON_START = new BooleanPreference("should_show_dashboard_on_start", false).makeGlobal(); - public final CommonPreference SHOW_DASHBOARD_ON_MAP_SCREEN = new BooleanPreference("show_dashboard_on_map_screen", false).makeGlobal(); - public final CommonPreference SHOW_OSMAND_WELCOME_SCREEN = new BooleanPreference("show_osmand_welcome_screen", true).makeGlobal(); - - public final CommonPreference API_NAV_DRAWER_ITEMS_JSON = new StringPreference("api_nav_drawer_items_json", "{}").makeGlobal(); - public final CommonPreference API_CONNECTED_APPS_JSON = new StringPreference("api_connected_apps_json", "[]").makeGlobal(); - public final CommonPreference NAV_DRAWER_LOGO = new StringPreference("drawer_logo", "").makeProfile(); - public final CommonPreference NAV_DRAWER_URL = new StringPreference("drawer_url", "").makeProfile(); - - public final CommonPreference NUMBER_OF_STARTS_FIRST_XMAS_SHOWN = new IntPreference("number_of_starts_first_xmas_shown", 0).makeGlobal(); - - public final OsmandPreference AVAILABLE_APP_MODES = new StringPreference("available_application_modes", "car,bicycle,pedestrian,public_transport,").makeGlobal().cache(); - - public final OsmandPreference LAST_FAV_CATEGORY_ENTERED = new StringPreference("last_fav_category", "").makeGlobal(); - - public final OsmandPreference DEFAULT_APPLICATION_MODE = new CommonPreference("default_application_mode_string", ApplicationMode.DEFAULT) { + public final OsmandPreference DEFAULT_APPLICATION_MODE = new CommonPreference(this, "default_application_mode_string", ApplicationMode.DEFAULT) { @Override protected ApplicationMode getValue(Object prefs, ApplicationMode defaultValue) { @@ -1665,7 +781,7 @@ public class OsmandSettings { } }.makeGlobal(); - public final OsmandPreference LAST_ROUTE_APPLICATION_MODE = new CommonPreference("last_route_application_mode_backup_string", ApplicationMode.DEFAULT) { + public final OsmandPreference LAST_ROUTE_APPLICATION_MODE = new CommonPreference(this, "last_route_application_mode_backup_string", ApplicationMode.DEFAULT) { @Override protected ApplicationMode getValue(Object prefs, ApplicationMode defaultValue) { @@ -1706,13 +822,12 @@ public class OsmandSettings { } }.makeGlobal(); - public final OsmandPreference FIRST_MAP_IS_DOWNLOADED = new BooleanPreference( - "first_map_is_downloaded", false); + public final OsmandPreference FIRST_MAP_IS_DOWNLOADED = new BooleanPreference(this, "first_map_is_downloaded", false); - public final CommonPreference DRIVING_REGION_AUTOMATIC = new BooleanPreference("driving_region_automatic", true).makeProfile().cache(); - public final OsmandPreference DRIVING_REGION = new EnumStringPreference( + public final CommonPreference DRIVING_REGION_AUTOMATIC = new BooleanPreference(this, "driving_region_automatic", true).makeProfile().cache(); + public final OsmandPreference DRIVING_REGION = new EnumStringPreference(this, "default_driving_region", DrivingRegion.EUROPE_ASIA, DrivingRegion.values()) { - protected boolean setValue(Object prefs, DrivingRegion val) { + public boolean setValue(Object prefs, DrivingRegion val) { if (val != null) { METRIC_SYSTEM.set(val.defMetrics); } @@ -1720,29 +835,14 @@ public class OsmandSettings { } protected DrivingRegion getDefaultValue() { - Locale df = Locale.getDefault(); - if (df == null) { - return DrivingRegion.EUROPE_ASIA; - } - if (df.getCountry().equalsIgnoreCase(Locale.US.getCountry())) { - return DrivingRegion.US; - } else if (df.getCountry().equalsIgnoreCase(Locale.CANADA.getCountry())) { - return DrivingRegion.CANADA; - } else if (df.getCountry().equalsIgnoreCase(Locale.JAPAN.getCountry())) { - return DrivingRegion.JAPAN; - } else if (df.getCountry().equalsIgnoreCase("au")) { - return DrivingRegion.AUSTRALIA; - } else if(df.getCountry().equalsIgnoreCase(Locale.UK.getCountry())) { - return DrivingRegion.UK_AND_OTHERS; - } - return DrivingRegion.EUROPE_ASIA; + return DrivingRegion.getDrivingRegionByLocale(); } }.makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name // cache of metrics constants as they are used very often - public final OsmandPreference METRIC_SYSTEM = new EnumStringPreference( + public final OsmandPreference METRIC_SYSTEM = new EnumStringPreference(this, "default_metric_system", MetricsConstants.KILOMETERS_AND_METERS, MetricsConstants.values()) { protected MetricsConstants getDefaultValue() { return DRIVING_REGION.get().defMetrics; @@ -1752,8 +852,8 @@ public class OsmandSettings { //public final OsmandPreference COORDINATES_FORMAT = new IntPreference("coordinates_format", PointDescription.FORMAT_DEGREES).makeGlobal(); - public final OsmandPreference ANGULAR_UNITS = new EnumStringPreference( - "angular_measurement", AngularConstants.DEGREES, AngularConstants.values()).makeProfile(); + public final OsmandPreference ANGULAR_UNITS = new EnumStringPreference(this, + "angular_measurement", AngularConstants.DEGREES, AngularConstants.values()).makeProfile(); public static final String LAST_START_LAT = "last_searched_lat"; //$NON-NLS-1$ public static final String LAST_START_LON = "last_searched_lon"; //$NON-NLS-1$ @@ -1779,7 +879,7 @@ public class OsmandSettings { putFloat(LAST_START_LON, (float) lon).commit(); } - public final OsmandPreference SPEED_SYSTEM = new EnumStringPreference( + public final OsmandPreference SPEED_SYSTEM = new EnumStringPreference(this, "default_speed_system", SpeedConstants.KILOMETERS_PER_HOUR, SpeedConstants.values()) { @Override @@ -1808,25 +908,25 @@ public class OsmandSettings { // this value string is synchronized with settings_pref.xml preference name // cache of metrics constants as they are used very often - public final OsmandPreference DIRECTION_STYLE = new EnumStringPreference( + public final OsmandPreference DIRECTION_STYLE = new EnumStringPreference(this, "direction_style", RelativeDirectionStyle.SIDEWISE, RelativeDirectionStyle.values()).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name // cache of metrics constants as they are used very often - public final OsmandPreference ACCESSIBILITY_MODE = new EnumStringPreference( + public final OsmandPreference ACCESSIBILITY_MODE = new EnumStringPreference(this, "accessibility_mode", AccessibilityMode.DEFAULT, AccessibilityMode.values()).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference SPEECH_RATE = - new FloatPreference("speech_rate", 1f).makeProfile(); + new FloatPreference(this, "speech_rate", 1f).makeProfile(); public final OsmandPreference ARRIVAL_DISTANCE_FACTOR = - new FloatPreference("arrival_distance_factor", 1f).makeProfile(); + new FloatPreference(this, "arrival_distance_factor", 1f).makeProfile(); public final OsmandPreference SPEED_LIMIT_EXCEED_KMH = - new FloatPreference("speed_limit_exceed", 5f).makeProfile(); + new FloatPreference(this, "speed_limit_exceed", 5f).makeProfile(); - public final CommonPreference DEFAULT_SPEED = new FloatPreference("default_speed", 10f).makeProfile().cache(); + public final CommonPreference DEFAULT_SPEED = new FloatPreference(this, "default_speed", 10f).makeProfile().cache(); { DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.DEFAULT, 1.5f); @@ -1838,13 +938,13 @@ public class OsmandSettings { DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.SKI, 1.38f); } - public final OsmandPreference MIN_SPEED = new FloatPreference( + public final OsmandPreference MIN_SPEED = new FloatPreference(this, "min_speed", 0f).makeProfile().cache(); - public final OsmandPreference MAX_SPEED = new FloatPreference( + public final OsmandPreference MAX_SPEED = new FloatPreference(this, "max_speed", 0f).makeProfile().cache(); - public final CommonPreference ICON_RES_NAME = new StringPreference("app_mode_icon_res_name", "ic_world_globe_dark").makeProfile().cache(); + public final CommonPreference ICON_RES_NAME = new StringPreference(this, "app_mode_icon_res_name", "ic_world_globe_dark").makeProfile().cache(); { ICON_RES_NAME.setModeDefaultValue(ApplicationMode.DEFAULT, "ic_world_globe_dark"); @@ -1857,13 +957,14 @@ public class OsmandSettings { ICON_RES_NAME.setModeDefaultValue(ApplicationMode.SKI, "ic_action_skiing"); } - public final CommonPreference ICON_COLOR = new EnumStringPreference<>("app_mode_icon_color", ProfileIconColors.DEFAULT, ProfileIconColors.values()).makeProfile().cache(); + public final CommonPreference ICON_COLOR = new EnumStringPreference<>(this, + "app_mode_icon_color", ProfileIconColors.DEFAULT, ProfileIconColors.values()).makeProfile().cache(); - public final CommonPreference USER_PROFILE_NAME = new StringPreference("user_profile_name", "").makeProfile().cache(); + public final CommonPreference USER_PROFILE_NAME = new StringPreference(this, "user_profile_name", "").makeProfile().cache(); - public final CommonPreference PARENT_APP_MODE = new StringPreference("parent_app_mode", null).makeProfile().cache(); + public final CommonPreference PARENT_APP_MODE = new StringPreference(this, "parent_app_mode", null).makeProfile().cache(); - public final CommonPreference ROUTING_PROFILE = new StringPreference("routing_profile", "").makeProfile().cache(); + public final CommonPreference ROUTING_PROFILE = new StringPreference(this, "routing_profile", "").makeProfile().cache(); { ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.CAR, "car"); @@ -1875,14 +976,14 @@ public class OsmandSettings { ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.SKI, "ski"); } - public final CommonPreference ROUTE_SERVICE = new EnumStringPreference<>("route_service", RouteService.OSMAND, RouteService.values()).makeProfile().cache(); + public final CommonPreference ROUTE_SERVICE = new EnumStringPreference<>(this, "route_service", RouteService.OSMAND, RouteService.values()).makeProfile().cache(); { ROUTE_SERVICE.setModeDefaultValue(ApplicationMode.DEFAULT, RouteService.STRAIGHT); ROUTE_SERVICE.setModeDefaultValue(ApplicationMode.AIRCRAFT, RouteService.STRAIGHT); } - public final CommonPreference NAVIGATION_ICON = new EnumStringPreference<>("navigation_icon", NavigationIcon.DEFAULT, NavigationIcon.values()).makeProfile().cache(); + public final CommonPreference NAVIGATION_ICON = new EnumStringPreference<>(this, "navigation_icon", NavigationIcon.DEFAULT, NavigationIcon.values()).makeProfile().cache(); { NAVIGATION_ICON.setModeDefaultValue(ApplicationMode.DEFAULT, NavigationIcon.DEFAULT); @@ -1893,7 +994,7 @@ public class OsmandSettings { NAVIGATION_ICON.setModeDefaultValue(ApplicationMode.SKI, NavigationIcon.DEFAULT); } - public final CommonPreference LOCATION_ICON = new EnumStringPreference<>("location_icon", LocationIcon.DEFAULT, LocationIcon.values()).makeProfile().cache(); + public final CommonPreference LOCATION_ICON = new EnumStringPreference<>(this, "location_icon", LocationIcon.DEFAULT, LocationIcon.values()).makeProfile().cache(); { LOCATION_ICON.setModeDefaultValue(ApplicationMode.DEFAULT, LocationIcon.DEFAULT); @@ -1904,131 +1005,132 @@ public class OsmandSettings { LOCATION_ICON.setModeDefaultValue(ApplicationMode.SKI, LocationIcon.BICYCLE); } - public final CommonPreference APP_MODE_ORDER = new IntPreference("app_mode_order", 0).makeProfile().cache(); + public final CommonPreference APP_MODE_ORDER = new IntPreference(this, "app_mode_order", 0).makeProfile().cache(); public final OsmandPreference SWITCH_MAP_DIRECTION_TO_COMPASS_KMH = - new FloatPreference("speed_for_map_to_direction_of_movement", 0f).makeProfile(); + new FloatPreference(this, "speed_for_map_to_direction_of_movement", 0f).makeProfile(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USE_TRACKBALL_FOR_MOVEMENTS = - new BooleanPreference("use_trackball_for_movements", true).makeProfile(); + new BooleanPreference(this, "use_trackball_for_movements", true).makeProfile(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference ACCESSIBILITY_SMART_AUTOANNOUNCE = - new BooleanAccessibilityPreference("accessibility_smart_autoannounce", true).makeProfile(); - + new BooleanAccessibilityPreference(this, "accessibility_smart_autoannounce", true).makeProfile(); + // this value string is synchronized with settings_pref.xml preference name // cache of metrics constants as they are used very often - public final OsmandPreference ACCESSIBILITY_AUTOANNOUNCE_PERIOD = new IntPreference("accessibility_autoannounce_period", 10000).makeProfile().cache(); + public final OsmandPreference ACCESSIBILITY_AUTOANNOUNCE_PERIOD = new IntPreference(this, "accessibility_autoannounce_period", 10000).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference DISABLE_OFFROUTE_RECALC = - new BooleanPreference("disable_offroute_recalc", false).makeProfile(); + new BooleanPreference(this, "disable_offroute_recalc", false).makeProfile(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference DISABLE_WRONG_DIRECTION_RECALC = - new BooleanPreference("disable_wrong_direction_recalc", false).makeProfile(); - + new BooleanPreference(this, "disable_wrong_direction_recalc", false).makeProfile(); + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference DIRECTION_AUDIO_FEEDBACK = - new BooleanAccessibilityPreference("direction_audio_feedback", false).makeProfile(); - + new BooleanAccessibilityPreference(this, "direction_audio_feedback", false).makeProfile(); + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference DIRECTION_HAPTIC_FEEDBACK = - new BooleanAccessibilityPreference("direction_haptic_feedback", false).makeProfile(); + new BooleanAccessibilityPreference(this, "direction_haptic_feedback", false).makeProfile(); // magnetic field doesn'torkmost of the time on some phones - public final OsmandPreference USE_MAGNETIC_FIELD_SENSOR_COMPASS = new BooleanPreference("use_magnetic_field_sensor_compass", false).makeProfile().cache(); - public final OsmandPreference USE_KALMAN_FILTER_FOR_COMPASS = new BooleanPreference("use_kalman_filter_compass", true).makeProfile().cache(); - public final OsmandPreference USE_VOLUME_BUTTONS_AS_ZOOM = new BooleanPreference("use_volume_buttons_as_zoom", false).makeProfile().cache(); + public final OsmandPreference USE_MAGNETIC_FIELD_SENSOR_COMPASS = new BooleanPreference(this, "use_magnetic_field_sensor_compass", false).makeProfile().cache(); + public final OsmandPreference USE_KALMAN_FILTER_FOR_COMPASS = new BooleanPreference(this, "use_kalman_filter_compass", true).makeProfile().cache(); + public final OsmandPreference USE_VOLUME_BUTTONS_AS_ZOOM = new BooleanPreference(this, "use_volume_buttons_as_zoom", false).makeProfile().cache(); - public final OsmandPreference DO_NOT_SHOW_STARTUP_MESSAGES = new BooleanPreference("do_not_show_startup_messages", false).makeGlobal().cache(); - public final OsmandPreference SHOW_DOWNLOAD_MAP_DIALOG = new BooleanPreference("show_download_map_dialog", true).makeGlobal().cache(); - public final OsmandPreference DO_NOT_USE_ANIMATIONS = new BooleanPreference("do_not_use_animations", false).makeProfile().cache(); + public final OsmandPreference DO_NOT_SHOW_STARTUP_MESSAGES = new BooleanPreference(this, "do_not_show_startup_messages", false).makeGlobal().cache(); + public final OsmandPreference SHOW_DOWNLOAD_MAP_DIALOG = new BooleanPreference(this, "show_download_map_dialog", true).makeGlobal().cache(); + public final OsmandPreference DO_NOT_USE_ANIMATIONS = new BooleanPreference(this, "do_not_use_animations", false).makeProfile().cache(); - public final OsmandPreference SEND_ANONYMOUS_MAP_DOWNLOADS_DATA = new BooleanPreference("send_anonymous_map_downloads_data", false).makeGlobal().cache(); - public final OsmandPreference SEND_ANONYMOUS_APP_USAGE_DATA = new BooleanPreference("send_anonymous_app_usage_data", false).makeGlobal().cache(); - public final OsmandPreference SEND_ANONYMOUS_DATA_REQUEST_PROCESSED = new BooleanPreference("send_anonymous_data_request_processed", false).makeGlobal().cache(); - public final OsmandPreference SEND_ANONYMOUS_DATA_REQUESTS_COUNT = new IntPreference("send_anonymous_data_requests_count", 0).makeGlobal().cache(); - public final OsmandPreference SEND_ANONYMOUS_DATA_LAST_REQUEST_NS = new IntPreference("send_anonymous_data_last_request_ns", -1).makeGlobal().cache(); + public final OsmandPreference SEND_ANONYMOUS_MAP_DOWNLOADS_DATA = new BooleanPreference(this, "send_anonymous_map_downloads_data", false).makeGlobal().cache(); + public final OsmandPreference SEND_ANONYMOUS_APP_USAGE_DATA = new BooleanPreference(this, "send_anonymous_app_usage_data", false).makeGlobal().cache(); + public final OsmandPreference SEND_ANONYMOUS_DATA_REQUEST_PROCESSED = new BooleanPreference(this, "send_anonymous_data_request_processed", false).makeGlobal().cache(); + public final OsmandPreference SEND_ANONYMOUS_DATA_REQUESTS_COUNT = new IntPreference(this, "send_anonymous_data_requests_count", 0).makeGlobal().cache(); + public final OsmandPreference SEND_ANONYMOUS_DATA_LAST_REQUEST_NS = new IntPreference(this, "send_anonymous_data_last_request_ns", -1).makeGlobal().cache(); - public final OsmandPreference MAP_EMPTY_STATE_ALLOWED = new BooleanPreference("map_empty_state_allowed", false).makeProfile().cache(); + public final OsmandPreference MAP_EMPTY_STATE_ALLOWED = new BooleanPreference(this, "map_empty_state_allowed", false).makeProfile().cache(); - public final CommonPreference TEXT_SCALE = new FloatPreference("text_scale", 1f).makeProfile().cache(); + public final CommonPreference TEXT_SCALE = new FloatPreference(this, "text_scale", 1f).makeProfile().cache(); { TEXT_SCALE.setModeDefaultValue(ApplicationMode.CAR, 1.25f); } - public final CommonPreference MAP_DENSITY = new FloatPreference("map_density_n", 1f).makeProfile().cache(); + public final CommonPreference MAP_DENSITY = new FloatPreference(this, "map_density_n", 1f).makeProfile().cache(); { MAP_DENSITY.setModeDefaultValue(ApplicationMode.CAR, 1.5f); } - public final OsmandPreference SHOW_POI_LABEL = new BooleanPreference("show_poi_label", false).makeProfile(); + public final OsmandPreference SHOW_POI_LABEL = new BooleanPreference(this, "show_poi_label", false).makeProfile(); - public final OsmandPreference SHOW_MAPILLARY = new BooleanPreference("show_mapillary", false).makeProfile(); - public final OsmandPreference MAPILLARY_FIRST_DIALOG_SHOWN = new BooleanPreference("mapillary_first_dialog_shown", false).makeGlobal(); - public final OsmandPreference ONLINE_PHOTOS_ROW_COLLAPSED = new BooleanPreference("mapillary_menu_collapsed", true).makeGlobal(); - public final OsmandPreference WEBGL_SUPPORTED = new BooleanPreference("webgl_supported", true).makeGlobal(); + public final OsmandPreference SHOW_MAPILLARY = new BooleanPreference(this, "show_mapillary", false).makeProfile(); + public final OsmandPreference MAPILLARY_FIRST_DIALOG_SHOWN = new BooleanPreference(this, "mapillary_first_dialog_shown", false).makeGlobal(); + public final OsmandPreference ONLINE_PHOTOS_ROW_COLLAPSED = new BooleanPreference(this, "mapillary_menu_collapsed", true).makeGlobal(); + public final OsmandPreference WEBGL_SUPPORTED = new BooleanPreference(this, "webgl_supported", true).makeGlobal(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference PREFERRED_LOCALE = new StringPreference("preferred_locale", "").makeGlobal(); + public final OsmandPreference PREFERRED_LOCALE = new StringPreference(this, "preferred_locale", "").makeGlobal(); - public final OsmandPreference MAP_PREFERRED_LOCALE = new StringPreference("map_preferred_locale", "").makeGlobal().cache(); - public final OsmandPreference MAP_TRANSLITERATE_NAMES = new BooleanPreference("map_transliterate_names", false).makeGlobal().cache(); + public final OsmandPreference MAP_PREFERRED_LOCALE = new StringPreference(this, "map_preferred_locale", "").makeGlobal().cache(); + public final OsmandPreference MAP_TRANSLITERATE_NAMES = new BooleanPreference(this, "map_transliterate_names", false).makeGlobal().cache(); public boolean usingEnglishNames() { return MAP_PREFERRED_LOCALE.get().equals("en"); } // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference USER_NAME = new StringPreference("user_name", "").makeGlobal(); + public final OsmandPreference USER_NAME = new StringPreference(this, "user_name", "").makeGlobal(); public static final String BILLING_USER_DONATION_WORLD_PARAMETER = ""; public static final String BILLING_USER_DONATION_NONE_PARAMETER = "none"; - public final OsmandPreference INAPPS_READ = new BooleanPreference("inapps_read", false).makeGlobal(); + public final OsmandPreference INAPPS_READ = new BooleanPreference(this, "inapps_read", false).makeGlobal(); - public final OsmandPreference BILLING_USER_ID = new StringPreference("billing_user_id", "").makeGlobal(); - public final OsmandPreference BILLING_USER_TOKEN = new StringPreference("billing_user_token", "").makeGlobal(); - public final OsmandPreference BILLING_USER_NAME = new StringPreference("billing_user_name", "").makeGlobal(); - public final OsmandPreference BILLING_USER_EMAIL = new StringPreference("billing_user_email", "").makeGlobal(); - public final OsmandPreference BILLING_USER_COUNTRY = new StringPreference("billing_user_country", "").makeGlobal(); - public final OsmandPreference BILLING_USER_COUNTRY_DOWNLOAD_NAME = new StringPreference("billing_user_country_download_name", BILLING_USER_DONATION_NONE_PARAMETER).makeGlobal(); - public final OsmandPreference BILLING_HIDE_USER_NAME = new BooleanPreference("billing_hide_user_name", false).makeGlobal(); - public final OsmandPreference BILLING_PURCHASE_TOKEN_SENT = new BooleanPreference("billing_purchase_token_sent", false).makeGlobal(); - public final OsmandPreference BILLING_PURCHASE_TOKENS_SENT = new StringPreference("billing_purchase_tokens_sent", "").makeGlobal(); - public final OsmandPreference LIVE_UPDATES_PURCHASED = new BooleanPreference("billing_live_updates_purchased", false).makeGlobal(); - public final OsmandPreference LIVE_UPDATES_PURCHASE_CANCELLED_TIME = new LongPreference("live_updates_purchase_cancelled_time", 0).makeGlobal(); - public final OsmandPreference LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN = new BooleanPreference("live_updates_purchase_cancelled_first_dlg_shown", false).makeGlobal(); - public final OsmandPreference LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN = new BooleanPreference("live_updates_purchase_cancelled_second_dlg_shown", false).makeGlobal(); - public final OsmandPreference FULL_VERSION_PURCHASED = new BooleanPreference("billing_full_version_purchased", false).makeGlobal(); - public final OsmandPreference DEPTH_CONTOURS_PURCHASED = new BooleanPreference("billing_sea_depth_purchased", false).makeGlobal(); - public final OsmandPreference EMAIL_SUBSCRIBED = new BooleanPreference("email_subscribed", false).makeGlobal(); + public final OsmandPreference BILLING_USER_ID = new StringPreference(this, "billing_user_id", "").makeGlobal(); + public final OsmandPreference BILLING_USER_TOKEN = new StringPreference(this, "billing_user_token", "").makeGlobal(); + public final OsmandPreference BILLING_USER_NAME = new StringPreference(this, "billing_user_name", "").makeGlobal(); + public final OsmandPreference BILLING_USER_EMAIL = new StringPreference(this, "billing_user_email", "").makeGlobal(); + public final OsmandPreference BILLING_USER_COUNTRY = new StringPreference(this, "billing_user_country", "").makeGlobal(); + public final OsmandPreference BILLING_USER_COUNTRY_DOWNLOAD_NAME = new StringPreference(this, "billing_user_country_download_name", BILLING_USER_DONATION_NONE_PARAMETER).makeGlobal(); + public final OsmandPreference BILLING_HIDE_USER_NAME = new BooleanPreference(this, "billing_hide_user_name", false).makeGlobal(); + public final OsmandPreference BILLING_PURCHASE_TOKEN_SENT = new BooleanPreference(this, "billing_purchase_token_sent", false).makeGlobal(); + public final OsmandPreference BILLING_PURCHASE_TOKENS_SENT = new StringPreference(this, "billing_purchase_tokens_sent", "").makeGlobal(); + public final OsmandPreference LIVE_UPDATES_PURCHASED = new BooleanPreference(this, "billing_live_updates_purchased", false).makeGlobal(); + public final OsmandPreference LIVE_UPDATES_PURCHASE_CANCELLED_TIME = new LongPreference(this, "live_updates_purchase_cancelled_time", 0).makeGlobal(); + public final OsmandPreference LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN = new BooleanPreference(this, "live_updates_purchase_cancelled_first_dlg_shown", false).makeGlobal(); + public final OsmandPreference LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN = new BooleanPreference(this, "live_updates_purchase_cancelled_second_dlg_shown", false).makeGlobal(); + public final OsmandPreference FULL_VERSION_PURCHASED = new BooleanPreference(this, "billing_full_version_purchased", false).makeGlobal(); + public final OsmandPreference DEPTH_CONTOURS_PURCHASED = new BooleanPreference(this, "billing_sea_depth_purchased", false).makeGlobal(); + public final OsmandPreference CONTOUR_LINES_PURCHASED = new BooleanPreference(this, "billing_srtm_purchased", false).makeGlobal(); + public final OsmandPreference EMAIL_SUBSCRIBED = new BooleanPreference(this, "email_subscribed", false).makeGlobal(); - public final OsmandPreference DISCOUNT_ID = new IntPreference("discount_id", 0).makeGlobal(); - public final OsmandPreference DISCOUNT_SHOW_NUMBER_OF_STARTS = new IntPreference("number_of_starts_on_discount_show", 0).makeGlobal(); - public final OsmandPreference DISCOUNT_TOTAL_SHOW = new IntPreference("discount_total_show", 0).makeGlobal(); - public final OsmandPreference DISCOUNT_SHOW_DATETIME_MS = new LongPreference("show_discount_datetime_ms", 0).makeGlobal(); + public final OsmandPreference DISCOUNT_ID = new IntPreference(this, "discount_id", 0).makeGlobal(); + public final OsmandPreference DISCOUNT_SHOW_NUMBER_OF_STARTS = new IntPreference(this, "number_of_starts_on_discount_show", 0).makeGlobal(); + public final OsmandPreference DISCOUNT_TOTAL_SHOW = new IntPreference(this, "discount_total_show", 0).makeGlobal(); + public final OsmandPreference DISCOUNT_SHOW_DATETIME_MS = new LongPreference(this, "show_discount_datetime_ms", 0).makeGlobal(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USER_OSM_BUG_NAME = - new StringPreference("user_osm_bug_name", "NoName/OsmAnd").makeGlobal(); + new StringPreference(this, "user_osm_bug_name", "NoName/OsmAnd").makeGlobal(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USER_PASSWORD = - new StringPreference("user_password", "").makeGlobal(); + new StringPreference(this, "user_password", "").makeGlobal(); // this value boolean is synchronized with settings_pref.xml preference offline POI/Bugs edition - public final OsmandPreference OFFLINE_EDITION = new BooleanPreference("offline_osm_editing", true).makeGlobal(); + public final OsmandPreference OFFLINE_EDITION = new BooleanPreference(this, "offline_osm_editing", true).makeGlobal(); // this value string is synchronized with settings_pref.xml preference name public final CommonPreference DAYNIGHT_MODE = - new EnumStringPreference("daynight_mode", DayNightMode.DAY, DayNightMode.values()); + new EnumStringPreference(this, "daynight_mode", DayNightMode.DAY, DayNightMode.values()); { DAYNIGHT_MODE.makeProfile().cache(); @@ -2038,7 +1140,7 @@ public class OsmandSettings { } // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference AUTO_ZOOM_MAP = new BooleanPreference("auto_zoom_map_on_off", false).makeProfile().cache(); + public final CommonPreference AUTO_ZOOM_MAP = new BooleanPreference(this, "auto_zoom_map_on_off", false).makeProfile().cache(); { AUTO_ZOOM_MAP.setModeDefaultValue(ApplicationMode.CAR, true); AUTO_ZOOM_MAP.setModeDefaultValue(ApplicationMode.BICYCLE, false); @@ -2046,7 +1148,7 @@ public class OsmandSettings { } public final CommonPreference AUTO_ZOOM_MAP_SCALE = - new EnumStringPreference("auto_zoom_map_scale", AutoZoomMap.FAR, + new EnumStringPreference(this, "auto_zoom_map_scale", AutoZoomMap.FAR, AutoZoomMap.values()).makeProfile().cache(); { AUTO_ZOOM_MAP_SCALE.setModeDefaultValue(ApplicationMode.CAR, AutoZoomMap.FAR); @@ -2054,7 +1156,7 @@ public class OsmandSettings { AUTO_ZOOM_MAP_SCALE.setModeDefaultValue(ApplicationMode.PEDESTRIAN, AutoZoomMap.CLOSE); } - public final CommonPreference DELAY_TO_START_NAVIGATION = new IntPreference("delay_to_start_navigation", -1) { + public final CommonPreference DELAY_TO_START_NAVIGATION = new IntPreference(this, "delay_to_start_navigation", -1) { protected Integer getDefaultValue() { if (DEFAULT_APPLICATION_MODE.get().isDerivedRoutingFrom(ApplicationMode.CAR)) { @@ -2064,16 +1166,16 @@ public class OsmandSettings { } }.makeGlobal().cache(); - public final CommonPreference SNAP_TO_ROAD = new BooleanPreference("snap_to_road", false).makeProfile().cache(); + public final CommonPreference SNAP_TO_ROAD = new BooleanPreference(this, "snap_to_road", false).makeProfile().cache(); { SNAP_TO_ROAD.setModeDefaultValue(ApplicationMode.CAR, true); SNAP_TO_ROAD.setModeDefaultValue(ApplicationMode.BICYCLE, true); } - public final CommonPreference INTERRUPT_MUSIC = new BooleanPreference("interrupt_music", false).makeProfile(); + public final CommonPreference INTERRUPT_MUSIC = new BooleanPreference(this, "interrupt_music", false).makeProfile(); - public final CommonPreference ENABLE_PROXY = new BooleanPreference("enable_proxy", false) { + public final CommonPreference ENABLE_PROXY = new BooleanPreference(this, "enable_proxy", false) { @Override protected boolean setValue(Object prefs, Boolean val) { boolean valueSaved = super.setValue(prefs, val); @@ -2084,19 +1186,19 @@ public class OsmandSettings { } }.makeGlobal(); - public final CommonPreference PROXY_HOST = new StringPreference("proxy_host", "127.0.0.1").makeGlobal(); - public final CommonPreference PROXY_PORT = new IntPreference("proxy_port", 8118).makeGlobal(); - public final CommonPreference USER_ANDROID_ID = new StringPreference("user_android_id", "").makeGlobal(); + public final CommonPreference PROXY_HOST = new StringPreference(this, "proxy_host", "127.0.0.1").makeGlobal(); + public final CommonPreference PROXY_PORT = new IntPreference(this, "proxy_port", 8118).makeGlobal(); + public final CommonPreference USER_ANDROID_ID = new StringPreference(this, "user_android_id", "").makeGlobal(); // this value string is synchronized with settings_pref.xml preference name public static final String SAVE_CURRENT_TRACK = "save_current_track"; //$NON-NLS-1$ - public final CommonPreference SAVE_GLOBAL_TRACK_TO_GPX = new BooleanPreference("save_global_track_to_gpx", false).makeGlobal().cache(); - public final CommonPreference SAVE_GLOBAL_TRACK_INTERVAL = new IntPreference("save_global_track_interval", 5000).makeProfile().cache(); - public final CommonPreference SAVE_GLOBAL_TRACK_REMEMBER = new BooleanPreference("save_global_track_remember", false).makeProfile().cache(); - public final CommonPreference SHOW_SAVED_TRACK_REMEMBER = new BooleanPreference("show_saved_track_remember", true).makeGlobal(); + public final CommonPreference SAVE_GLOBAL_TRACK_TO_GPX = new BooleanPreference(this, "save_global_track_to_gpx", false).makeGlobal().cache(); + public final CommonPreference SAVE_GLOBAL_TRACK_INTERVAL = new IntPreference(this, "save_global_track_interval", 5000).makeProfile().cache(); + public final CommonPreference SAVE_GLOBAL_TRACK_REMEMBER = new BooleanPreference(this, "save_global_track_remember", false).makeProfile().cache(); + public final CommonPreference SHOW_SAVED_TRACK_REMEMBER = new BooleanPreference(this, "show_saved_track_remember", true).makeGlobal(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference SAVE_TRACK_TO_GPX = new BooleanPreference("save_track_to_gpx", false).makeProfile().cache(); + public final CommonPreference SAVE_TRACK_TO_GPX = new BooleanPreference(this, "save_track_to_gpx", false).makeProfile().cache(); { SAVE_TRACK_TO_GPX.setModeDefaultValue(ApplicationMode.CAR, false); @@ -2108,67 +1210,70 @@ public class OsmandSettings { public static final Integer MONTHLY_DIRECTORY = 1; // public static final Integer DAILY_DIRECTORY = 2; - public final CommonPreference DISABLE_RECORDING_ONCE_APP_KILLED = new BooleanPreference("disable_recording_once_app_killed", false).makeProfile(); + public final CommonPreference DISABLE_RECORDING_ONCE_APP_KILLED = new BooleanPreference(this, "disable_recording_once_app_killed", false).makeProfile(); - public final CommonPreference SAVE_HEADING_TO_GPX = new BooleanPreference("save_heading_to_gpx", false).makeProfile(); + public final CommonPreference SAVE_HEADING_TO_GPX = new BooleanPreference(this, "save_heading_to_gpx", false).makeProfile(); - public final CommonPreference TRACK_STORAGE_DIRECTORY = new IntPreference("track_storage_directory", 0).makeProfile(); + public final CommonPreference TRACK_STORAGE_DIRECTORY = new IntPreference(this, "track_storage_directory", 0).makeProfile(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference FAST_ROUTE_MODE = new BooleanPreference("fast_route_mode", true).makeProfile(); + public final OsmandPreference FAST_ROUTE_MODE = new BooleanPreference(this, "fast_route_mode", true).makeProfile(); // dev version - public final CommonPreference DISABLE_COMPLEX_ROUTING = new BooleanPreference("disable_complex_routing", false).makeGlobal(); - public final CommonPreference ENABLE_TIME_CONDITIONAL_ROUTING = new BooleanPreference("enable_time_conditional_routing", true).makeProfile(); + public final CommonPreference DISABLE_COMPLEX_ROUTING = new BooleanPreference(this, "disable_complex_routing", false).makeProfile(); + public final CommonPreference ENABLE_TIME_CONDITIONAL_ROUTING = new BooleanPreference(this, "enable_time_conditional_routing", true).makeProfile(); public boolean simulateNavigation = false; - public final CommonPreference SHOW_ROUTING_ALARMS = new BooleanPreference("show_routing_alarms", true).makeProfile().cache(); + public final CommonPreference SHOW_ROUTING_ALARMS = new BooleanPreference(this, "show_routing_alarms", true).makeProfile().cache(); - public final CommonPreference SHOW_TRAFFIC_WARNINGS = new BooleanPreference("show_traffic_warnings", false).makeProfile().cache(); + public final CommonPreference SHOW_TRAFFIC_WARNINGS = new BooleanPreference(this, "show_traffic_warnings", false).makeProfile().cache(); { SHOW_TRAFFIC_WARNINGS.setModeDefaultValue(ApplicationMode.CAR, true); } - public final CommonPreference SHOW_PEDESTRIAN = new BooleanPreference("show_pedestrian", false).makeProfile().cache(); + public final CommonPreference SHOW_PEDESTRIAN = new BooleanPreference(this, "show_pedestrian", false).makeProfile().cache(); { SHOW_PEDESTRIAN.setModeDefaultValue(ApplicationMode.CAR, true); } - public final CommonPreference SHOW_TUNNELS = new BooleanPreference("show_tunnels", false).makeProfile().cache(); + public final CommonPreference SHOW_TUNNELS = new BooleanPreference(this, "show_tunnels", false).makeProfile().cache(); { SHOW_TUNNELS.setModeDefaultValue(ApplicationMode.CAR, true); } - public final OsmandPreference SHOW_CAMERAS = new BooleanPreference("show_cameras", false).makeProfile().cache(); - public final CommonPreference SHOW_LANES = new BooleanPreference("show_lanes", false).makeProfile().cache(); + public final OsmandPreference SHOW_CAMERAS = new BooleanPreference(this, "show_cameras", false).makeProfile().cache(); + public final CommonPreference SHOW_LANES = new BooleanPreference(this, "show_lanes", false).makeProfile().cache(); { SHOW_LANES.setModeDefaultValue(ApplicationMode.CAR, true); SHOW_LANES.setModeDefaultValue(ApplicationMode.BICYCLE, true); } - public final OsmandPreference SHOW_WPT = new BooleanPreference("show_gpx_wpt", true).makeGlobal().cache(); - public final OsmandPreference SHOW_NEARBY_FAVORITES = new BooleanPreference("show_nearby_favorites", false).makeProfile().cache(); - public final OsmandPreference SHOW_NEARBY_POI = new BooleanPreference("show_nearby_poi", false).makeProfile().cache(); + public final OsmandPreference SHOW_WPT = new BooleanPreference(this, "show_gpx_wpt", true).makeGlobal().cache(); + public final OsmandPreference SHOW_NEARBY_FAVORITES = new BooleanPreference(this, "show_nearby_favorites", false).makeProfile().cache(); + public final OsmandPreference SHOW_NEARBY_POI = new BooleanPreference(this, "show_nearby_poi", false).makeProfile().cache(); - public final OsmandPreference SPEAK_STREET_NAMES = new BooleanPreference("speak_street_names", true).makeProfile().cache(); - public final CommonPreference SPEAK_TRAFFIC_WARNINGS = new BooleanPreference("speak_traffic_warnings", true).makeProfile().cache(); + public final OsmandPreference SPEAK_STREET_NAMES = new BooleanPreference(this, "speak_street_names", true).makeProfile().cache(); + public final CommonPreference SPEAK_TRAFFIC_WARNINGS = new BooleanPreference(this, "speak_traffic_warnings", true).makeProfile().cache(); { SPEAK_TRAFFIC_WARNINGS.setModeDefaultValue(ApplicationMode.CAR, true); } - public final CommonPreference SPEAK_PEDESTRIAN = new BooleanPreference("speak_pedestrian", false).makeProfile().cache(); + + public final CommonPreference SPEAK_PEDESTRIAN = new BooleanPreference(this, "speak_pedestrian", false).makeProfile().cache(); + { SPEAK_PEDESTRIAN.setModeDefaultValue(ApplicationMode.CAR, true); } - public final OsmandPreference SPEAK_SPEED_LIMIT = new BooleanPreference("speak_speed_limit", false).makeProfile().cache(); - public final OsmandPreference SPEAK_SPEED_CAMERA = new BooleanPreference("speak_cameras", false).makeProfile().cache(); - public final OsmandPreference SPEAK_TUNNELS = new BooleanPreference("speak_tunnels", false).makeProfile().cache(); - public final OsmandPreference SPEED_CAMERAS_UNINSTALLED = new BooleanPreference("speed_cameras_uninstalled", false).makeGlobal(); - public final OsmandPreference SPEED_CAMERAS_ALERT_SHOWED = new BooleanPreference("speed_cameras_alert_showed", false).makeGlobal(); + public final OsmandPreference SPEAK_SPEED_LIMIT = new BooleanPreference(this, "speak_speed_limit", false).makeProfile().cache(); + public final OsmandPreference SPEAK_SPEED_CAMERA = new BooleanPreference(this, "speak_cameras", false).makeProfile().cache(); + public final OsmandPreference SPEAK_TUNNELS = new BooleanPreference(this, "speak_tunnels", false).makeProfile().cache(); + + public final OsmandPreference SPEED_CAMERAS_UNINSTALLED = new BooleanPreference(this, "speed_cameras_uninstalled", false).makeGlobal(); + public final OsmandPreference SPEED_CAMERAS_ALERT_SHOWED = new BooleanPreference(this, "speed_cameras_alert_showed", false).makeGlobal(); public Set getForbiddenTypes() { Set typeNames = new HashSet<>(); @@ -2178,7 +1283,7 @@ public class OsmandSettings { return typeNames; } - public final OsmandPreference ANNOUNCE_WPT = new BooleanPreference("announce_wpt", true) { + public final OsmandPreference ANNOUNCE_WPT = new BooleanPreference(this, "announce_wpt", true) { @Override protected boolean setValue(Object prefs, Boolean val) { boolean valueSaved = super.setValue(prefs, val); @@ -2190,7 +1295,7 @@ public class OsmandSettings { } }.makeProfile().cache(); - public final OsmandPreference ANNOUNCE_NEARBY_FAVORITES = new BooleanPreference("announce_nearby_favorites", false) { + public final OsmandPreference ANNOUNCE_NEARBY_FAVORITES = new BooleanPreference(this, "announce_nearby_favorites", false) { @Override protected boolean setValue(Object prefs, Boolean val) { boolean valueSaved = super.setValue(prefs, val); @@ -2202,7 +1307,7 @@ public class OsmandSettings { } }.makeProfile().cache(); - public final OsmandPreference ANNOUNCE_NEARBY_POI = new BooleanPreference("announce_nearby_poi", false) { + public final OsmandPreference ANNOUNCE_NEARBY_POI = new BooleanPreference(this, "announce_nearby_poi", false) { @Override protected boolean setValue(Object prefs, Boolean val) { boolean valueSaved = super.setValue(prefs, val); @@ -2214,83 +1319,83 @@ public class OsmandSettings { } }.makeProfile().cache(); - public final OsmandPreference SHOW_START_FINISH_ICONS = new BooleanPreference("show_start_finish_icons", true).makeGlobal().cache(); + public final OsmandPreference GPX_ROUTE_CALC_OSMAND_PARTS = new BooleanPreference(this, "gpx_routing_calculate_osmand_route", true).makeGlobal().cache(); + public final OsmandPreference GPX_CALCULATE_RTEPT = new BooleanPreference(this, "gpx_routing_calculate_rtept", true).makeGlobal().cache(); + public final OsmandPreference GPX_ROUTE_CALC = new BooleanPreference(this, "calc_gpx_route", false).makeGlobal().cache(); + public final OsmandPreference SHOW_START_FINISH_ICONS = new BooleanPreference(this, "show_start_finish_icons", true).makeGlobal().cache(); - public final OsmandPreference GPX_ROUTE_CALC_OSMAND_PARTS = new BooleanPreference("gpx_routing_calculate_osmand_route", true).makeGlobal().cache(); -// public final OsmandPreference GPX_CALCULATE_RTEPT = new BooleanPreference("gpx_routing_calculate_rtept", true).makeGlobal().cache(); - public final OsmandPreference GPX_ROUTE_CALC = new BooleanPreference("calc_gpx_route", false).makeGlobal().cache(); + public final OsmandPreference AVOID_TOLL_ROADS = new BooleanPreference(this, "avoid_toll_roads", false).makeProfile().cache(); + public final OsmandPreference AVOID_MOTORWAY = new BooleanPreference(this, "avoid_motorway", false).makeProfile().cache(); + public final OsmandPreference AVOID_UNPAVED_ROADS = new BooleanPreference(this, "avoid_unpaved_roads", false).makeProfile().cache(); + public final OsmandPreference AVOID_FERRIES = new BooleanPreference(this, "avoid_ferries", false).makeProfile().cache(); - public final OsmandPreference AVOID_TOLL_ROADS = new BooleanPreference("avoid_toll_roads", false).makeProfile().cache(); - public final OsmandPreference AVOID_MOTORWAY = new BooleanPreference("avoid_motorway", false).makeProfile().cache(); - public final OsmandPreference AVOID_UNPAVED_ROADS = new BooleanPreference("avoid_unpaved_roads", false).makeProfile().cache(); - public final OsmandPreference AVOID_FERRIES = new BooleanPreference("avoid_ferries", false).makeProfile().cache(); + public final OsmandPreference PREFER_MOTORWAYS = new BooleanPreference(this, "prefer_motorways", false).makeProfile().cache(); - public final OsmandPreference PREFER_MOTORWAYS = new BooleanPreference("prefer_motorways", false).makeProfile().cache(); + public final OsmandPreference LAST_UPDATES_CARD_REFRESH = new LongPreference(this, "last_updates_card_refresh", 0).makeGlobal(); - public final OsmandPreference LAST_UPDATES_CARD_REFRESH = new LongPreference("last_updates_card_refresh", 0).makeGlobal(); - - public final CommonPreference CURRENT_TRACK_COLOR = new IntPreference("current_track_color", 0).makeGlobal().cache(); - public final CommonPreference CURRENT_TRACK_WIDTH = new StringPreference("current_track_width", "").makeGlobal().cache(); - public final CommonPreference CURRENT_TRACK_SHOW_ARROWS = new BooleanPreference("current_track_show_arrows", false).makeGlobal().cache(); - public final CommonPreference CURRENT_TRACK_SHOW_START_FINISH = new BooleanPreference("current_track_show_start_finish", true).makeGlobal().cache(); - public final ListStringPreference CUSTOM_TRACK_COLORS = (ListStringPreference) new ListStringPreference("custom_track_colors", null, ",").makeGlobal(); + public final CommonPreference CURRENT_TRACK_COLOR = new IntPreference(this, "current_track_color", 0).makeGlobal().cache(); + public final CommonPreference CURRENT_TRACK_WIDTH = new StringPreference(this, "current_track_width", "").makeGlobal().cache(); + public final CommonPreference CURRENT_TRACK_SHOW_ARROWS = new BooleanPreference(this, "current_track_show_arrows", false).makeGlobal().cache(); + public final CommonPreference CURRENT_TRACK_SHOW_START_FINISH = new BooleanPreference(this, "current_track_show_start_finish", true).makeGlobal().cache(); + public final ListStringPreference CUSTOM_TRACK_COLORS = (ListStringPreference) new ListStringPreference(this, "custom_track_colors", null, ",").makeGlobal(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference SAVE_TRACK_INTERVAL = new IntPreference("save_track_interval", 5000).makeProfile(); + public final CommonPreference SAVE_TRACK_INTERVAL = new IntPreference(this, "save_track_interval", 5000).makeProfile(); { SAVE_TRACK_INTERVAL.setModeDefaultValue(ApplicationMode.CAR, 3000); SAVE_TRACK_INTERVAL.setModeDefaultValue(ApplicationMode.BICYCLE, 5000); SAVE_TRACK_INTERVAL.setModeDefaultValue(ApplicationMode.PEDESTRIAN, 10000); } - + // Please note that SAVE_TRACK_MIN_DISTANCE, SAVE_TRACK_PRECISION, SAVE_TRACK_MIN_SPEED should all be "0" for the default profile, as we have no interface to change them - public final CommonPreference SAVE_TRACK_MIN_DISTANCE = new FloatPreference("save_track_min_distance", 0).makeProfile(); + public final CommonPreference SAVE_TRACK_MIN_DISTANCE = new FloatPreference(this, "save_track_min_distance", 0).makeProfile(); //{ // SAVE_TRACK_MIN_DISTANCE.setModeDefaultValue(ApplicationMode.CAR, 5.f); // SAVE_TRACK_MIN_DISTANCE.setModeDefaultValue(ApplicationMode.BICYCLE, 5.f); // SAVE_TRACK_MIN_DISTANCE.setModeDefaultValue(ApplicationMode.PEDESTRIAN, 5.f); //} - public final CommonPreference SAVE_TRACK_PRECISION = new FloatPreference("save_track_precision", 50.0f).makeProfile(); + public final CommonPreference SAVE_TRACK_PRECISION = new FloatPreference(this, "save_track_precision", 50.0f).makeProfile(); //{ // SAVE_TRACK_PRECISION.setModeDefaultValue(ApplicationMode.CAR, 50.f); // SAVE_TRACK_PRECISION.setModeDefaultValue(ApplicationMode.BICYCLE, 50.f); // SAVE_TRACK_PRECISION.setModeDefaultValue(ApplicationMode.PEDESTRIAN, 50.f); //} - public final CommonPreference SAVE_TRACK_MIN_SPEED = new FloatPreference("save_track_min_speed", 0.f).makeProfile(); + public final CommonPreference SAVE_TRACK_MIN_SPEED = new FloatPreference(this, "save_track_min_speed", 0.f).makeProfile(); //{ // SAVE_TRACK_MIN_SPEED.setModeDefaultValue(ApplicationMode.CAR, 2.f); // SAVE_TRACK_MIN_SPEED.setModeDefaultValue(ApplicationMode.BICYCLE, 1.f); // SAVE_TRACK_MIN_SPEED.setModeDefaultValue(ApplicationMode.PEDESTRIAN, 0.f); //} - public final CommonPreference AUTO_SPLIT_RECORDING = new BooleanPreference("auto_split_recording", true).makeProfile(); + public final CommonPreference AUTO_SPLIT_RECORDING = new BooleanPreference(this, "auto_split_recording", true).makeProfile(); - public final CommonPreference SHOW_TRIP_REC_NOTIFICATION = new BooleanPreference("show_trip_recording_notification", true).makeProfile(); + public final CommonPreference SHOW_TRIP_REC_NOTIFICATION = new BooleanPreference(this, "show_trip_recording_notification", true).makeProfile(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference LIVE_MONITORING = new BooleanPreference("live_monitoring", false).makeProfile(); + public final CommonPreference LIVE_MONITORING = new BooleanPreference(this, "live_monitoring", false).makeProfile(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference LIVE_MONITORING_INTERVAL = new IntPreference("live_monitoring_interval", 5000).makeProfile(); + public final CommonPreference LIVE_MONITORING_INTERVAL = new IntPreference(this, "live_monitoring_interval", 5000).makeProfile(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference LIVE_MONITORING_MAX_INTERVAL_TO_SEND = new IntPreference("live_monitoring_maximum_interval_to_send", 900000).makeProfile(); + public final CommonPreference LIVE_MONITORING_MAX_INTERVAL_TO_SEND = new IntPreference(this, "live_monitoring_maximum_interval_to_send", 900000).makeProfile(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference LIVE_MONITORING_URL = new StringPreference("live_monitoring_url", + public final CommonPreference LIVE_MONITORING_URL = new StringPreference(this, "live_monitoring_url", "https://example.com?lat={0}&lon={1}×tamp={2}&hdop={3}&altitude={4}&speed={5}").makeProfile(); - public final CommonPreference GPS_STATUS_APP = new StringPreference("gps_status_app", "").makeGlobal(); + public final CommonPreference GPS_STATUS_APP = new StringPreference(this, "gps_status_app", "").makeGlobal(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference SHOW_OSM_BUGS = new BooleanPreference("show_osm_bugs", false).makeProfile().cache(); + public final OsmandPreference SHOW_OSM_BUGS = new BooleanPreference(this, "show_osm_bugs", false).makeProfile().cache(); - public final OsmandPreference SHOW_OSM_EDITS = new BooleanPreference("show_osm_edits", true).makeProfile().cache(); + public final OsmandPreference SHOW_OSM_EDITS = new BooleanPreference(this, "show_osm_edits", true).makeProfile().cache(); - public final CommonPreference SHOW_CLOSED_OSM_BUGS = new BooleanPreference("show_closed_osm_bugs", false).makeProfile().cache(); - public final CommonPreference SHOW_OSM_BUGS_MIN_ZOOM = new IntPreference("show_osm_bugs_min_zoom", 8).makeProfile().cache(); + public final CommonPreference SHOW_CLOSED_OSM_BUGS = new BooleanPreference(this, "show_closed_osm_bugs", false).makeProfile().cache(); + public final CommonPreference SHOW_OSM_BUGS_MIN_ZOOM = new IntPreference(this, "show_osm_bugs_min_zoom", 8).makeProfile().cache(); + + public final CommonPreference MAP_INFO_CONTROLS = new StringPreference(this, "map_info_controls", "").makeProfile(); - public final CommonPreference MAP_INFO_CONTROLS = new StringPreference("map_info_controls", "").makeProfile(); { for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { MAP_INFO_CONTROLS.setModeDefaultValue(mode, ""); @@ -2299,23 +1404,23 @@ public class OsmandSettings { // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference DEBUG_RENDERING_INFO = new BooleanPreference("debug_rendering", false).makeGlobal(); + public final OsmandPreference DEBUG_RENDERING_INFO = new BooleanPreference(this, "debug_rendering", false).makeGlobal(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference SHOW_FAVORITES = new BooleanPreference("show_favorites", true).makeProfile().cache(); + public final OsmandPreference SHOW_FAVORITES = new BooleanPreference(this, "show_favorites", true).makeProfile().cache(); - public final CommonPreference SHOW_ZOOM_BUTTONS_NAVIGATION = new BooleanPreference("show_zoom_buttons_navigation", false).makeProfile().cache(); + public final CommonPreference SHOW_ZOOM_BUTTONS_NAVIGATION = new BooleanPreference(this, "show_zoom_buttons_navigation", false).makeProfile().cache(); { SHOW_ZOOM_BUTTONS_NAVIGATION.setModeDefaultValue(ApplicationMode.PEDESTRIAN, true); } // Json - public final OsmandPreference SELECTED_GPX = new StringPreference("selected_gpx", "").makeGlobal(); + public final OsmandPreference SELECTED_GPX = new StringPreference(this, "selected_gpx", "").makeGlobal(); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference MAP_SCREEN_ORIENTATION = - new IntPreference("map_screen_orientation", -1/*ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED*/).makeProfile(); + new IntPreference(this, "map_screen_orientation", -1/*ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED*/).makeProfile(); // this value string is synchronized with settings_pref.xml preference name // public final CommonPreference SHOW_VIEW_ANGLE = new BooleanPreference("show_view_angle", false).makeProfile().cache(); @@ -2327,7 +1432,7 @@ public class OsmandSettings { // this value string is synchronized with settings_pref.xml preference name // seconds to auto_follow - public final CommonPreference AUTO_FOLLOW_ROUTE = new IntPreference("auto_follow_route", 0).makeProfile(); + public final CommonPreference AUTO_FOLLOW_ROUTE = new IntPreference(this, "auto_follow_route", 0).makeProfile(); { AUTO_FOLLOW_ROUTE.setModeDefaultValue(ApplicationMode.CAR, 15); @@ -2337,7 +1442,7 @@ public class OsmandSettings { // this value string is synchronized with settings_pref.xml preference name // seconds to auto_follow - public final CommonPreference KEEP_INFORMING = new IntPreference("keep_informing", 0).makeProfile(); + public final CommonPreference KEEP_INFORMING = new IntPreference(this, "keep_informing", 0).makeProfile(); { KEEP_INFORMING.setModeDefaultValue(ApplicationMode.CAR, 0); @@ -2345,9 +1450,9 @@ public class OsmandSettings { KEEP_INFORMING.setModeDefaultValue(ApplicationMode.PEDESTRIAN, 0); } - public final CommonPreference USE_SYSTEM_SCREEN_TIMEOUT = new BooleanPreference("use_system_screen_timeout", false).makeProfile(); + public final CommonPreference USE_SYSTEM_SCREEN_TIMEOUT = new BooleanPreference(this, "use_system_screen_timeout", false).makeProfile(); - public final CommonPreference TURN_SCREEN_ON_TIME_INT = new IntPreference("turn_screen_on_time_int", 0).makeProfile(); + public final CommonPreference TURN_SCREEN_ON_TIME_INT = new IntPreference(this, "turn_screen_on_time_int", 0).makeProfile(); { TURN_SCREEN_ON_TIME_INT.setModeDefaultValue(ApplicationMode.CAR, 0); @@ -2355,17 +1460,17 @@ public class OsmandSettings { TURN_SCREEN_ON_TIME_INT.setModeDefaultValue(ApplicationMode.PEDESTRIAN, 0); } - public final CommonPreference TURN_SCREEN_ON_SENSOR = new BooleanPreference("turn_screen_on_sensor", false).makeProfile(); - + public final CommonPreference TURN_SCREEN_ON_SENSOR = new BooleanPreference(this, "turn_screen_on_sensor", false).makeProfile(); + { TURN_SCREEN_ON_SENSOR.setModeDefaultValue(ApplicationMode.CAR, false); TURN_SCREEN_ON_SENSOR.setModeDefaultValue(ApplicationMode.BICYCLE, false); TURN_SCREEN_ON_SENSOR.setModeDefaultValue(ApplicationMode.PEDESTRIAN, false); } - public final CommonPreference TURN_SCREEN_ON_NAVIGATION_INSTRUCTIONS = new BooleanPreference("turn_screen_on_navigation_instructions", false).makeProfile(); + public final CommonPreference TURN_SCREEN_ON_NAVIGATION_INSTRUCTIONS = new BooleanPreference(this, "turn_screen_on_navigation_instructions", false).makeProfile(); - public final CommonPreference TURN_SCREEN_ON_POWER_BUTTON = new BooleanPreference("turn_screen_on_power_button", false).makeProfile(); + public final CommonPreference TURN_SCREEN_ON_POWER_BUTTON = new BooleanPreference(this, "turn_screen_on_power_button", false).makeProfile(); // this value string is synchronized with settings_pref.xml preference name // try without AUTO_FOLLOW_ROUTE_NAV (see forum discussion 'Simplify our navigation preference menu') @@ -2376,7 +1481,7 @@ public class OsmandSettings { public static final int ROTATE_MAP_BEARING = 1; public static final int ROTATE_MAP_COMPASS = 2; public final CommonPreference ROTATE_MAP = - new IntPreference("rotate_map", ROTATE_MAP_NONE).makeProfile().cache(); + new IntPreference(this, "rotate_map", ROTATE_MAP_NONE).makeProfile().cache(); { ROTATE_MAP.setModeDefaultValue(ApplicationMode.CAR, ROTATE_MAP_BEARING); @@ -2390,16 +1495,16 @@ public class OsmandSettings { public static final int MIDDLE_BOTTOM_CONSTANT = 2; public static final int MIDDLE_TOP_CONSTANT = 3; public static final int LANDSCAPE_MIDDLE_RIGHT_CONSTANT = 4; - public final CommonPreference CENTER_POSITION_ON_MAP = new BooleanPreference("center_position_on_map", false).makeProfile(); + public final CommonPreference CENTER_POSITION_ON_MAP = new BooleanPreference(this, "center_position_on_map", false).makeProfile(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference MAX_LEVEL_TO_DOWNLOAD_TILE = new IntPreference("max_level_download_tile", 20).makeProfile().cache(); + public final OsmandPreference MAX_LEVEL_TO_DOWNLOAD_TILE = new IntPreference(this, "max_level_download_tile", 20).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference LEVEL_TO_SWITCH_VECTOR_RASTER = new IntPreference("level_to_switch_vector_raster", 1).makeGlobal().cache(); + public final OsmandPreference LEVEL_TO_SWITCH_VECTOR_RASTER = new IntPreference(this, "level_to_switch_vector_raster", 1).makeGlobal().cache(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference AUDIO_MANAGER_STREAM = new IntPreference("audio_stream", 3/*AudioManager.STREAM_MUSIC*/) { + public final OsmandPreference AUDIO_MANAGER_STREAM = new IntPreference(this, "audio_stream", 3/*AudioManager.STREAM_MUSIC*/) { @Override protected boolean setValue(Object prefs, Integer stream) { boolean valueSaved = super.setValue(prefs, stream); @@ -2425,7 +1530,7 @@ public class OsmandSettings { }.makeProfile(); // Corresponding USAGE value for AudioAttributes - public final OsmandPreference AUDIO_USAGE = new IntPreference("audio_usage", + public final OsmandPreference AUDIO_USAGE = new IntPreference(this, "audio_usage", 12/*AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE*/).makeProfile(); // For now this can be changed only in TestVoiceActivity @@ -2433,67 +1538,68 @@ public class OsmandSettings { { // 1500 ms delay works for most configurations to establish a BT SCO link - VOICE_PROMPT_DELAY[0] = new IntPreference("voice_prompt_delay_0", 1500).makeGlobal().cache(); /*AudioManager.STREAM_VOICE_CALL*/ + VOICE_PROMPT_DELAY[0] = new IntPreference(this, "voice_prompt_delay_0", 1500).makeGlobal().cache(); /*AudioManager.STREAM_VOICE_CALL*/ // On most devices sound output works pomptly so usually no voice prompt delay needed - VOICE_PROMPT_DELAY[3] = new IntPreference("voice_prompt_delay_3", 0).makeGlobal().cache(); /*AudioManager.STREAM_MUSIC*/ - VOICE_PROMPT_DELAY[5] = new IntPreference("voice_prompt_delay_5", 0).makeGlobal().cache(); /*AudioManager.STREAM_NOTIFICATION*/ + VOICE_PROMPT_DELAY[3] = new IntPreference(this, "voice_prompt_delay_3", 0).makeGlobal().cache(); /*AudioManager.STREAM_MUSIC*/ + VOICE_PROMPT_DELAY[5] = new IntPreference(this, "voice_prompt_delay_5", 0).makeGlobal().cache(); /*AudioManager.STREAM_NOTIFICATION*/ } - public final OsmandPreference DISPLAY_TTS_UTTERANCE = new BooleanPreference("display_tts_utterance", false).makeGlobal(); + + public final OsmandPreference DISPLAY_TTS_UTTERANCE = new BooleanPreference(this, "display_tts_utterance", false).makeGlobal(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference MAP_ONLINE_DATA = new BooleanPreference("map_online_data", false).makeProfile(); + public final CommonPreference MAP_ONLINE_DATA = new BooleanPreference(this, "map_online_data", false).makeProfile(); - public final CommonPreference TERRAIN_MODE = new EnumStringPreference<>("terrain_mode", TerrainMode.HILLSHADE, TerrainMode.values()).makeProfile(); + public final CommonPreference TERRAIN_MODE = new EnumStringPreference<>(this, "terrain_mode", TerrainMode.HILLSHADE, TerrainMode.values()).makeProfile(); - public final CommonPreference HILLSHADE_MIN_ZOOM = new IntPreference("hillshade_min_zoom", 3).makeProfile(); + public final CommonPreference HILLSHADE_MIN_ZOOM = new IntPreference(this, "hillshade_min_zoom", 3).makeProfile(); - public final CommonPreference HILLSHADE_MAX_ZOOM = new IntPreference("hillshade_max_zoom", 17).makeProfile(); + public final CommonPreference HILLSHADE_MAX_ZOOM = new IntPreference(this, "hillshade_max_zoom", 17).makeProfile(); - public final CommonPreference HILLSHADE_TRANSPARENCY = new IntPreference("hillshade_transparency", 100).makeProfile(); + public final CommonPreference HILLSHADE_TRANSPARENCY = new IntPreference(this, "hillshade_transparency", 100).makeProfile(); - public final CommonPreference SLOPE_MIN_ZOOM = new IntPreference("slope_min_zoom", 3).makeProfile(); + public final CommonPreference SLOPE_MIN_ZOOM = new IntPreference(this, "slope_min_zoom", 3).makeProfile(); - public final CommonPreference SLOPE_MAX_ZOOM = new IntPreference("slope_max_zoom", 17).makeProfile(); + public final CommonPreference SLOPE_MAX_ZOOM = new IntPreference(this, "slope_max_zoom", 17).makeProfile(); - public final CommonPreference SLOPE_TRANSPARENCY = new IntPreference("slope_transparency", 80).makeProfile(); + public final CommonPreference SLOPE_TRANSPARENCY = new IntPreference(this, "slope_transparency", 80).makeProfile(); - public final CommonPreference TERRAIN = new BooleanPreference("terrain_layer", true).makeProfile(); + public final CommonPreference TERRAIN = new BooleanPreference(this, "terrain_layer", true).makeProfile(); - public final CommonPreference CONTOUR_LINES_ZOOM = new StringPreference("contour_lines_zoom", null).makeProfile().cache(); + public final CommonPreference CONTOUR_LINES_ZOOM = new StringPreference(this, "contour_lines_zoom", null).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference MAP_OVERLAY = new StringPreference("map_overlay", null).makeProfile().cache(); + public final CommonPreference MAP_OVERLAY = new StringPreference(this, "map_overlay", null).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference MAP_UNDERLAY = new StringPreference("map_underlay", null).makeProfile().cache(); + public final CommonPreference MAP_UNDERLAY = new StringPreference(this, "map_underlay", null).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference MAP_OVERLAY_TRANSPARENCY = new IntPreference("overlay_transparency", 100).makeProfile().cache(); + public final CommonPreference MAP_OVERLAY_TRANSPARENCY = new IntPreference(this, "overlay_transparency", 100).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference MAP_TRANSPARENCY = new IntPreference("map_transparency", 255).makeProfile().cache(); + public final CommonPreference MAP_TRANSPARENCY = new IntPreference(this, "map_transparency", 255).makeProfile().cache(); // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference MAP_TILE_SOURCES = new StringPreference("map_tile_sources", + public final CommonPreference MAP_TILE_SOURCES = new StringPreference(this, "map_tile_sources", TileSourceManager.getMapnikSource().getName()).makeProfile(); public final CommonPreference LAYER_TRANSPARENCY_SEEKBAR_MODE = - new EnumStringPreference<>("layer_transparency_seekbar_mode", LayerTransparencySeekbarMode.UNDEFINED, LayerTransparencySeekbarMode.values()); + new EnumStringPreference<>(this, "layer_transparency_seekbar_mode", LayerTransparencySeekbarMode.UNDEFINED, LayerTransparencySeekbarMode.values()); - public final CommonPreference MAP_OVERLAY_PREVIOUS = new StringPreference("map_overlay_previous", null).makeGlobal().cache(); + public final CommonPreference MAP_OVERLAY_PREVIOUS = new StringPreference(this, "map_overlay_previous", null).makeGlobal().cache(); - public final CommonPreference MAP_UNDERLAY_PREVIOUS = new StringPreference("map_underlay_previous", null).makeGlobal().cache(); + public final CommonPreference MAP_UNDERLAY_PREVIOUS = new StringPreference(this, "map_underlay_previous", null).makeGlobal().cache(); - public CommonPreference PREVIOUS_INSTALLED_VERSION = new StringPreference("previous_installed_version", "").makeGlobal(); + public CommonPreference PREVIOUS_INSTALLED_VERSION = new StringPreference(this, "previous_installed_version", "").makeGlobal(); - public final OsmandPreference SHOULD_SHOW_FREE_VERSION_BANNER = new BooleanPreference("should_show_free_version_banner", false).makeGlobal().cache(); + public final OsmandPreference SHOULD_SHOW_FREE_VERSION_BANNER = new BooleanPreference(this, "should_show_free_version_banner", false).makeGlobal().cache(); - public final OsmandPreference MARKERS_DISTANCE_INDICATION_ENABLED = new BooleanPreference("markers_distance_indication_enabled", true).makeProfile(); + public final OsmandPreference MARKERS_DISTANCE_INDICATION_ENABLED = new BooleanPreference(this, "markers_distance_indication_enabled", true).makeProfile(); - public final OsmandPreference DISPLAYED_MARKERS_WIDGETS_COUNT = new IntPreference("displayed_markers_widgets_count", 1).makeProfile(); + public final OsmandPreference DISPLAYED_MARKERS_WIDGETS_COUNT = new IntPreference(this, "displayed_markers_widgets_count", 1).makeProfile(); public final CommonPreference MAP_MARKERS_MODE = - new EnumStringPreference<>("map_markers_mode", MapMarkersMode.TOOLBAR, MapMarkersMode.values()); + new EnumStringPreference<>(this, "map_markers_mode", MapMarkersMode.TOOLBAR, MapMarkersMode.values()); { MAP_MARKERS_MODE.makeProfile().cache(); @@ -2503,19 +1609,19 @@ public class OsmandSettings { MAP_MARKERS_MODE.setModeDefaultValue(ApplicationMode.PEDESTRIAN, MapMarkersMode.TOOLBAR); } - public final OsmandPreference SHOW_MAP_MARKERS = new BooleanPreference("show_map_markers", true).makeProfile(); + public final OsmandPreference SHOW_MAP_MARKERS = new BooleanPreference(this, "show_map_markers", true).makeProfile(); - public final OsmandPreference SHOW_COORDINATES_WIDGET = new BooleanPreference("show_coordinates_widget", false).makeProfile().cache(); + public final OsmandPreference SHOW_COORDINATES_WIDGET = new BooleanPreference(this, "show_coordinates_widget", false).makeProfile().cache(); - public final CommonPreference NOTES_SORT_BY_MODE = new EnumStringPreference<>("notes_sort_by_mode", NotesSortByMode.BY_DATE, NotesSortByMode.values()); - public final CommonPreference TRACKS_SORT_BY_MODE = new EnumStringPreference<>("tracks_sort_by_mode", TracksSortByMode.BY_DATE, TracksSortByMode.values()); + public final CommonPreference NOTES_SORT_BY_MODE = new EnumStringPreference<>(this, "notes_sort_by_mode", NotesSortByMode.BY_DATE, NotesSortByMode.values()); + public final CommonPreference TRACKS_SORT_BY_MODE = new EnumStringPreference<>(this, "tracks_sort_by_mode", TracksSortByMode.BY_DATE, TracksSortByMode.values()); - public final OsmandPreference ANIMATE_MY_LOCATION = new BooleanPreference("animate_my_location", true).makeProfile().cache(); + public final OsmandPreference ANIMATE_MY_LOCATION = new BooleanPreference(this, "animate_my_location", true).makeProfile().cache(); - public final OsmandPreference EXTERNAL_INPUT_DEVICE = new IntPreference("external_input_device", 0).makeProfile(); + public final OsmandPreference EXTERNAL_INPUT_DEVICE = new IntPreference(this, "external_input_device", 0).makeProfile(); - public final OsmandPreference ROUTE_MAP_MARKERS_START_MY_LOC = new BooleanPreference("route_map_markers_start_my_loc", false).makeGlobal().cache(); - public final OsmandPreference ROUTE_MAP_MARKERS_ROUND_TRIP = new BooleanPreference("route_map_markers_round_trip", false).makeGlobal().cache(); + public final OsmandPreference ROUTE_MAP_MARKERS_START_MY_LOC = new BooleanPreference(this, "route_map_markers_start_my_loc", false).makeGlobal().cache(); + public final OsmandPreference ROUTE_MAP_MARKERS_ROUND_TRIP = new BooleanPreference(this, "route_map_markers_round_trip", false).makeGlobal().cache(); public ITileSource getMapTileSource(boolean warnWhenSelected) { String tileName = MAP_TILE_SOURCES.get(); @@ -2643,7 +1749,7 @@ public class OsmandSettings { public static final int EXTERNAL_STORAGE_TYPE_INTERNAL_FILE = 2; // ctx.getFilesDir() public static final int EXTERNAL_STORAGE_TYPE_OBB = 3; // ctx.getObbDirs public static final int EXTERNAL_STORAGE_TYPE_SPECIFIED = 4; - public final OsmandPreference OSMAND_USAGE_SPACE = new LongPreference("osmand_usage_space", 0).makeGlobal(); + public final OsmandPreference OSMAND_USAGE_SPACE = new LongPreference(this, "osmand_usage_space", 0).makeGlobal(); public void freezeExternalStorageDirectory() { @@ -2662,7 +1768,7 @@ public class OsmandSettings { setExternalStorageDirectoryPre19(getInternalAppPath().getAbsolutePath()); } else { File externalStorage = getExternal1AppPath(); - if (externalStorage != null && OsmandSettings.isWritable(externalStorage)) { + if (externalStorage != null && FileUtils.isWritable(externalStorage)) { setExternalStorageDirectoryV19(EXTERNAL_STORAGE_TYPE_EXTERNAL_FILE, getExternal1AppPath().getAbsolutePath()); } else { @@ -2714,7 +1820,7 @@ public class OsmandSettings { int type = settingsAPI.getInt(globalPreferences, EXTERNAL_STORAGE_DIR_TYPE_V19, -1); File location = getDefaultLocationV19(); if (type == -1) { - if (isWritable(location)) { + if (FileUtils.isWritable(location)) { if (tp != null) { tp.value = settingsAPI.contains(globalPreferences, EXTERNAL_STORAGE_DIR_V19) ? EXTERNAL_STORAGE_TYPE_SPECIFIED : @@ -2752,20 +1858,6 @@ public class OsmandSettings { return new File(location); } - - public static boolean isWritable(File dirToTest) { - boolean isWriteable = false; - try { - dirToTest.mkdirs(); - File writeTestFile = File.createTempFile("osmand_", ".tmp", dirToTest); - isWriteable = writeTestFile.exists(); - writeTestFile.delete(); - } catch (IOException e) { - isWriteable = false; - } - return isWriteable; - } - public boolean isExternalStorageDirectoryTypeSpecifiedV19() { return settingsAPI.contains(globalPreferences, EXTERNAL_STORAGE_DIR_TYPE_V19); } @@ -2879,10 +1971,6 @@ public class OsmandSettings { } } - private Object objectToShow; - private boolean editObjectToShow; - private String searchRequestToShow; - public void setSearchRequestToShow(String request) { this.searchRequestToShow = request; } @@ -2910,7 +1998,7 @@ public class OsmandSettings { } public void setMapLocationToShow(double latitude, double longitude, int zoom, PointDescription pointDescription, - boolean addToHistory, Object toShow) { + boolean addToHistory, Object toShow) { SettingsEditor edit = settingsAPI.edit(globalPreferences); edit.putFloat(MAP_LAT_TO_SHOW, (float) latitude); edit.putFloat(MAP_LON_TO_SHOW, (float) longitude); @@ -2974,7 +2062,6 @@ public class OsmandSettings { public final static String INTERMEDIATE_POINTS = "intermediate_points"; //$NON-NLS-1$ public final static String INTERMEDIATE_POINTS_DESCRIPTION = "intermediate_points_description"; //$NON-NLS-1$ - private IntermediatePointsStorage intermediatePointsStorage = new IntermediatePointsStorage(); public final static String POINT_NAVIGATE_LAT_BACKUP = "point_navigate_lat_backup"; //$NON-NLS-1$ public final static String POINT_NAVIGATE_LON_BACKUP = "point_navigate_lon_backup"; //$NON-NLS-1$ @@ -2988,11 +2075,10 @@ public class OsmandSettings { public final static String MY_LOC_POINT_LON = "my_loc_point_lon"; public final static String MY_LOC_POINT_DESCRIPTION = "my_loc_point_description"; - private static final String IMPASSABLE_ROAD_POINTS = "impassable_road_points"; - private static final String IMPASSABLE_ROADS_DESCRIPTIONS = "impassable_roads_descriptions"; - private static final String IMPASSABLE_ROADS_IDS = "impassable_roads_ids"; - private static final String IMPASSABLE_ROADS_APP_MODE_KEYS = "impassable_roads_app_mode_keys"; - private ImpassableRoadsStorage mImpassableRoadsStorage = new ImpassableRoadsStorage(); + public static final String IMPASSABLE_ROAD_POINTS = "impassable_road_points"; + public static final String IMPASSABLE_ROADS_DESCRIPTIONS = "impassable_roads_descriptions"; + public static final String IMPASSABLE_ROADS_IDS = "impassable_roads_ids"; + public static final String IMPASSABLE_ROADS_APP_MODE_KEYS = "impassable_roads_app_mode_keys"; public void backupPointToStart() { settingsAPI.edit(globalPreferences) @@ -3142,334 +2228,7 @@ public class OsmandSettings { } public final CommonPreference USE_INTERMEDIATE_POINTS_NAVIGATION = - new BooleanPreference("use_intermediate_points_navigation", false).makeGlobal().cache(); - - - private class IntermediatePointsStorage extends MapPointsStorage { - - public IntermediatePointsStorage() { - pointsKey = INTERMEDIATE_POINTS; - descriptionsKey = INTERMEDIATE_POINTS_DESCRIPTION; - } - - @Override - public boolean savePoints(List ps, List ds) { - boolean res = super.savePoints(ps, ds); - backupTargetPoints(); - return res; - } - } - - private class ImpassableRoadsStorage extends MapPointsStorage { - - protected String roadsIdsKey; - protected String appModeKey; - - public ImpassableRoadsStorage() { - pointsKey = IMPASSABLE_ROAD_POINTS; - descriptionsKey = IMPASSABLE_ROADS_DESCRIPTIONS; - roadsIdsKey = IMPASSABLE_ROADS_IDS; - appModeKey = IMPASSABLE_ROADS_APP_MODE_KEYS; - } - - public List getRoadIds(int size) { - List list = new ArrayList<>(); - String roadIds = settingsAPI.getString(globalPreferences, roadsIdsKey, ""); - if (roadIds.trim().length() > 0) { - StringTokenizer tok = new StringTokenizer(roadIds, ","); - while (tok.hasMoreTokens() && list.size() <= size) { - list.add(Long.parseLong(tok.nextToken())); - } - } - while (list.size() < size) { - list.add(0L); - } - return list; - } - - public List getAppModeKeys(int size) { - List list = new ArrayList<>(); - String roadIds = settingsAPI.getString(globalPreferences, appModeKey, ""); - if (roadIds.trim().length() > 0) { - StringTokenizer tok = new StringTokenizer(roadIds, ","); - while (tok.hasMoreTokens() && list.size() <= size) { - list.add(tok.nextToken()); - } - } - while (list.size() < size) { - list.add(""); - } - return list; - } - - public List getImpassableRoadsInfo() { - List points = getPoints(); - List roadIds = getRoadIds(points.size()); - List appModeKeys = getAppModeKeys(points.size()); - List descriptions = getPointDescriptions(points.size()); - - List avoidRoadsInfo = new ArrayList<>(); - - for (int i = 0; i < points.size(); i++) { - LatLon latLon = points.get(i); - PointDescription description = PointDescription.deserializeFromString(descriptions.get(i), null); - - AvoidRoadInfo avoidRoadInfo = new AvoidRoadInfo(); - avoidRoadInfo.id = roadIds.get(i); - avoidRoadInfo.latitude = latLon.getLatitude(); - avoidRoadInfo.longitude = latLon.getLongitude(); - avoidRoadInfo.name = description.getName(); - avoidRoadInfo.appModeKey = appModeKeys.get(i); - avoidRoadsInfo.add(avoidRoadInfo); - } - - return avoidRoadsInfo; - } - - public boolean addImpassableRoadInfo(AvoidRoadInfo avoidRoadInfo) { - List points = getPoints(); - List roadIds = getRoadIds(points.size()); - List appModeKeys = getAppModeKeys(points.size()); - List descriptions = getPointDescriptions(points.size()); - - roadIds.add(0, avoidRoadInfo.id); - points.add(0, new LatLon(avoidRoadInfo.latitude, avoidRoadInfo.longitude)); - appModeKeys.add(0, avoidRoadInfo.appModeKey); - descriptions.add(0, PointDescription.serializeToString(new PointDescription("", avoidRoadInfo.name))); - - return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); - } - - public boolean updateImpassableRoadInfo(AvoidRoadInfo avoidRoadInfo) { - List points = getPoints(); - - int index = points.indexOf(new LatLon(avoidRoadInfo.latitude, avoidRoadInfo.longitude)); - if (index != -1) { - List roadIds = getRoadIds(points.size()); - List appModeKeys = getAppModeKeys(points.size()); - List descriptions = getPointDescriptions(points.size()); - - roadIds.set(index, avoidRoadInfo.id); - appModeKeys.set(index, avoidRoadInfo.appModeKey); - descriptions.set(index, PointDescription.serializeToString(new PointDescription("", avoidRoadInfo.name))); - return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); - } - return false; - } - - @Override - public boolean deletePoint(int index) { - List points = getPoints(); - List roadIds = getRoadIds(points.size()); - List appModeKeys = getAppModeKeys(points.size()); - List descriptions = getPointDescriptions(points.size()); - - if (index < points.size()) { - points.remove(index); - roadIds.remove(index); - appModeKeys.remove(index); - descriptions.remove(index); - return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); - } - return false; - } - - @Override - public boolean deletePoint(LatLon latLon) { - List points = getPoints(); - List roadIds = getRoadIds(points.size()); - List appModeKeys = getAppModeKeys(points.size()); - List descriptions = getPointDescriptions(points.size()); - - int index = points.indexOf(latLon); - if (index != -1) { - points.remove(index); - roadIds.remove(index); - appModeKeys.remove(index); - descriptions.remove(index); - return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); - } - return false; - } - - @Override - public boolean movePoint(LatLon latLonEx, LatLon latLonNew) { - List points = getPoints(); - List roadIds = getRoadIds(points.size()); - List appModeKeys = getAppModeKeys(points.size()); - List descriptions = getPointDescriptions(points.size()); - - int i = points.indexOf(latLonEx); - if (i != -1) { - points.set(i, latLonNew); - return saveAvoidRoadData(points, descriptions, roadIds, appModeKeys); - } else { - return false; - } - } - - public boolean saveAvoidRoadData(List points, List descriptions, - List roadIds, List appModeKeys) { - return savePoints(points, descriptions) && saveRoadIds(roadIds) && saveAppModeKeys(appModeKeys); - } - - public boolean saveRoadIds(List roadIds) { - StringBuilder stringBuilder = new StringBuilder(); - Iterator iterator = roadIds.iterator(); - while (iterator.hasNext()) { - stringBuilder.append(iterator.next()); - if (iterator.hasNext()) { - stringBuilder.append(","); - } - } - return settingsAPI.edit(globalPreferences) - .putString(roadsIdsKey, stringBuilder.toString()) - .commit(); - } - - public boolean saveAppModeKeys(List appModeKeys) { - StringBuilder stringBuilder = new StringBuilder(); - Iterator iterator = appModeKeys.iterator(); - while (iterator.hasNext()) { - stringBuilder.append(iterator.next()); - if (iterator.hasNext()) { - stringBuilder.append(","); - } - } - return settingsAPI.edit(globalPreferences) - .putString(appModeKey, stringBuilder.toString()) - .commit(); - } - } - - private abstract class MapPointsStorage { - - protected String pointsKey; - protected String descriptionsKey; - - public MapPointsStorage() { - } - - public List getPointDescriptions(int sz) { - List list = new ArrayList<>(); - String ip = settingsAPI.getString(globalPreferences, descriptionsKey, ""); - if (ip.trim().length() > 0) { - list.addAll(Arrays.asList(ip.split("--"))); - } - while (list.size() > sz) { - list.remove(list.size() - 1); - } - while (list.size() < sz) { - list.add(""); - } - return list; - } - - public List getPoints() { - List list = new ArrayList<>(); - String ip = settingsAPI.getString(globalPreferences, pointsKey, ""); - if (ip.trim().length() > 0) { - StringTokenizer tok = new StringTokenizer(ip, ","); - while (tok.hasMoreTokens()) { - String lat = tok.nextToken(); - if (!tok.hasMoreTokens()) { - break; - } - String lon = tok.nextToken(); - list.add(new LatLon(Float.parseFloat(lat), Float.parseFloat(lon))); - } - } - return list; - } - - public boolean insertPoint(double latitude, double longitude, PointDescription historyDescription, int index) { - List ps = getPoints(); - List ds = getPointDescriptions(ps.size()); - ps.add(index, new LatLon(latitude, longitude)); - ds.add(index, PointDescription.serializeToString(historyDescription)); - if (historyDescription != null && !historyDescription.isSearchingAddress(ctx)) { - SearchHistoryHelper.getInstance(ctx).addNewItemToHistory(latitude, longitude, historyDescription); - } - return savePoints(ps, ds); - } - - public boolean updatePoint(double latitude, double longitude, PointDescription historyDescription) { - List ps = getPoints(); - List ds = getPointDescriptions(ps.size()); - int i = ps.indexOf(new LatLon(latitude, longitude)); - if (i != -1) { - ds.set(i, PointDescription.serializeToString(historyDescription)); - if (historyDescription != null && !historyDescription.isSearchingAddress(ctx)) { - SearchHistoryHelper.getInstance(ctx).addNewItemToHistory(latitude, longitude, historyDescription); - } - return savePoints(ps, ds); - } else { - return false; - } - } - - public boolean deletePoint(int index) { - List ps = getPoints(); - List ds = getPointDescriptions(ps.size()); - if (index < ps.size()) { - ps.remove(index); - ds.remove(index); - return savePoints(ps, ds); - } else { - return false; - } - } - - public boolean deletePoint(LatLon latLon) { - List ps = getPoints(); - List ds = getPointDescriptions(ps.size()); - int index = ps.indexOf(latLon); - if (index != -1) { - ps.remove(index); - ds.remove(index); - return savePoints(ps, ds); - } else { - return false; - } - } - - public boolean savePoints(List ps, List ds) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < ps.size(); i++) { - if (i > 0) { - sb.append(","); - } - sb.append(((float) ps.get(i).getLatitude() + "")).append(",").append(((float) ps.get(i).getLongitude() + "")); - } - StringBuilder tb = new StringBuilder(); - for (int i = 0; i < ds.size(); i++) { - if (i > 0) { - tb.append("--"); - } - if (ds.get(i) == null) { - tb.append(""); - } else { - tb.append(ds.get(i)); - } - } - return settingsAPI.edit(globalPreferences) - .putString(pointsKey, sb.toString()) - .putString(descriptionsKey, tb.toString()) - .commit(); - } - - public boolean movePoint(LatLon latLonEx, LatLon latLonNew) { - List ps = getPoints(); - List ds = getPointDescriptions(ps.size()); - int i = ps.indexOf(latLonEx); - if (i != -1) { - ps.set(i, latLonNew); - return savePoints(ps, ds); - } else { - return false; - } - } - } + new BooleanPreference(this, "use_intermediate_points_navigation", false).makeGlobal().cache(); public List getIntermediatePointDescriptions(int sz) { @@ -3530,27 +2289,27 @@ public class OsmandSettings { } public List getImpassableRoadPoints() { - return mImpassableRoadsStorage.getImpassableRoadsInfo(); + return impassableRoadsStorage.getImpassableRoadsInfo(); } public boolean addImpassableRoad(AvoidRoadInfo avoidRoadInfo) { - return mImpassableRoadsStorage.addImpassableRoadInfo(avoidRoadInfo); + return impassableRoadsStorage.addImpassableRoadInfo(avoidRoadInfo); } public boolean updateImpassableRoadInfo(AvoidRoadInfo avoidRoadInfo) { - return mImpassableRoadsStorage.updateImpassableRoadInfo(avoidRoadInfo); + return impassableRoadsStorage.updateImpassableRoadInfo(avoidRoadInfo); } public boolean removeImpassableRoad(int index) { - return mImpassableRoadsStorage.deletePoint(index); + return impassableRoadsStorage.deletePoint(index); } public boolean removeImpassableRoad(LatLon latLon) { - return mImpassableRoadsStorage.deletePoint(latLon); + return impassableRoadsStorage.deletePoint(latLon); } public boolean moveImpassableRoad(LatLon latLonEx, LatLon latLonNew) { - return mImpassableRoadsStorage.movePoint(latLonEx, latLonNew); + return impassableRoadsStorage.movePoint(latLonEx, latLonNew); } /** @@ -3562,16 +2321,16 @@ public class OsmandSettings { public static final String QUICK_FAB_MARGIN_X_LANDSCAPE_MARGIN = "quick_fab_margin_x_landscape_margin"; public static final String QUICK_FAB_MARGIN_Y_LANDSCAPE_MARGIN = "quick_fab_margin_y_landscape_margin"; - public final CommonPreference QUICK_ACTION = new BooleanPreference("quick_action_state", false).makeProfile(); + public final CommonPreference QUICK_ACTION = new BooleanPreference(this, "quick_action_state", false).makeProfile(); - public final CommonPreference QUICK_ACTION_LIST = new StringPreference("quick_action_list", "").makeGlobal(); + public final CommonPreference QUICK_ACTION_LIST = new StringPreference(this, "quick_action_list", "").makeGlobal(); - public final CommonPreference IS_QUICK_ACTION_TUTORIAL_SHOWN = new BooleanPreference("quick_action_tutorial", false).makeGlobal(); + public final CommonPreference IS_QUICK_ACTION_TUTORIAL_SHOWN = new BooleanPreference(this, "quick_action_tutorial", false).makeGlobal(); - private final CommonPreference QUICK_ACTION_FAB_MARGIN_X_PORTRAIT = new IntPreference(QUICK_FAB_MARGIN_X_PORTRAIT_MARGIN, 0).makeProfile(); - private final CommonPreference QUICK_ACTION_FAB_MARGIN_Y_PORTRAIT = new IntPreference(QUICK_FAB_MARGIN_Y_PORTRAIT_MARGIN, 0).makeProfile(); - private final CommonPreference QUICK_ACTION_FAB_MARGIN_X_LANDSCAPE_MARGIN = new IntPreference(QUICK_FAB_MARGIN_X_LANDSCAPE_MARGIN, 0).makeProfile(); - private final CommonPreference QUICK_ACTION_FAB_MARGIN_Y_LANDSCAPE_MARGIN = new IntPreference(QUICK_FAB_MARGIN_Y_LANDSCAPE_MARGIN, 0).makeProfile(); + private final CommonPreference QUICK_ACTION_FAB_MARGIN_X_PORTRAIT = new IntPreference(this, QUICK_FAB_MARGIN_X_PORTRAIT_MARGIN, 0).makeProfile(); + private final CommonPreference QUICK_ACTION_FAB_MARGIN_Y_PORTRAIT = new IntPreference(this, QUICK_FAB_MARGIN_Y_PORTRAIT_MARGIN, 0).makeProfile(); + private final CommonPreference QUICK_ACTION_FAB_MARGIN_X_LANDSCAPE_MARGIN = new IntPreference(this, QUICK_FAB_MARGIN_X_LANDSCAPE_MARGIN, 0).makeProfile(); + private final CommonPreference QUICK_ACTION_FAB_MARGIN_Y_LANDSCAPE_MARGIN = new IntPreference(this, QUICK_FAB_MARGIN_Y_LANDSCAPE_MARGIN, 0).makeProfile(); public boolean setPortraitFabMargin(int x, int y) { return QUICK_ACTION_FAB_MARGIN_X_PORTRAIT.set(x) && QUICK_ACTION_FAB_MARGIN_Y_PORTRAIT.set(y); @@ -3717,10 +2476,10 @@ public class OsmandSettings { return settingsAPI.edit(globalPreferences).putString(LAST_SEARCHED_INTERSECTED_STREET, street).commit(); } - public final OsmandPreference LAST_SELECTED_GPX_TRACK_FOR_NEW_POINT = new StringPreference("last_selected_gpx_track_for_new_point", null).makeGlobal().cache(); + public final OsmandPreference LAST_SELECTED_GPX_TRACK_FOR_NEW_POINT = new StringPreference(this, "last_selected_gpx_track_for_new_point", null).makeGlobal().cache(); // Avoid using this property, probably you need to use PoiFiltersHelper.getSelectedPoiFilters() - public final OsmandPreference SELECTED_POI_FILTER_FOR_MAP = new StringPreference("selected_poi_filter_for_map", null).makeProfile().cache(); + public final OsmandPreference SELECTED_POI_FILTER_FOR_MAP = new StringPreference(this, "selected_poi_filter_for_map", null).makeProfile().cache(); public Set getSelectedPoiFilters() { Set result = new LinkedHashSet<>(); @@ -3736,21 +2495,21 @@ public class OsmandSettings { } public final ListStringPreference POI_FILTERS_ORDER = (ListStringPreference) - new ListStringPreference("poi_filters_order", null, ",,").makeProfile().cache(); - + new ListStringPreference(this, "poi_filters_order", null, ",,").makeProfile().cache(); + public final ListStringPreference INACTIVE_POI_FILTERS = (ListStringPreference) - new ListStringPreference("inactive_poi_filters", null, ",,").makeProfile().cache(); + new ListStringPreference(this, "inactive_poi_filters", null, ",,").makeProfile().cache(); public final ContextMenuItemsPreference DRAWER_ITEMS = - (ContextMenuItemsPreference) new ContextMenuItemsPreference("drawer_items", DRAWER_ITEM_ID_SCHEME, new ContextMenuItemsSettings()) + (ContextMenuItemsPreference) new ContextMenuItemsPreference(this, "drawer_items", DRAWER_ITEM_ID_SCHEME, new ContextMenuItemsSettings()) .makeProfile().cache(); public final ContextMenuItemsPreference CONFIGURE_MAP_ITEMS = - (ContextMenuItemsPreference) new ContextMenuItemsPreference("configure_map_items", CONFIGURE_MAP_ITEM_ID_SCHEME, new ContextMenuItemsSettings()) + (ContextMenuItemsPreference) new ContextMenuItemsPreference(this, "configure_map_items", CONFIGURE_MAP_ITEM_ID_SCHEME, new ContextMenuItemsSettings()) .makeProfile().cache(); public final ContextMenuItemsPreference CONTEXT_MENU_ACTIONS_ITEMS = - (ContextMenuItemsPreference) new ContextMenuItemsPreference("context_menu_items", MAP_CONTEXT_MENU_ACTIONS, new MainContextMenuItemsSettings()) + (ContextMenuItemsPreference) new ContextMenuItemsPreference(this, "context_menu_items", MAP_CONTEXT_MENU_ACTIONS, new MainContextMenuItemsSettings()) .makeProfile().cache(); public final List CONTEXT_MENU_ITEMS_PREFERENCES = Arrays.asList(DRAWER_ITEMS, CONFIGURE_MAP_ITEMS, CONTEXT_MENU_ACTIONS_ITEMS); @@ -3758,7 +2517,7 @@ public class OsmandSettings { @Nullable public ContextMenuItemsPreference getContextMenuItemsPreference(@NonNull String id) { for (ContextMenuItemsPreference preference : CONTEXT_MENU_ITEMS_PREFERENCES) { - if (id.startsWith(preference.idScheme)) { + if (id.startsWith(preference.getIdScheme())) { return preference; } } @@ -3772,7 +2531,7 @@ public class OsmandSettings { }; // this value string is synchronized with settings_pref.xml preference name // this value could localized - public final OsmandPreference VOICE_PROVIDER = new StringPreference("voice_provider", null) { + public final OsmandPreference VOICE_PROVIDER = new StringPreference(this, "voice_provider", null) { protected String getDefaultValue() { Configuration config = ctx.getResources().getConfiguration(); @@ -3787,7 +2546,7 @@ public class OsmandSettings { // this value string is synchronized with settings_pref.xml preference name - public final CommonPreference RENDERER = new StringPreference("renderer", RendererRegistry.DEFAULT_RENDER) { + public final CommonPreference RENDERER = new StringPreference(this, "renderer", RendererRegistry.DEFAULT_RENDER) { @Override protected boolean setValue(Object prefs, String val) { @@ -3808,11 +2567,9 @@ public class OsmandSettings { RENDERER.setModeDefaultValue(ApplicationMode.SKI, RendererRegistry.WINTER_SKI_RENDER); } - Map> customRendersProps = new LinkedHashMap>(); - public CommonPreference getCustomRenderProperty(String attrName) { if (!customRendersProps.containsKey(attrName)) { - customRendersProps.put(attrName, new StringPreference(RENDERER_PREFERENCE_PREFIX + attrName, "").makeProfile()); + customRendersProps.put(attrName, new StringPreference(this, RENDERER_PREFERENCE_PREFIX + attrName, "").makeProfile()); } return customRendersProps.get(attrName); } @@ -3822,91 +2579,86 @@ public class OsmandSettings { getCustomRenderProperty("defAppMode"); } - Map> customBooleanRendersProps = new LinkedHashMap>(); - public CommonPreference getCustomRenderBooleanProperty(String attrName) { if (!customBooleanRendersProps.containsKey(attrName)) { - customBooleanRendersProps.put(attrName, new BooleanPreference(RENDERER_PREFERENCE_PREFIX + attrName, false).makeProfile()); + customBooleanRendersProps.put(attrName, new BooleanPreference(this, RENDERER_PREFERENCE_PREFIX + attrName, false).makeProfile()); } return customBooleanRendersProps.get(attrName); } - Map> customRoutingProps = new LinkedHashMap<>(); - public CommonPreference getCustomRoutingProperty(String attrName, String defValue) { if (!customRoutingProps.containsKey(attrName)) { - customRoutingProps.put(attrName, new StringPreference(ROUTING_PREFERENCE_PREFIX + attrName, defValue).makeProfile()); + customRoutingProps.put(attrName, new StringPreference(this, ROUTING_PREFERENCE_PREFIX + attrName, defValue).makeProfile()); } return customRoutingProps.get(attrName); } - Map> customBooleanRoutingProps = new LinkedHashMap<>(); public CommonPreference getCustomRoutingBooleanProperty(String attrName, boolean defaulfValue) { if (!customBooleanRoutingProps.containsKey(attrName)) { - customBooleanRoutingProps.put(attrName, new BooleanStringPreference(ROUTING_PREFERENCE_PREFIX + attrName, defaulfValue).makeProfile()); + customBooleanRoutingProps.put(attrName, new BooleanStringPreference(this, ROUTING_PREFERENCE_PREFIX + attrName, defaulfValue).makeProfile()); } return customBooleanRoutingProps.get(attrName); } - public final CommonPreference ROUTE_RECALCULATION_DISTANCE = new FloatPreference("routing_recalc_distance", 0.f).makeProfile(); - public final CommonPreference ROUTE_STRAIGHT_ANGLE = new FloatPreference("routing_straight_angle", 30.f).makeProfile(); + public final CommonPreference ROUTE_RECALCULATION_DISTANCE = new FloatPreference(this, "routing_recalc_distance", 0.f).makeProfile(); + public final CommonPreference ROUTE_STRAIGHT_ANGLE = new FloatPreference(this, "routing_straight_angle", 30.f).makeProfile(); - public final OsmandPreference USE_OSM_LIVE_FOR_ROUTING = new BooleanPreference("enable_osmc_routing", true).makeGlobal(); + public final OsmandPreference USE_OSM_LIVE_FOR_ROUTING = new BooleanPreference(this, "enable_osmc_routing", true).makeProfile(); - public final OsmandPreference USE_OSM_LIVE_FOR_PUBLIC_TRANSPORT = new BooleanPreference("enable_osmc_public_transport", false).makeGlobal(); + public final OsmandPreference USE_OSM_LIVE_FOR_PUBLIC_TRANSPORT = new BooleanPreference(this, "enable_osmc_public_transport", false).makeProfile(); - public final OsmandPreference VOICE_MUTE = new BooleanPreference("voice_mute", false).makeProfile().cache(); + public final OsmandPreference VOICE_MUTE = new BooleanPreference(this, "voice_mute", false).makeProfile().cache(); // for background service - public final OsmandPreference MAP_ACTIVITY_ENABLED = new BooleanPreference("map_activity_enabled", false).makeGlobal(); + public final OsmandPreference MAP_ACTIVITY_ENABLED = new BooleanPreference(this, "map_activity_enabled", false).makeGlobal(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference SAFE_MODE = new BooleanPreference("safe_mode", false).makeGlobal(); - public final OsmandPreference PT_SAFE_MODE = new BooleanPreference("pt_safe_mode", false).makeGlobal(); - public final OsmandPreference NATIVE_RENDERING_FAILED = new BooleanPreference("native_rendering_failed_init", false).makeGlobal(); + public final OsmandPreference SAFE_MODE = new BooleanPreference(this, "safe_mode", false).makeGlobal(); + public final OsmandPreference PT_SAFE_MODE = new BooleanPreference(this, "pt_safe_mode", false).makeProfile(); + public final OsmandPreference NATIVE_RENDERING_FAILED = new BooleanPreference(this, "native_rendering_failed_init", false).makeGlobal(); - public final OsmandPreference USE_OPENGL_RENDER = new BooleanPreference("use_opengl_render", + public final OsmandPreference USE_OPENGL_RENDER = new BooleanPreference(this, "use_opengl_render", false /*Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH*/ ).makeGlobal().cache(); - public final OsmandPreference OPENGL_RENDER_FAILED = new BooleanPreference("opengl_render_failed", false).makeGlobal().cache(); + public final OsmandPreference OPENGL_RENDER_FAILED = new BooleanPreference(this, "opengl_render_failed", false).makeGlobal().cache(); // this value string is synchronized with settings_pref.xml preference name - public final OsmandPreference CONTRIBUTION_INSTALL_APP_DATE = new StringPreference("CONTRIBUTION_INSTALL_APP_DATE", null).makeGlobal(); + public final OsmandPreference CONTRIBUTION_INSTALL_APP_DATE = new StringPreference(this, "CONTRIBUTION_INSTALL_APP_DATE", null).makeGlobal(); - public final OsmandPreference COORDINATES_FORMAT = new IntPreference("coordinates_format", PointDescription.FORMAT_DEGREES).makeProfile(); + public final OsmandPreference COORDINATES_FORMAT = new IntPreference(this, "coordinates_format", PointDescription.FORMAT_DEGREES).makeProfile(); - public final OsmandPreference FOLLOW_THE_ROUTE = new BooleanPreference("follow_to_route", false).makeGlobal(); - public final OsmandPreference FOLLOW_THE_GPX_ROUTE = new StringPreference("follow_gpx", null).makeGlobal(); - - public final OsmandPreference SELECTED_TRAVEL_BOOK = new StringPreference("selected_travel_book", "").makeGlobal(); + public final OsmandPreference FOLLOW_THE_ROUTE = new BooleanPreference(this, "follow_to_route", false).makeGlobal(); + public final OsmandPreference FOLLOW_THE_GPX_ROUTE = new StringPreference(this, "follow_gpx", null).makeGlobal(); + + public final OsmandPreference SELECTED_TRAVEL_BOOK = new StringPreference(this, "selected_travel_book", "").makeGlobal(); public final ListStringPreference DISPLAYED_TRANSPORT_SETTINGS = (ListStringPreference) - new ListStringPreference("displayed_transport_settings", null, ",").makeProfile(); - + new ListStringPreference(this, "displayed_transport_settings", null, ",").makeProfile(); + public final OsmandPreference SHOW_ARRIVAL_TIME_OTHERWISE_EXPECTED_TIME = - new BooleanPreference("show_arrival_time", true).makeProfile(); + new BooleanPreference(this, "show_arrival_time", true).makeProfile(); public final OsmandPreference SHOW_INTERMEDIATE_ARRIVAL_TIME_OTHERWISE_EXPECTED_TIME = - new BooleanPreference("show_intermediate_arrival_time", true).makeProfile(); + new BooleanPreference(this, "show_intermediate_arrival_time", true).makeProfile(); public final OsmandPreference SHOW_RELATIVE_BEARING_OTHERWISE_REGULAR_BEARING = - new BooleanPreference("show_relative_bearing", true).makeProfile(); + new BooleanPreference(this, "show_relative_bearing", true).makeProfile(); public final OsmandPreference AGPS_DATA_LAST_TIME_DOWNLOADED = - new LongPreference("agps_data_downloaded", 0).makeGlobal(); + new LongPreference(this, "agps_data_downloaded", 0).makeGlobal(); // Live Updates public final OsmandPreference IS_LIVE_UPDATES_ON = - new BooleanPreference("is_live_updates_on", false).makeGlobal(); + new BooleanPreference(this, "is_live_updates_on", false).makeGlobal(); public final OsmandPreference LIVE_UPDATES_RETRIES = - new IntPreference("live_updates_retryes", 2).makeGlobal(); + new IntPreference(this, "live_updates_retryes", 2).makeGlobal(); // UI boxes public final CommonPreference TRANSPARENT_MAP_THEME = - new BooleanPreference("transparent_map_theme", true).makeProfile(); + new BooleanPreference(this, "transparent_map_theme", true).makeProfile(); { TRANSPARENT_MAP_THEME.setModeDefaultValue(ApplicationMode.CAR, false); @@ -3915,7 +2667,7 @@ public class OsmandSettings { } public final CommonPreference SHOW_STREET_NAME = - new BooleanPreference("show_street_name", false).makeProfile(); + new BooleanPreference(this, "show_street_name", false).makeProfile(); { SHOW_STREET_NAME.setModeDefaultValue(ApplicationMode.DEFAULT, false); @@ -3934,13 +2686,13 @@ public class OsmandSettings { public static final int PARROT_EXTERNAL_DEVICE = 3; public final CommonPreference SEARCH_TAB = - new IntPreference("SEARCH_TAB", 0).makeGlobal().cache(); + new IntPreference(this, "SEARCH_TAB", 0).makeGlobal().cache(); public final CommonPreference FAVORITES_TAB = - new IntPreference("FAVORITES_TAB", 0).makeGlobal().cache(); + new IntPreference(this, "FAVORITES_TAB", 0).makeGlobal().cache(); public final CommonPreference OSMAND_THEME = - new IntPreference("osmand_theme", OSMAND_LIGHT_THEME) { + new IntPreference(this, "osmand_theme", OSMAND_LIGHT_THEME) { @Override public void readFromJson(JSONObject json, ApplicationMode appMode) throws JSONException { Integer theme = parseString(json.getString(getId())); @@ -3952,7 +2704,7 @@ public class OsmandSettings { }.makeProfile().cache(); public final OsmandPreference OPEN_ONLY_HEADER_STATE_ROUTE_CALCULATED = - new BooleanPreference("open_only_header_route_calculated", false).makeProfile(); + new BooleanPreference(this, "open_only_header_route_calculated", false).makeProfile(); public boolean isLightActionBar() { return isLightContent(); @@ -3988,29 +2740,28 @@ public class OsmandSettings { } public final CommonPreference FLUORESCENT_OVERLAYS = - new BooleanPreference("fluorescent_overlays", false).makeGlobal().cache(); - + new BooleanPreference(this, "fluorescent_overlays", false).makeGlobal().cache(); // public final OsmandPreference NUMBER_OF_FREE_DOWNLOADS_V2 = new IntPreference("free_downloads_v2", 0).makeGlobal(); - public final OsmandPreference NUMBER_OF_FREE_DOWNLOADS = new IntPreference(NUMBER_OF_FREE_DOWNLOADS_ID, 0).makeGlobal(); + public final OsmandPreference NUMBER_OF_FREE_DOWNLOADS = new IntPreference(this, NUMBER_OF_FREE_DOWNLOADS_ID, 0).makeGlobal(); // For RateUsDialog public final OsmandPreference LAST_DISPLAY_TIME = - new LongPreference("last_display_time", 0).makeGlobal().cache(); + new LongPreference(this, "last_display_time", 0).makeGlobal().cache(); public final OsmandPreference LAST_CHECKED_UPDATES = - new LongPreference("last_checked_updates", 0).makeGlobal(); + new LongPreference(this, "last_checked_updates", 0).makeGlobal(); public final OsmandPreference NUMBER_OF_APP_STARTS_ON_DISLIKE_MOMENT = - new IntPreference("number_of_app_starts_on_dislike_moment", 0).makeGlobal().cache(); + new IntPreference(this, "number_of_app_starts_on_dislike_moment", 0).makeGlobal().cache(); public final OsmandPreference RATE_US_STATE = - new EnumStringPreference<>("rate_us_state", RateUsState.INITIAL_STATE, RateUsState.values()).makeGlobal(); + new EnumStringPreference<>(this, "rate_us_state", RateUsState.INITIAL_STATE, RateUsState.values()).makeGlobal(); public final CommonPreference CUSTOM_APP_MODES_KEYS = - new StringPreference("custom_app_modes_keys", "").makeGlobal().cache(); + new StringPreference(this, "custom_app_modes_keys", "").makeGlobal().cache(); public Set getCustomAppModesKeys() { String appModesKeys = CUSTOM_APP_MODES_KEYS.get(); @@ -4022,458 +2773,13 @@ public class OsmandSettings { return res; } - public enum DayNightMode { - AUTO(R.string.daynight_mode_auto, R.drawable.ic_action_map_sunset), - DAY(R.string.daynight_mode_day, R.drawable.ic_action_map_day), - NIGHT(R.string.daynight_mode_night, R.drawable.ic_action_map_night), - SENSOR(R.string.daynight_mode_sensor, R.drawable.ic_action_map_light_sensor); - - private final int key; - @DrawableRes - private final int drawableRes; - - DayNightMode(@StringRes int key, @DrawableRes int drawableRes) { - this.key = key; - this.drawableRes = drawableRes; - } - - public String toHumanString(Context ctx) { - return ctx.getString(key); - } - - @DrawableRes - public int getIconRes() { - return drawableRes; - } - - public boolean isSensor() { - return this == SENSOR; - } - - public boolean isAuto() { - return this == AUTO; - } - - public boolean isDay() { - return this == DAY; - } - - public boolean isNight() { - return this == NIGHT; - } - - public static DayNightMode[] possibleValues(Context context) { - SensorManager mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - Sensor mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); - boolean isLightSensorEnabled = mLight != null; - if (isLightSensorEnabled) { - return DayNightMode.values(); - } else { - return new DayNightMode[]{AUTO, DAY, NIGHT}; + public void setQuickActions(HashMap quickActions, ApplicationMode mode) { + if (!QUICK_ACTION.isSetForMode(mode)) { + Boolean actionState = quickActions.get(mode.getStringKey()); + if (actionState == null) { + actionState = QUICK_ACTION.getDefaultValue(); } + setPreference(QUICK_ACTION.getId(), actionState, mode); } } - - - public enum LayerTransparencySeekbarMode { - OVERLAY(R.string.overlay_transparency), - UNDERLAY(R.string.map_transparency), - OFF(R.string.shared_string_off), - UNDEFINED(R.string.shared_string_none); - - private final int key; - - LayerTransparencySeekbarMode(int key) { - this.key = key; - } - - public String toHumanString(Context ctx) { - return ctx.getString(key); - } - } - - public enum NotesSortByMode { - BY_TYPE, - BY_DATE; - - public boolean isByType() { - return this == BY_TYPE; - } - - public boolean isByDate() { - return this == BY_DATE; - } - } - - public enum TracksSortByMode { - BY_DATE(R.string.sort_last_modified, R.drawable.ic_action_time_start), - BY_NAME_ASCENDING(R.string.sort_name_ascending, R.drawable.ic_action_sort_by_name_ascending), - BY_NAME_DESCENDING(R.string.sort_name_descending, R.drawable.ic_action_sort_by_name_descending); - - private final int iconId; - private final int nameId; - - TracksSortByMode(int nameId, int iconId) { - this.nameId = nameId; - this.iconId = iconId; - } - - public boolean isByName() { - return this == BY_NAME_ASCENDING || this == BY_NAME_DESCENDING; - } - - public boolean isByDate() { - return this == BY_DATE; - } - - @StringRes - public int getNameId() { - return nameId; - } - - @DrawableRes - public int getIconId() { - return iconId; - } - } - - public enum MapMarkersMode { - TOOLBAR(R.string.shared_string_topbar), - WIDGETS(R.string.shared_string_widgets), - NONE(R.string.shared_string_none); - - private final int key; - - MapMarkersMode(int key) { - this.key = key; - } - - public String toHumanString(Context ctx) { - return ctx.getString(key); - } - - public boolean isToolbar() { - return this == TOOLBAR; - } - - public boolean isWidgets() { - return this == WIDGETS; - } - - public boolean isNone() { - return this == NONE; - } - - public static MapMarkersMode[] possibleValues(Context context) { - return new MapMarkersMode[]{TOOLBAR, WIDGETS, NONE}; - } - } - - public enum SpeedConstants { - KILOMETERS_PER_HOUR(R.string.km_h, R.string.si_kmh, false), - MILES_PER_HOUR(R.string.mile_per_hour, R.string.si_mph, true), - METERS_PER_SECOND(R.string.m_s, R.string.si_m_s, false), - MINUTES_PER_MILE(R.string.min_mile, R.string.si_min_m, true), - MINUTES_PER_KILOMETER(R.string.min_km, R.string.si_min_km, false), - NAUTICALMILES_PER_HOUR(R.string.nm_h, R.string.si_nm_h, true); - - public final int key; - public final int descr; - public final boolean imperial; - - SpeedConstants(int key, int descr, boolean imperial) { - this.key = key; - this.descr = descr; - this.imperial = imperial; - } - - - - public String toHumanString(Context ctx) { - return ctx.getString(descr); - } - - public String toShortString(Context ctx) { - return ctx.getString(key); - } - - - } - - public enum MetricsConstants { - KILOMETERS_AND_METERS(R.string.si_km_m, "km-m"), - MILES_AND_FEET(R.string.si_mi_feet, "mi-f"), - MILES_AND_METERS(R.string.si_mi_meters, "mi-m"), - MILES_AND_YARDS(R.string.si_mi_yard, "mi-y"), - NAUTICAL_MILES(R.string.si_nm, "nm"); - - private final int key; - private final String ttsString; - - MetricsConstants(int key, String ttsString) { - this.key = key; - this.ttsString = ttsString; - } - - public String toHumanString(Context ctx) { - return ctx.getString(key); - } - - public String toTTSString() { - return ttsString; - } - - } - - public enum AngularConstants { - DEGREES(R.string.shared_string_degrees, "°"), - DEGREES360(R.string.shared_string_degrees, "°"), - MILLIRADS(R.string.shared_string_milliradians, "mil"); - - private final int key; - private final String unit; - - AngularConstants(int key, String unit) { - this.key = key; - this.unit = unit; - } - - public String toHumanString(Context ctx) { - return ctx.getString(key); - } - public String getUnitSymbol() { - return unit; - } - - } - - public enum AutoZoomMap { - FARTHEST(R.string.auto_zoom_farthest, 1f, 15.5f), - FAR(R.string.auto_zoom_far, 1.4f, 17f), - CLOSE(R.string.auto_zoom_close, 2f, 19f); - public final float coefficient; - public final int name; - public final float maxZoom; - - AutoZoomMap(int name, float coefficient, float maxZoom) { - this.name = name; - this.coefficient = coefficient; - this.maxZoom = maxZoom; - - } - } - - /** - * Class represents specific for driving region - * Signs, leftHandDriving - */ - public enum DrivingRegion { - - EUROPE_ASIA(R.string.driving_region_europe_asia, MetricsConstants.KILOMETERS_AND_METERS, false), - US(R.string.driving_region_us, MetricsConstants.MILES_AND_FEET, false), - CANADA(R.string.driving_region_canada, MetricsConstants.KILOMETERS_AND_METERS, false), - UK_AND_OTHERS(R.string.driving_region_uk, MetricsConstants.MILES_AND_METERS, true), - JAPAN(R.string.driving_region_japan, MetricsConstants.KILOMETERS_AND_METERS, true), - AUSTRALIA(R.string.driving_region_australia, MetricsConstants.KILOMETERS_AND_METERS, true); - - public final boolean leftHandDriving; - public final MetricsConstants defMetrics; - public final int name; - - DrivingRegion(int name, MetricsConstants def, boolean leftHandDriving) { - this.name = name; - defMetrics = def; - this.leftHandDriving = leftHandDriving; - } - - public boolean isAmericanTypeSigns() { - return this == OsmandSettings.DrivingRegion.AUSTRALIA || - this == OsmandSettings.DrivingRegion.US || - this == OsmandSettings.DrivingRegion.CANADA; - } - - public String getDescription(Context ctx) { - return ctx.getString(leftHandDriving ? R.string.left_side_navigation : R.string.right_side_navigation) + - ", " + - defMetrics.toHumanString(ctx).toLowerCase(); - } - } - - public enum RulerMode { - FIRST, - SECOND, - EMPTY - } - - public enum WikiArticleShowImages { - ON(R.string.shared_string_on), - OFF(R.string.shared_string_off), - WIFI(R.string.shared_string_wifi_only); - - public final int name; - - WikiArticleShowImages(int name) { - this.name = name; - } - } - - public enum TerrainMode { - HILLSHADE, - SLOPE - } - - private OsmandPreference[] generalPrefs = new OsmandPreference[]{ - EXTERNAL_INPUT_DEVICE, - CENTER_POSITION_ON_MAP, - ROTATE_MAP, - MAP_SCREEN_ORIENTATION, - LIVE_MONITORING_URL, - LIVE_MONITORING_MAX_INTERVAL_TO_SEND, - LIVE_MONITORING_INTERVAL, - LIVE_MONITORING, - SHOW_TRIP_REC_NOTIFICATION, - AUTO_SPLIT_RECORDING, - SAVE_TRACK_MIN_SPEED, - SAVE_TRACK_PRECISION, - SAVE_TRACK_MIN_DISTANCE, - SAVE_TRACK_INTERVAL, - TRACK_STORAGE_DIRECTORY, - SAVE_HEADING_TO_GPX, - DISABLE_RECORDING_ONCE_APP_KILLED, - SAVE_TRACK_TO_GPX, - SAVE_GLOBAL_TRACK_REMEMBER, - SAVE_GLOBAL_TRACK_INTERVAL, - MAP_EMPTY_STATE_ALLOWED, - DO_NOT_USE_ANIMATIONS, - USE_KALMAN_FILTER_FOR_COMPASS, - USE_MAGNETIC_FIELD_SENSOR_COMPASS, - USE_TRACKBALL_FOR_MOVEMENTS, - SPEED_SYSTEM, - ANGULAR_UNITS, - METRIC_SYSTEM, - DRIVING_REGION, - DRIVING_REGION_AUTOMATIC - }; - - String[] - appModeBeanPrefsIds = new String[] { - ICON_COLOR.getId(), - ICON_RES_NAME.getId(), - PARENT_APP_MODE.getId(), - ROUTING_PROFILE.getId(), - ROUTE_SERVICE.getId(), - USER_PROFILE_NAME.getId(), - LOCATION_ICON.getId(), - NAVIGATION_ICON.getId(), - APP_MODE_ORDER.getId() - }; - - public class PreferencesDataStore extends PreferenceDataStore { - - private ApplicationMode appMode; - - public PreferencesDataStore(@NonNull ApplicationMode appMode) { - this.appMode = appMode; - } - - @Override - public void putString(String key, @Nullable String value) { - setPreference(key, value, appMode); - } - - @Override - public void putStringSet(String key, @Nullable Set values) { - setPreference(key, values, appMode); - } - - @Override - public void putInt(String key, int value) { - setPreference(key, value, appMode); - } - - @Override - public void putLong(String key, long value) { - setPreference(key, value, appMode); - } - - @Override - public void putFloat(String key, float value) { - setPreference(key, value, appMode); - } - - @Override - public void putBoolean(String key, boolean value) { - setPreference(key, value, appMode); - } - - public void putValue(String key, Object value) { - setPreference(key, value, appMode); - } - - @Nullable - @Override - public String getString(String key, @Nullable String defValue) { - OsmandPreference preference = getPreference(key); - if (preference instanceof StringPreference) { - return ((StringPreference) preference).getModeValue(appMode); - } else { - Object value = preference.getModeValue(appMode); - if (value != null) { - return value.toString(); - } - } - return defValue; - } - - @Nullable - @Override - public Set getStringSet(String key, @Nullable Set defValues) { - return super.getStringSet(key, defValues); - } - - @Override - public int getInt(String key, int defValue) { - OsmandPreference preference = getPreference(key); - if (preference instanceof IntPreference) { - return ((IntPreference) preference).getModeValue(appMode); - } - return defValue; - } - - @Override - public long getLong(String key, long defValue) { - OsmandPreference preference = getPreference(key); - if (preference instanceof LongPreference) { - return ((LongPreference) preference).getModeValue(appMode); - } - return defValue; - } - - @Override - public float getFloat(String key, float defValue) { - OsmandPreference preference = getPreference(key); - if (preference instanceof FloatPreference) { - return ((FloatPreference) preference).getModeValue(appMode); - } - return defValue; - } - - @Override - public boolean getBoolean(String key, boolean defValue) { - OsmandPreference preference = getPreference(key); - if (preference instanceof BooleanPreference) { - return ((BooleanPreference) preference).getModeValue(appMode); - } - return defValue; - } - - @Nullable - public Object getValue(String key, Object defValue) { - OsmandPreference preference = getPreference(key); - if (preference != null) { - return preference.getModeValue(appMode); - } - return defValue; - } - } -} +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/PreferenceWithListener.java b/OsmAnd/src/net/osmand/plus/settings/backend/PreferenceWithListener.java new file mode 100644 index 0000000000..5ffb4466ba --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/PreferenceWithListener.java @@ -0,0 +1,49 @@ +package net.osmand.plus.settings.backend; + +import net.osmand.StateChangedListener; + +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +abstract class PreferenceWithListener implements OsmandPreference { + private List>> l = null; + + @Override + public synchronized void addListener(StateChangedListener listener) { + if (l == null) { + l = new LinkedList>>(); + } + if (!l.contains(new WeakReference>(listener))) { + l.add(new WeakReference>(listener)); + } + } + + public synchronized void fireEvent(T value) { + if (l != null) { + Iterator>> it = l.iterator(); + while (it.hasNext()) { + StateChangedListener t = it.next().get(); + if (t == null) { + it.remove(); + } else { + t.stateChanged(value); + } + } + } + } + + @Override + public synchronized void removeListener(StateChangedListener listener) { + if (l != null) { + Iterator>> it = l.iterator(); + while (it.hasNext()) { + StateChangedListener t = it.next().get(); + if (t == listener) { + it.remove(); + } + } + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/SettingsHelper.java b/OsmAnd/src/net/osmand/plus/settings/backend/SettingsHelper.java index 2b5b4282d4..60a411121a 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/SettingsHelper.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/SettingsHelper.java @@ -20,8 +20,6 @@ import net.osmand.map.TileSourceManager.TileSourceTemplate; import net.osmand.map.WorldRegion; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; -import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; -import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBuilder; import net.osmand.plus.CustomOsmandPlugin; import net.osmand.plus.CustomOsmandPlugin.SuggestedDownloadItem; import net.osmand.plus.CustomRegion; @@ -29,12 +27,13 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.SQLiteTileSource; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; import net.osmand.plus.helpers.AvoidSpecificRoads; import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickActionRegistry; +import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; +import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBuilder; import net.osmand.router.GeneralRouter; import net.osmand.util.Algorithms; @@ -100,6 +99,8 @@ public class SettingsHelper { public static final int VERSION = 1; + public static final String SETTINGS_TYPE_LIST_KEY = "settings_type_list_key"; + public static final String REPLACE_KEY = "replace"; public static final String SETTINGS_LATEST_CHANGES_KEY = "settings_latest_changes"; public static final String SETTINGS_VERSION_KEY = "settings_version"; @@ -945,7 +946,7 @@ public class SettingsHelper { @Override protected void init() { super.init(); - appModeBeanPrefsIds = new HashSet<>(Arrays.asList(app.getSettings().appModeBeanPrefsIds)); + appModeBeanPrefsIds = new HashSet<>(Arrays.asList(getAppModeBeanPrefsIds())); } @NonNull @@ -1186,6 +1187,21 @@ public class SettingsHelper { } }; } + + private String[] getAppModeBeanPrefsIds() { + OsmandSettings settings = app.getSettings(); + return new String[] { + settings.ICON_COLOR.getId(), + settings.ICON_RES_NAME.getId(), + settings.PARENT_APP_MODE.getId(), + settings.ROUTING_PROFILE.getId(), + settings.ROUTE_SERVICE.getId(), + settings.USER_PROFILE_NAME.getId(), + settings.LOCATION_ICON.getId(), + settings.NAVIGATION_ICON.getId(), + settings.APP_MODE_ORDER.getId() + }; + } } public abstract static class StreamSettingsItemReader extends SettingsItemReader { @@ -2928,4 +2944,113 @@ public class SettingsHelper { CHECK_DUPLICATES, IMPORT } + + public List getFilteredSettingsItems(Map> additionalData, + List settingsTypes) { + List settingsItems = new ArrayList<>(); + for (ExportSettingsType settingsType : settingsTypes) { + List settingsDataObjects = additionalData.get(settingsType); + if (settingsDataObjects != null) { + for (Object object : settingsDataObjects) { + if (object instanceof ApplicationModeBean) { + settingsItems.add(new ProfileSettingsItem(app, null, (ApplicationModeBean) object)); + } + } + settingsItems.addAll(prepareAdditionalSettingsItems(new ArrayList<>(settingsDataObjects))); + } + } + return settingsItems; + } + + public Map> getAdditionalData() { + Map> dataList = new HashMap<>(); + + QuickActionRegistry registry = app.getQuickActionRegistry(); + List actionsList = registry.getQuickActions(); + if (!actionsList.isEmpty()) { + dataList.put(ExportSettingsType.QUICK_ACTIONS, actionsList); + } + + List poiList = app.getPoiFilters().getUserDefinedPoiFilters(false); + if (!poiList.isEmpty()) { + dataList.put(ExportSettingsType.POI_TYPES, poiList); + } + + List iTileSources = new ArrayList<>(); + Set tileSourceNames = app.getSettings().getTileSourceEntries(true).keySet(); + for (String name : tileSourceNames) { + File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + name); + if (f != null) { + ITileSource template; + if (f.getName().endsWith(SQLiteTileSource.EXT)) { + template = new SQLiteTileSource(app, f, TileSourceManager.getKnownSourceTemplates()); + } else { + template = TileSourceManager.createTileSourceTemplate(f); + } + if (template.getUrlTemplate() != null) { + iTileSources.add(template); + } + } + } + if (!iTileSources.isEmpty()) { + dataList.put(ExportSettingsType.MAP_SOURCES, iTileSources); + } + + Map externalRenderers = app.getRendererRegistry().getExternalRenderers(); + if (!externalRenderers.isEmpty()) { + dataList.put(ExportSettingsType.CUSTOM_RENDER_STYLE, new ArrayList<>(externalRenderers.values())); + } + + File routingProfilesFolder = app.getAppPath(IndexConstants.ROUTING_PROFILES_DIR); + if (routingProfilesFolder.exists() && routingProfilesFolder.isDirectory()) { + File[] fl = routingProfilesFolder.listFiles(); + if (fl != null && fl.length > 0) { + dataList.put(ExportSettingsType.CUSTOM_ROUTING, Arrays.asList(fl)); + } + } + + Map impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads(); + if (!impassableRoads.isEmpty()) { + dataList.put(ExportSettingsType.AVOID_ROADS, new ArrayList<>(impassableRoads.values())); + } + return dataList; + } + + public List prepareAdditionalSettingsItems(List data) { + List settingsItems = new ArrayList<>(); + List quickActions = new ArrayList<>(); + List poiUIFilters = new ArrayList<>(); + List tileSourceTemplates = new ArrayList<>(); + List avoidRoads = new ArrayList<>(); + for (Object object : data) { + if (object instanceof QuickAction) { + quickActions.add((QuickAction) object); + } else if (object instanceof PoiUIFilter) { + poiUIFilters.add((PoiUIFilter) object); + } else if (object instanceof TileSourceTemplate || object instanceof SQLiteTileSource) { + tileSourceTemplates.add((ITileSource) object); + } else if (object instanceof File) { + try { + settingsItems.add(new FileSettingsItem(app, (File) object)); + } catch (IllegalArgumentException e) { + LOG.warn("Trying to export unsuported file type", e); + } + } else if (object instanceof AvoidRoadInfo) { + avoidRoads.add((AvoidRoadInfo) object); + } + } + if (!quickActions.isEmpty()) { + settingsItems.add(new QuickActionsSettingsItem(app, quickActions)); + } + if (!poiUIFilters.isEmpty()) { + settingsItems.add(new PoiUiFiltersSettingsItem(app, poiUIFilters)); + } + if (!tileSourceTemplates.isEmpty()) { + settingsItems.add(new MapSourcesSettingsItem(app, tileSourceTemplates)); + } + if (!avoidRoads.isEmpty()) { + settingsItems.add(new AvoidRoadsSettingsItem(app, avoidRoads)); + } + return settingsItems; + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/SettingsMapPointsStorage.java b/OsmAnd/src/net/osmand/plus/settings/backend/SettingsMapPointsStorage.java new file mode 100644 index 0000000000..07307f05f2 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/SettingsMapPointsStorage.java @@ -0,0 +1,150 @@ +package net.osmand.plus.settings.backend; + +import net.osmand.data.LatLon; +import net.osmand.data.PointDescription; +import net.osmand.plus.api.SettingsAPI; +import net.osmand.plus.helpers.SearchHistoryHelper; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.StringTokenizer; + +abstract class SettingsMapPointsStorage { + + private OsmandSettings osmandSettings; + protected String pointsKey; + protected String descriptionsKey; + + public SettingsMapPointsStorage(OsmandSettings osmandSettings) { + this.osmandSettings = osmandSettings; + } + + protected SettingsAPI getSettingsAPI() { + return osmandSettings.getSettingsAPI(); + } + + protected OsmandSettings getOsmandSettings() { + return osmandSettings; + } + + public List getPointDescriptions(int sz) { + List list = new ArrayList<>(); + String ip = getSettingsAPI().getString(osmandSettings.getGlobalPreferences(), descriptionsKey, ""); + if (ip.trim().length() > 0) { + list.addAll(Arrays.asList(ip.split("--"))); + } + while (list.size() > sz) { + list.remove(list.size() - 1); + } + while (list.size() < sz) { + list.add(""); + } + return list; + } + + public List getPoints() { + List list = new ArrayList<>(); + String ip = getSettingsAPI().getString(osmandSettings.getGlobalPreferences(), pointsKey, ""); + if (ip.trim().length() > 0) { + StringTokenizer tok = new StringTokenizer(ip, ","); + while (tok.hasMoreTokens()) { + String lat = tok.nextToken(); + if (!tok.hasMoreTokens()) { + break; + } + String lon = tok.nextToken(); + list.add(new LatLon(Float.parseFloat(lat), Float.parseFloat(lon))); + } + } + return list; + } + + public boolean insertPoint(double latitude, double longitude, PointDescription historyDescription, int index) { + List ps = getPoints(); + List ds = getPointDescriptions(ps.size()); + ps.add(index, new LatLon(latitude, longitude)); + ds.add(index, PointDescription.serializeToString(historyDescription)); + if (historyDescription != null && !historyDescription.isSearchingAddress(osmandSettings.getContext())) { + SearchHistoryHelper.getInstance(osmandSettings.getContext()).addNewItemToHistory(latitude, longitude, historyDescription); + } + return savePoints(ps, ds); + } + + public boolean updatePoint(double latitude, double longitude, PointDescription historyDescription) { + List ps = getPoints(); + List ds = getPointDescriptions(ps.size()); + int i = ps.indexOf(new LatLon(latitude, longitude)); + if (i != -1) { + ds.set(i, PointDescription.serializeToString(historyDescription)); + if (historyDescription != null && !historyDescription.isSearchingAddress(osmandSettings.getContext())) { + SearchHistoryHelper.getInstance(osmandSettings.getContext()).addNewItemToHistory(latitude, longitude, historyDescription); + } + return savePoints(ps, ds); + } else { + return false; + } + } + + public boolean deletePoint(int index) { + List ps = getPoints(); + List ds = getPointDescriptions(ps.size()); + if (index < ps.size()) { + ps.remove(index); + ds.remove(index); + return savePoints(ps, ds); + } else { + return false; + } + } + + public boolean deletePoint(LatLon latLon) { + List ps = getPoints(); + List ds = getPointDescriptions(ps.size()); + int index = ps.indexOf(latLon); + if (index != -1) { + ps.remove(index); + ds.remove(index); + return savePoints(ps, ds); + } else { + return false; + } + } + + public boolean savePoints(List ps, List ds) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < ps.size(); i++) { + if (i > 0) { + sb.append(","); + } + sb.append(((float) ps.get(i).getLatitude() + "")).append(",").append(((float) ps.get(i).getLongitude() + "")); + } + StringBuilder tb = new StringBuilder(); + for (int i = 0; i < ds.size(); i++) { + if (i > 0) { + tb.append("--"); + } + if (ds.get(i) == null) { + tb.append(""); + } else { + tb.append(ds.get(i)); + } + } + return getSettingsAPI().edit(osmandSettings.getGlobalPreferences()) + .putString(pointsKey, sb.toString()) + .putString(descriptionsKey, tb.toString()) + .commit(); + } + + public boolean movePoint(LatLon latLonEx, LatLon latLonNew) { + List ps = getPoints(); + List ds = getPointDescriptions(ps.size()); + int i = ps.indexOf(latLonEx); + if (i != -1) { + ps.set(i, latLonNew); + return savePoints(ps, ds); + } else { + return false; + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/StringPreference.java b/OsmAnd/src/net/osmand/plus/settings/backend/StringPreference.java new file mode 100644 index 0000000000..1bbb6d4e94 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/StringPreference.java @@ -0,0 +1,23 @@ +package net.osmand.plus.settings.backend; + +public class StringPreference extends CommonPreference { + + StringPreference(OsmandSettings settings, String id, String defaultValue) { + super(settings, id, defaultValue); + } + + @Override + protected String getValue(Object prefs, String defaultValue) { + return getSettingsAPI().getString(prefs, getId(), defaultValue); + } + + @Override + protected boolean setValue(Object prefs, String val) { + return getSettingsAPI().edit(prefs).putString(getId(), (val != null) ? val.trim() : val).commit(); + } + + @Override + public String parseString(String s) { + return s; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java index c988dbdf68..f34d6938fb 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java @@ -16,9 +16,8 @@ import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.BooleanPreference; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.BooleanPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; @@ -57,7 +56,7 @@ public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet { String title = switchPreference.getTitle().toString(); items.add(new TitleItem(title)); - final OsmandSettings.BooleanPreference pref = (BooleanPreference) preference; + final BooleanPreference pref = (BooleanPreference) preference; CharSequence summaryOn = switchPreference.getSummaryOn(); CharSequence summaryOff = switchPreference.getSummaryOff(); final String on = summaryOn == null || summaryOn.toString().equals("") diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ChangeDataStorageBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ChangeDataStorageBottomSheet.java index 334cc42aa8..e61faafe16 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ChangeDataStorageBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ChangeDataStorageBottomSheet.java @@ -11,8 +11,8 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import net.osmand.FileUtils; import net.osmand.PlatformUtil; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; @@ -63,7 +63,7 @@ public class ChangeDataStorageBottomSheet extends BasePreferenceBottomSheet { CharSequence desc = null; File currentStorageFile = new File(currentDirectory.getDirectory()); - if ((!OsmandSettings.isWritable(currentStorageFile))) { + if ((!FileUtils.isWritable(currentStorageFile))) { desc = String.format(getString(R.string.android_19_location_disabled), currentStorageFile.getAbsoluteFile()); } else { String from = currentDirectory.getKey().equals(MANUALLY_SPECIFIED) ? currentDirectory.getDirectory() : currentDirectory.getTitle(); diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java index 4c658406ce..d2679564a8 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java @@ -14,9 +14,11 @@ import androidx.fragment.app.FragmentManager; import com.google.android.material.slider.Slider; import net.osmand.AndroidUtils; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -43,7 +45,7 @@ public class RecalculateRouteInDeviationBottomSheet extends BooleanPreferenceBot private OsmandApplication app; private OsmandSettings settings; private ApplicationMode appMode; - private OsmandSettings.CommonPreference preference; + private CommonPreference preference; private Slider slider; private TextView tvSliderTitle; @@ -75,8 +77,8 @@ public class RecalculateRouteInDeviationBottomSheet extends BooleanPreferenceBot int contentPaddingSmall = app.getResources().getDimensionPixelSize(R.dimen.content_padding_small); int contentPadding = app.getResources().getDimensionPixelSize(R.dimen.content_padding); - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); - if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS) { + MetricsConstants mc = settings.METRIC_SYSTEM.get(); + if (mc == MetricsConstants.KILOMETERS_AND_METERS) { entryValues = new Float[]{10.f, 20.0f, 30.0f, 50.0f, 100.0f, 200.0f, 500.0f, 1000.0f, 1500.0f}; } else { entryValues = new Float[]{9.1f, 18.3f, 30.5f, 45.7f, 91.5f, 183.0f, 482.0f, 965.0f, 1609.0f}; diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index e416d2c32e..86940e7d7a 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -55,8 +55,9 @@ import net.osmand.PlatformUtil; import net.osmand.access.AccessibilitySettingsFragment; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -834,11 +835,11 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl return icon; } - public SwitchPreferenceCompat createSwitchPreference(OsmandSettings.OsmandPreference b, int title, int summary, int layoutId) { + public SwitchPreferenceCompat createSwitchPreference(OsmandPreference b, int title, int summary, int layoutId) { return createSwitchPreference(b, getString(title), getString(summary), layoutId); } - public SwitchPreferenceCompat createSwitchPreference(OsmandSettings.OsmandPreference b, String title, String summary, int layoutId) { + public SwitchPreferenceCompat createSwitchPreference(OsmandPreference b, String title, String summary, int layoutId) { SwitchPreferenceCompat p = new SwitchPreferenceCompat(getContext()); p.setTitle(title); p.setKey(b.getId()); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java index 44fb4bf7f6..ee8e11b970 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java @@ -27,6 +27,9 @@ import net.osmand.PlatformUtil; import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.ContextMenuItemsPreference; +import net.osmand.plus.settings.backend.ContextMenuItemsSettings; +import net.osmand.plus.settings.backend.MainContextMenuItemsSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -51,6 +54,8 @@ import java.util.HashMap; import java.util.List; import static net.osmand.aidlapi.OsmAndCustomizationConstants.APP_PROFILES_ID; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_CONFIGURE_PROFILE_ID; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SWITCH_PROFILE_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_MORE_ID; import static net.osmand.plus.settings.fragments.RearrangeMenuItemsAdapter.AdapterItemType.BUTTON; import static net.osmand.plus.settings.fragments.RearrangeMenuItemsAdapter.AdapterItemType.DESCRIPTION; @@ -169,9 +174,9 @@ public class ConfigureMenuItemsFragment extends BaseOsmAndFragment private void initMainActionsIds(ApplicationMode appMode) { List defItems = getCustomizableDefaultItems(contextMenuAdapter.getDefaultItems()); - OsmandSettings.ContextMenuItemsSettings pref = getSettingForScreen(app, screenType).getModeValue(appMode); - if (pref instanceof OsmandSettings.MainContextMenuItemsSettings) { - mainActionItems = new ArrayList<>(((OsmandSettings.MainContextMenuItemsSettings) pref).getMainIds()); + ContextMenuItemsSettings pref = getSettingForScreen(app, screenType).getModeValue(appMode); + if (pref instanceof MainContextMenuItemsSettings) { + mainActionItems = new ArrayList<>(((MainContextMenuItemsSettings) pref).getMainIds()); if (mainActionItems.isEmpty()) { for (int i = 0; i < MAIN_BUTTONS_QUANTITY && i < defItems.size(); i++) { mainActionItems.add(defItems.get(i).getId()); @@ -183,7 +188,9 @@ public class ConfigureMenuItemsFragment extends BaseOsmAndFragment public static List getCustomizableDefaultItems(List defItems) { List items = new ArrayList<>(); for (ContextMenuItem item : defItems) { - if (!APP_PROFILES_ID.equals(item.getId())) { + if (!APP_PROFILES_ID.equals(item.getId()) + && !DRAWER_CONFIGURE_PROFILE_ID.equals(item.getId()) + && !DRAWER_SWITCH_PROFILE_ID.equals(item.getId())) { items.add(item); } } @@ -261,11 +268,11 @@ public class ConfigureMenuItemsFragment extends BaseOsmAndFragment } } FragmentManager fm = getFragmentManager(); - final OsmandSettings.ContextMenuItemsSettings prefToSave; + final ContextMenuItemsSettings prefToSave; if (screenType == ScreenType.CONTEXT_MENU_ACTIONS) { - prefToSave = new OsmandSettings.MainContextMenuItemsSettings(mainActionItems, hiddenMenuItems, ids); + prefToSave = new MainContextMenuItemsSettings(mainActionItems, hiddenMenuItems, ids); } else { - prefToSave = new OsmandSettings.ContextMenuItemsSettings(hiddenMenuItems, ids); + prefToSave = new ContextMenuItemsSettings(hiddenMenuItems, ids); } if (fm != null) { ChangeGeneralProfilesPrefBottomSheet.showInstance(fm, @@ -529,7 +536,7 @@ public class ConfigureMenuItemsFragment extends BaseOsmAndFragment } } - public static OsmandSettings.ContextMenuItemsPreference getSettingForScreen(OsmandApplication app, ScreenType screenType) throws IllegalArgumentException { + public static ContextMenuItemsPreference getSettingForScreen(OsmandApplication app, ScreenType screenType) throws IllegalArgumentException { switch (screenType) { case DRAWER: return app.getSettings().DRAWER_ITEMS; diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageFragment.java index 93719a83ab..842b53e2fa 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/DataStorageFragment.java @@ -23,6 +23,7 @@ import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; import net.osmand.AndroidUtils; +import net.osmand.FileUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.ProgressImplementation; @@ -408,7 +409,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto String newDirectory = newStorageDirectory.getDirectory(); int type = newStorageDirectory.getType(); File newDirectoryFile = new File(newDirectory); - boolean wr = OsmandSettings.isWritable(newDirectoryFile); + boolean wr = FileUtils.isWritable(newDirectoryFile); if (wr) { app.setExternalStorageDirectory(type, newDirectory); reloadData(); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportImportSettingsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportImportSettingsAdapter.java index 18e8900203..01199ccff2 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportImportSettingsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportImportSettingsAdapter.java @@ -16,6 +16,7 @@ import net.osmand.AndroidUtils; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.ITileSource; +import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; @@ -50,8 +51,8 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter { private OsmandApplication app; private UiUtilities uiUtilities; private List data; - private Map> itemsMap; - private List itemsTypes; + private Map> itemsMap; + private List itemsTypes; private boolean nightMode; private boolean importState; private int activeColorRes; @@ -82,7 +83,7 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter { } boolean isLastGroup = groupPosition == getGroupCount() - 1; - final Type type = itemsTypes.get(groupPosition); + final ExportSettingsType type = itemsTypes.get(groupPosition); TextView titleTv = group.findViewById(R.id.title_tv); TextView subTextTv = group.findViewById(R.id.sub_text_tv); @@ -146,7 +147,7 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter { boolean isLastGroup = groupPosition == getGroupCount() - 1; boolean itemSelected = data.contains(currentItem); - final Type type = itemsTypes.get(groupPosition); + final ExportSettingsType type = itemsTypes.get(groupPosition); TextView title = child.findViewById(R.id.title_tv); TextView subText = child.findViewById(R.id.sub_title_tv); @@ -299,7 +300,7 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter { return app.getString(R.string.n_items_of_z, String.valueOf(amount), String.valueOf(listItems.size())); } - private int getGroupTitle(Type type) { + private int getGroupTitle(ExportSettingsType type) { switch (type) { case PROFILE: return R.string.shared_string_profiles; @@ -320,15 +321,15 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter { } } - private void setupIcon(ImageView icon, int iconRes, boolean itemSelected) { - if (itemSelected) { - icon.setImageDrawable(uiUtilities.getIcon(iconRes, activeColorRes)); - } else { - icon.setImageDrawable(uiUtilities.getIcon(iconRes, nightMode)); - } - } + private void setupIcon(ImageView icon, int iconRes, boolean itemSelected) { + if (itemSelected) { + icon.setImageDrawable(uiUtilities.getIcon(iconRes, activeColorRes)); + } else { + icon.setImageDrawable(uiUtilities.getIcon(iconRes, nightMode)); + } + } - public void updateSettingsList(Map> itemsMap) { + public void updateSettingsList(Map> itemsMap) { this.itemsMap = itemsMap; this.itemsTypes = new ArrayList<>(itemsMap.keySet()); Collections.sort(itemsTypes); @@ -354,14 +355,4 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter { List getData() { return this.data; } - - public enum Type { - PROFILE, - QUICK_ACTIONS, - POI_TYPES, - MAP_SOURCES, - CUSTOM_RENDER_STYLE, - CUSTOM_ROUTING, - AVOID_ROADS - } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportProfileBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportProfileBottomSheet.java index a09a8ba435..c95f7d578c 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportProfileBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportProfileBottomSheet.java @@ -21,45 +21,30 @@ import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; +import net.osmand.FileUtils; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; -import net.osmand.data.LatLon; -import net.osmand.map.ITileSource; -import net.osmand.map.TileSourceManager; -import net.osmand.map.TileSourceManager.TileSourceTemplate; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.SQLiteTileSource; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; -import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; -import net.osmand.plus.poi.PoiUIFilter; -import net.osmand.plus.quickaction.QuickAction; -import net.osmand.plus.quickaction.QuickActionRegistry; +import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.SettingsHelper; -import net.osmand.plus.settings.backend.SettingsHelper.AvoidRoadsSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.FileSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.MapSourcesSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.PoiUiFiltersSettingsItem; import net.osmand.plus.settings.backend.SettingsHelper.ProfileSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.QuickActionsSettingsItem; import net.osmand.plus.settings.backend.SettingsHelper.SettingsItem; import net.osmand.plus.settings.bottomsheets.BasePreferenceBottomSheet; -import net.osmand.plus.settings.fragments.ExportImportSettingsAdapter.Type; import org.apache.commons.logging.Log; import java.io.File; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { @@ -72,7 +57,7 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { private OsmandApplication app; private ApplicationMode profile; - private Map> dataList = new HashMap<>(); + private Map> dataList = new HashMap<>(); private ExportImportSettingsAdapter adapter; private SettingsHelper.SettingsExportListener exportListener; @@ -86,7 +71,7 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { super.onCreate(savedInstanceState); app = requiredMyApplication(); profile = getAppMode(); - dataList = getAdditionalData(); + dataList = app.getSettingsHelper().getAdditionalData(); if (savedInstanceState != null) { includeAdditionalData = savedInstanceState.getBoolean(INCLUDE_ADDITIONAL_DATA_KEY); exportingProfile = savedInstanceState.getBoolean(EXPORTING_PROFILE_KEY); @@ -145,7 +130,7 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { topSwitchDivider.setVisibility(includeAdditionalData ? View.VISIBLE : View.GONE); bottomSwitchDivider.setVisibility(includeAdditionalData ? View.VISIBLE : View.GONE); if (includeAdditionalData) { - adapter.updateSettingsList(getAdditionalData()); + adapter.updateSettingsList(app.getSettingsHelper().getAdditionalData()); adapter.selectAll(true); } else { adapter.selectAll(false); @@ -223,104 +208,11 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { } } - private Map> getAdditionalData() { - Map> dataList = new HashMap<>(); - - - QuickActionRegistry registry = app.getQuickActionRegistry(); - List actionsList = registry.getQuickActions(); - if (!actionsList.isEmpty()) { - dataList.put(Type.QUICK_ACTIONS, actionsList); - } - - List poiList = app.getPoiFilters().getUserDefinedPoiFilters(false); - if (!poiList.isEmpty()) { - dataList.put(Type.POI_TYPES, poiList); - } - - List iTileSources = new ArrayList<>(); - Set tileSourceNames = app.getSettings().getTileSourceEntries(true).keySet(); - for (String name : tileSourceNames) { - File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + name); - if (f != null) { - ITileSource template; - if (f.getName().endsWith(SQLiteTileSource.EXT)) { - template = new SQLiteTileSource(app, f, TileSourceManager.getKnownSourceTemplates()); - } else { - template = TileSourceManager.createTileSourceTemplate(f); - } - if (template.getUrlTemplate() != null) { - iTileSources.add(template); - } - } - } - if (!iTileSources.isEmpty()) { - dataList.put(Type.MAP_SOURCES, iTileSources); - } - - Map externalRenderers = app.getRendererRegistry().getExternalRenderers(); - if (!externalRenderers.isEmpty()) { - dataList.put(Type.CUSTOM_RENDER_STYLE, new ArrayList<>(externalRenderers.values())); - } - - File routingProfilesFolder = app.getAppPath(IndexConstants.ROUTING_PROFILES_DIR); - if (routingProfilesFolder.exists() && routingProfilesFolder.isDirectory()) { - File[] fl = routingProfilesFolder.listFiles(); - if (fl != null && fl.length > 0) { - dataList.put(Type.CUSTOM_ROUTING, Arrays.asList(fl)); - } - } - - Map impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads(); - if (!impassableRoads.isEmpty()) { - dataList.put(Type.AVOID_ROADS, new ArrayList<>(impassableRoads.values())); - } - return dataList; - } - private List prepareSettingsItemsForExport() { List settingsItems = new ArrayList<>(); settingsItems.add(new ProfileSettingsItem(app, profile)); if (includeAdditionalData) { - settingsItems.addAll(prepareAdditionalSettingsItems()); - } - return settingsItems; - } - - private List prepareAdditionalSettingsItems() { - List settingsItems = new ArrayList<>(); - List quickActions = new ArrayList<>(); - List poiUIFilters = new ArrayList<>(); - List tileSourceTemplates = new ArrayList<>(); - List avoidRoads = new ArrayList<>(); - for (Object object : adapter.getData()) { - if (object instanceof QuickAction) { - quickActions.add((QuickAction) object); - } else if (object instanceof PoiUIFilter) { - poiUIFilters.add((PoiUIFilter) object); - } else if (object instanceof TileSourceTemplate || object instanceof SQLiteTileSource) { - tileSourceTemplates.add((ITileSource) object); - } else if (object instanceof File) { - try { - settingsItems.add(new FileSettingsItem(app, (File) object)); - } catch (IllegalArgumentException e) { - LOG.warn("Trying to export unsuported file type", e); - } - } else if (object instanceof AvoidRoadInfo) { - avoidRoads.add((AvoidRoadInfo) object); - } - } - if (!quickActions.isEmpty()) { - settingsItems.add(new QuickActionsSettingsItem(app, quickActions)); - } - if (!poiUIFilters.isEmpty()) { - settingsItems.add(new PoiUiFiltersSettingsItem(app, poiUIFilters)); - } - if (!tileSourceTemplates.isEmpty()) { - settingsItems.add(new MapSourcesSettingsItem(app, tileSourceTemplates)); - } - if (!avoidRoads.isEmpty()) { - settingsItems.add(new AvoidRoadsSettingsItem(app, avoidRoads)); + settingsItems.addAll(app.getSettingsHelper().prepareAdditionalSettingsItems(adapter.getData())); } return settingsItems; } @@ -329,7 +221,7 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { if (app != null) { exportingProfile = true; showExportProgressDialog(); - File tempDir = getTempDir(); + File tempDir = FileUtils.getTempDir(app); String fileName = profile.toHumanString(); app.getSettingsHelper().exportSettings(tempDir, fileName, getSettingsExportListener(), prepareSettingsItemsForExport(), true); } @@ -391,19 +283,11 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { } private File getExportFile() { - File tempDir = getTempDir(); + File tempDir = FileUtils.getTempDir(app); String fileName = profile.toHumanString(); return new File(tempDir, fileName + IndexConstants.OSMAND_SETTINGS_FILE_EXT); } - private File getTempDir() { - File tempDir = app.getAppPath(IndexConstants.TEMP_DIR); - if (!tempDir.exists()) { - tempDir.mkdirs(); - } - return tempDir; - } - private void shareProfile(@NonNull File file, @NonNull ApplicationMode profile) { try { final Intent sendIntent = new Intent(); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java index e870fed149..3a81548285 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java @@ -22,9 +22,12 @@ import androidx.preference.PreferenceViewHolder; import androidx.preference.SwitchPreferenceCompat; import net.osmand.data.PointDescription; +import net.osmand.plus.helpers.enums.AngularConstants; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.helpers.enums.SpeedConstants; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.DrivingRegion; +import net.osmand.plus.helpers.enums.DrivingRegion; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.Version; @@ -123,7 +126,7 @@ public class GeneralProfileSettingsFragment extends BaseSettingsFragment impleme if (settings.isSystemDefaultThemeUsedForMode(mode)) { iconId = R.drawable.ic_action_android; } else { - iconId = settings.isLightContent() ? R.drawable.ic_action_sun : R.drawable.ic_action_moon; + iconId = settings.isLightContentForMode(mode) ? R.drawable.ic_action_sun : R.drawable.ic_action_moon; } return getActiveIcon(iconId); } @@ -186,7 +189,7 @@ public class GeneralProfileSettingsFragment extends BaseSettingsFragment impleme } private void setupUnitsOfLengthPref() { - OsmandSettings.MetricsConstants[] metricsConstants = OsmandSettings.MetricsConstants.values(); + MetricsConstants[] metricsConstants = MetricsConstants.values(); String[] entries = new String[metricsConstants.length]; Integer[] entryValues = new Integer[metricsConstants.length]; @@ -208,20 +211,20 @@ public class GeneralProfileSettingsFragment extends BaseSettingsFragment impleme } private void setupAngularUnitsPref() { - OsmandSettings.AngularConstants[] ac = OsmandSettings.AngularConstants.values(); + AngularConstants[] ac = AngularConstants.values(); String[] entries = new String[ac.length]; Integer[] entryValues = new Integer[ac.length]; for (int i = 0; i < entries.length; i++) { - if (ac[i] == OsmandSettings.AngularConstants.DEGREES) { - entries[i] = OsmandSettings.AngularConstants.DEGREES.toHumanString(app) + " 180"; - entryValues[i] = OsmandSettings.AngularConstants.DEGREES.ordinal(); - } else if (ac[i] == OsmandSettings.AngularConstants.DEGREES360) { - entries[i] = OsmandSettings.AngularConstants.DEGREES.toHumanString(app) + " 360"; - entryValues[i] = OsmandSettings.AngularConstants.DEGREES360.ordinal(); + if (ac[i] == AngularConstants.DEGREES) { + entries[i] = AngularConstants.DEGREES.toHumanString(app) + " 180"; + entryValues[i] = AngularConstants.DEGREES.ordinal(); + } else if (ac[i] == AngularConstants.DEGREES360) { + entries[i] = AngularConstants.DEGREES.toHumanString(app) + " 360"; + entryValues[i] = AngularConstants.DEGREES360.ordinal(); } else { entries[i] = ac[i].toHumanString(app); - entryValues[i] = OsmandSettings.AngularConstants.MILLIRADS.ordinal(); + entryValues[i] = AngularConstants.MILLIRADS.ordinal(); } } @@ -232,7 +235,7 @@ public class GeneralProfileSettingsFragment extends BaseSettingsFragment impleme } private void setupSpeedSystemPref() { - OsmandSettings.SpeedConstants[] speedConstants = OsmandSettings.SpeedConstants.values(); + SpeedConstants[] speedConstants = SpeedConstants.values(); String[] entries = new String[speedConstants.length]; Integer[] entryValues = new Integer[speedConstants.length]; @@ -377,14 +380,14 @@ public class GeneralProfileSettingsFragment extends BaseSettingsFragment impleme if (mapViewTrackingUtilities != null) { mapViewTrackingUtilities.resetDrivingRegionUpdate(); } - } else if (newValue instanceof OsmandSettings.DrivingRegion) { + } else if (newValue instanceof DrivingRegion) { applyPreference(settings.DRIVING_REGION_AUTOMATIC.getId(), applyToAllProfiles, false); if (applyToAllProfiles) { for (ApplicationMode appMode : ApplicationMode.allPossibleValues()) { - settings.DRIVING_REGION.setModeValue(appMode, (OsmandSettings.DrivingRegion) newValue); + settings.DRIVING_REGION.setModeValue(appMode, (DrivingRegion) newValue); } } else { - settings.DRIVING_REGION.setModeValue(selectedMode, (OsmandSettings.DrivingRegion) newValue); + settings.DRIVING_REGION.setModeValue(selectedMode, (DrivingRegion) newValue); } } updateAllSettings(); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportCompleteFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportCompleteFragment.java index a18e2f6aa9..e152d5ea68 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportCompleteFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportCompleteFragment.java @@ -22,6 +22,8 @@ import androidx.recyclerview.widget.RecyclerView; import net.osmand.AndroidUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; +import net.osmand.plus.helpers.ImportHelper; +import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.SettingsHelper.SettingsItem; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -31,7 +33,6 @@ import net.osmand.plus.dialogs.SelectMapStyleBottomSheetDialogFragment; import net.osmand.plus.quickaction.QuickActionListFragment; import net.osmand.plus.routepreparationmenu.AvoidRoadsBottomSheetDialogFragment; import net.osmand.plus.search.QuickSearchDialogFragment; -import net.osmand.plus.settings.fragments.ExportImportSettingsAdapter.Type; import java.util.List; @@ -117,11 +118,11 @@ public class ImportCompleteFragment extends BaseOsmAndFragment { if (settingsItems != null) { ImportedSettingsItemsAdapter adapter = new ImportedSettingsItemsAdapter( app, - ImportSettingsFragment.getSettingsToOperate(settingsItems, true), + ImportHelper.getSettingsToOperate(settingsItems, true), nightMode, new ImportedSettingsItemsAdapter.OnItemClickListener() { @Override - public void onItemClick(Type type) { + public void onItemClick(ExportSettingsType type) { navigateTo(type); } }); @@ -137,7 +138,7 @@ public class ImportCompleteFragment extends BaseOsmAndFragment { } } - private void navigateTo(Type type) { + private void navigateTo(ExportSettingsType type) { FragmentManager fm = getFragmentManager(); Activity activity = requireActivity(); if (fm == null || fm.isStateSaved()) { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java index 284964b88d..7e8f6bb934 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java @@ -34,11 +34,11 @@ import net.osmand.plus.AppInitializer; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.SQLiteTileSource; +import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; import net.osmand.plus.settings.backend.SettingsHelper; import net.osmand.plus.settings.backend.SettingsHelper.AvoidRoadsSettingsItem; import net.osmand.plus.settings.backend.SettingsHelper.FileSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.FileSettingsItem.FileSubtype; import net.osmand.plus.settings.backend.SettingsHelper.ImportAsyncTask; import net.osmand.plus.settings.backend.SettingsHelper.ImportType; import net.osmand.plus.settings.backend.SettingsHelper.MapSourcesSettingsItem; @@ -53,7 +53,6 @@ import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.quickaction.QuickAction; -import net.osmand.plus.settings.fragments.ExportImportSettingsAdapter.Type; import net.osmand.plus.widgets.TextViewEx; import net.osmand.util.Algorithms; @@ -65,6 +64,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static net.osmand.plus.helpers.ImportHelper.getSettingsToOperate; + public class ImportSettingsFragment extends BaseOsmAndFragment implements View.OnClickListener { @@ -180,7 +181,7 @@ public class ImportSettingsFragment extends BaseOsmAndFragment } adapter = new ExportImportSettingsAdapter(app, nightMode, true); - Map> itemsMap = new HashMap<>(); + Map> itemsMap = new HashMap<>(); if (settingsItems != null) { itemsMap = getSettingsToOperate(settingsItems, false); adapter.updateSettingsList(itemsMap); @@ -196,7 +197,7 @@ public class ImportSettingsFragment extends BaseOsmAndFragment } else { toolbarLayout.setTitle(getString(R.string.shared_string_import)); } - if (itemsMap.size() == 1 && itemsMap.containsKey(Type.PROFILE)) { + if (itemsMap.size() == 1 && itemsMap.containsKey(ExportSettingsType.PROFILE)) { expandableList.expandGroup(0); } } @@ -266,11 +267,7 @@ public class ImportSettingsFragment extends BaseOsmAndFragment FragmentManager fm = getFragmentManager(); if (succeed) { app.getRendererRegistry().updateExternalRenderers(); - AppInitializer.loadRoutingFiles(app, new AppInitializer.LoadRoutingFilesCallback() { - @Override - public void onRoutingFilesLoaded() { - } - }); + AppInitializer.loadRoutingFiles(app, null); if (fm != null && file != null) { ImportCompleteFragment.showInstance(fm, items, file.getName()); } @@ -420,89 +417,6 @@ public class ImportSettingsFragment extends BaseOsmAndFragment return settingsItems; } - public static Map> getSettingsToOperate(List settingsItems, boolean importComplete) { - Map> settingsToOperate = new HashMap<>(); - List profiles = new ArrayList<>(); - List quickActions = new ArrayList<>(); - List poiUIFilters = new ArrayList<>(); - List tileSourceTemplates = new ArrayList<>(); - List routingFilesList = new ArrayList<>(); - List renderFilesList = new ArrayList<>(); - List avoidRoads = new ArrayList<>(); - for (SettingsItem item : settingsItems) { - switch (item.getType()) { - case PROFILE: - profiles.add(((ProfileSettingsItem) item).getModeBean()); - break; - case FILE: - FileSettingsItem fileItem = (FileSettingsItem) item; - if (fileItem.getSubtype() == FileSubtype.RENDERING_STYLE) { - renderFilesList.add(fileItem.getFile()); - } else if (fileItem.getSubtype() == FileSubtype.ROUTING_CONFIG) { - routingFilesList.add(fileItem.getFile()); - } - break; - case QUICK_ACTIONS: - QuickActionsSettingsItem quickActionsItem = (QuickActionsSettingsItem) item; - if (importComplete) { - quickActions.addAll(quickActionsItem.getAppliedItems()); - } else { - quickActions.addAll(quickActionsItem.getItems()); - } - break; - case POI_UI_FILTERS: - PoiUiFiltersSettingsItem poiUiFilterItem = (PoiUiFiltersSettingsItem) item; - if (importComplete) { - poiUIFilters.addAll(poiUiFilterItem.getAppliedItems()); - } else { - poiUIFilters.addAll(poiUiFilterItem.getItems()); - } - break; - case MAP_SOURCES: - MapSourcesSettingsItem mapSourcesItem = (MapSourcesSettingsItem) item; - if (importComplete) { - tileSourceTemplates.addAll(mapSourcesItem.getAppliedItems()); - } else { - tileSourceTemplates.addAll(mapSourcesItem.getItems()); - } - break; - case AVOID_ROADS: - AvoidRoadsSettingsItem avoidRoadsItem = (AvoidRoadsSettingsItem) item; - if (importComplete) { - avoidRoads.addAll(avoidRoadsItem.getAppliedItems()); - } else { - avoidRoads.addAll(avoidRoadsItem.getItems()); - } - break; - default: - break; - } - } - - if (!profiles.isEmpty()) { - settingsToOperate.put(Type.PROFILE, profiles); - } - if (!quickActions.isEmpty()) { - settingsToOperate.put(Type.QUICK_ACTIONS, quickActions); - } - if (!poiUIFilters.isEmpty()) { - settingsToOperate.put(Type.POI_TYPES, poiUIFilters); - } - if (!tileSourceTemplates.isEmpty()) { - settingsToOperate.put(Type.MAP_SOURCES, tileSourceTemplates); - } - if (!renderFilesList.isEmpty()) { - settingsToOperate.put(Type.CUSTOM_RENDER_STYLE, renderFilesList); - } - if (!routingFilesList.isEmpty()) { - settingsToOperate.put(Type.CUSTOM_ROUTING, routingFilesList); - } - if (!avoidRoads.isEmpty()) { - settingsToOperate.put(Type.AVOID_ROADS, avoidRoads); - } - return settingsToOperate; - } - @Override public int getStatusBarColorId() { return nightMode ? R.color.status_bar_color_dark : R.color.status_bar_color_light; diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java index 270391c619..e663e6f189 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java @@ -14,7 +14,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.helpers.FontCache; -import net.osmand.plus.settings.fragments.ExportImportSettingsAdapter.Type; +import net.osmand.plus.settings.backend.ExportSettingsType; import java.util.ArrayList; @@ -25,15 +25,15 @@ import java.util.Map; public class ImportedSettingsItemsAdapter extends RecyclerView.Adapter { - private Map> itemsMap; - private List itemsTypes; + private Map> itemsMap; + private List itemsTypes; private UiUtilities uiUtils; private OsmandApplication app; private boolean nightMode; private OnItemClickListener listener; - ImportedSettingsItemsAdapter(@NonNull OsmandApplication app, Map> itemsMap, - boolean nightMode, OnItemClickListener listener) { + ImportedSettingsItemsAdapter(@NonNull OsmandApplication app, Map> itemsMap, + boolean nightMode, OnItemClickListener listener) { this.app = app; this.itemsMap = itemsMap; this.nightMode = nightMode; @@ -53,7 +53,7 @@ public class ImportedSettingsItemsAdapter extends @Override public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) { - final Type currentItemType = itemsTypes.get(position); + final ExportSettingsType currentItemType = itemsTypes.get(position); boolean isLastItem = itemsTypes.size() - 1 == position; int activeColorRes = nightMode ? R.color.active_color_primary_dark @@ -130,6 +130,6 @@ public class ImportedSettingsItemsAdapter extends } interface OnItemClickListener { - void onItemClick(Type type); + void onItemClick(ExportSettingsType type); } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/LiveMonitoringFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/LiveMonitoringFragment.java index e664953662..6a426e654c 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/LiveMonitoringFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/LiveMonitoringFragment.java @@ -5,6 +5,7 @@ import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; +import android.widget.Toast; import androidx.appcompat.widget.SwitchCompat; import androidx.core.content.ContextCompat; @@ -12,11 +13,12 @@ import androidx.preference.Preference; import net.osmand.AndroidUtils; import net.osmand.plus.OsmAndFormatter; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; +import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.preferences.EditTextPreferenceEx; import net.osmand.plus.settings.preferences.ListPreferenceEx; +import net.osmand.util.Algorithms; import static net.osmand.plus.UiUtilities.CompoundButtonType.TOOLBAR; import static net.osmand.plus.monitoring.OsmandMonitoringPlugin.MAX_INTERVAL_TO_SEND_MINUTES; @@ -60,6 +62,19 @@ public class LiveMonitoringFragment extends BaseSettingsFragment { updateToolbarSwitch(); } + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference.getKey().equals(settings.LIVE_MONITORING_URL.getId())) { + if (Algorithms.isValidMessageFormat((String) newValue)) { + return super.onPreferenceChange(preference, newValue); + } else { + Toast.makeText(app, R.string.wrong_format, Toast.LENGTH_SHORT).show(); + return false; + } + } + return super.onPreferenceChange(preference, newValue); + } + private void updateToolbarSwitch() { View view = getView(); if (view == null) { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java index 2428290517..c3381520a7 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java @@ -6,6 +6,7 @@ import android.os.Bundle; import android.view.View; import androidx.annotation.ColorRes; +import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; @@ -86,7 +87,8 @@ public class MainSettingsFragment extends BaseSettingsFragment { if (CONFIGURE_PROFILE.equals(key)) { View selectedProfile = holder.itemView.findViewById(R.id.selectable_list_item); if (selectedProfile != null) { - int activeProfileColor = getActiveProfileColor(); + int activeProfileColorId = getSelectedAppMode().getIconColorInfo().getColor(isNightMode()); + int activeProfileColor = ContextCompat.getColor(app, activeProfileColorId); Drawable backgroundDrawable = new ColorDrawable(UiUtilities.getColorWithAlpha(activeProfileColor, 0.15f)); AndroidUtils.setBackground(selectedProfile, backgroundDrawable); } @@ -153,9 +155,8 @@ public class MainSettingsFragment extends BaseSettingsFragment { ApplicationMode selectedMode = app.getSettings().APPLICATION_MODE.get(); String title = selectedMode.toHumanString(); String profileType = getAppModeDescription(getContext(), selectedMode); - int iconRes = selectedMode.getIconRes(); Preference configureProfile = findPreference(CONFIGURE_PROFILE); - configureProfile.setIcon(getPaintedIcon(iconRes, getActiveProfileColor())); + configureProfile.setIcon(getAppProfilesIcon(selectedMode, true)); configureProfile.setTitle(title); configureProfile.setSummary(profileType); } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/MapDuringNavigationFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/MapDuringNavigationFragment.java index 06aa0c4c08..0fe5a6d6a3 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/MapDuringNavigationFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/MapDuringNavigationFragment.java @@ -2,8 +2,8 @@ package net.osmand.plus.settings.fragments; import androidx.preference.Preference; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.AutoZoomMap; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.helpers.enums.AutoZoomMap; import net.osmand.plus.R; import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; @@ -78,7 +78,7 @@ public class MapDuringNavigationFragment extends BaseSettingsFragment { Float[] valuesKmh = new Float[]{0f, 5f, 7f, 10f, 15f, 20f}; Float[] valuesMph = new Float[]{0f, 3f, 5f, 7f, 10f, 15f}; String[] names; - if (settings.METRIC_SYSTEM.getModeValue(getSelectedAppMode()) == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS) { + if (settings.METRIC_SYSTEM.getModeValue(getSelectedAppMode()) == MetricsConstants.KILOMETERS_AND_METERS) { names = new String[valuesKmh.length]; for (int i = 0; i < names.length; i++) { names[i] = valuesKmh[i].intValue() + " " + getString(R.string.km_h); @@ -113,7 +113,7 @@ public class MapDuringNavigationFragment extends BaseSettingsFragment { } else { applyPreference(settings.AUTO_ZOOM_MAP.getId(), applyToAllProfiles, true); applyPreference(settings.AUTO_ZOOM_MAP_SCALE.getId(), - applyToAllProfiles, OsmandSettings.AutoZoomMap.values()[position - 1]); + applyToAllProfiles, AutoZoomMap.values()[position - 1]); } } } else { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java index 198e6e13eb..a17c265645 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java @@ -25,18 +25,19 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.slider.Slider; import net.osmand.StateChangedListener; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.BooleanPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; +import net.osmand.plus.Version; import net.osmand.plus.activities.SettingsBaseActivity; import net.osmand.plus.activities.SettingsNavigationActivity; import net.osmand.plus.routing.RouteProvider; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.BooleanPreference; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.settings.bottomsheets.RecalculateRouteInDeviationBottomSheet; import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.MultiSelectBooleanPreference; @@ -155,6 +156,58 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP getPreferenceScreen().addPreference(timeConditionalRouting); } + private void setupOsmLiveForPublicTransportPref() { + SwitchPreferenceEx useOsmLiveForPublicTransport = createSwitchPreferenceEx(settings.USE_OSM_LIVE_FOR_PUBLIC_TRANSPORT.getId(), + R.string.use_live_public_transport, R.layout.preference_with_descr_dialog_and_switch); + useOsmLiveForPublicTransport.setDescription(getString(R.string.use_osm_live_public_transport_description)); + useOsmLiveForPublicTransport.setSummaryOn(R.string.shared_string_enabled); + useOsmLiveForPublicTransport.setSummaryOff(R.string.shared_string_disabled); + useOsmLiveForPublicTransport.setIconSpaceReserved(true); + getPreferenceScreen().addPreference(useOsmLiveForPublicTransport); + } + + private void setupNativePublicTransport() { + if (!Version.isBlackberry(app)) { + SwitchPreferenceEx setupNativePublicTransport = createSwitchPreferenceEx(settings.PT_SAFE_MODE.getId(), + R.string.use_native_pt, R.layout.preference_with_descr_dialog_and_switch); + setupNativePublicTransport.setDescription(getString(R.string.use_native_pt_desc)); + setupNativePublicTransport.setSummaryOn(R.string.shared_string_enabled); + setupNativePublicTransport.setSummaryOff(R.string.shared_string_disabled); + setupNativePublicTransport.setIconSpaceReserved(true); + getPreferenceScreen().addPreference(setupNativePublicTransport); + } + } + + private void setupOsmLiveForRoutingPref() { + SwitchPreferenceEx useOsmLiveForRouting = createSwitchPreferenceEx(settings.USE_OSM_LIVE_FOR_ROUTING.getId(), + R.string.use_live_routing, R.layout.preference_with_descr_dialog_and_switch); + useOsmLiveForRouting.setDescription(getString(R.string.use_osm_live_routing_description)); + useOsmLiveForRouting.setSummaryOn(R.string.shared_string_enabled); + useOsmLiveForRouting.setSummaryOff(R.string.shared_string_disabled); + useOsmLiveForRouting.setIconSpaceReserved(true); + getPreferenceScreen().addPreference(useOsmLiveForRouting); + } + + private void setupDisableComplexRoutingPref() { + SwitchPreferenceEx disableComplexRouting = createSwitchPreferenceEx(settings.DISABLE_COMPLEX_ROUTING.getId(), + R.string.use_complex_routing, R.layout.preference_with_descr_dialog_and_switch); + disableComplexRouting.setDescription(getString(R.string.complex_routing_descr)); + disableComplexRouting.setSummaryOn(R.string.shared_string_enabled); + disableComplexRouting.setSummaryOff(R.string.shared_string_disabled); + disableComplexRouting.setIconSpaceReserved(true); + getPreferenceScreen().addPreference(disableComplexRouting); + } + + private void setupFastRecalculationPref() { + SwitchPreferenceEx useFastRecalculation = createSwitchPreferenceEx(settings.USE_FAST_RECALCULATION.getId(), + R.string.use_fast_recalculation, R.layout.preference_with_descr_dialog_and_switch); + useFastRecalculation.setDescription(getString(R.string.use_fast_recalculation_desc)); + useFastRecalculation.setSummaryOn(R.string.shared_string_enabled); + useFastRecalculation.setSummaryOff(R.string.shared_string_disabled); + useFastRecalculation.setIconSpaceReserved(true); + getPreferenceScreen().addPreference(useFastRecalculation); + } + private void setupRoutingPrefs() { OsmandApplication app = getMyApplication(); if (app == null) { @@ -170,7 +223,7 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP fastRoute.setSummaryOn(R.string.shared_string_on); fastRoute.setSummaryOff(R.string.shared_string_off); - if (am.getRouteService() == RouteProvider.RouteService.OSMAND){ + if (am.getRouteService() == RouteProvider.RouteService.OSMAND) { GeneralRouter router = app.getRouter(am); clearParameters(); if (router != null) { @@ -216,8 +269,7 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP } if (preferParameters.size() > 0) { String title = getString(R.string.prefer_in_routing_title); - String descr = getString(R.string.prefer_in_routing_descr); - MultiSelectBooleanPreference preferRouting = createRoutingBooleanMultiSelectPref(PREFER_ROUTING_PARAMETER_PREFIX, title, descr, preferParameters); + MultiSelectBooleanPreference preferRouting = createRoutingBooleanMultiSelectPref(PREFER_ROUTING_PARAMETER_PREFIX, title, "", preferParameters); screen.addPreference(preferRouting); } if (reliefFactorParameters.size() > 0) { @@ -231,7 +283,8 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP String description = SettingsBaseActivity.getRoutingStringPropertyDescription(app, p.getId(), p.getDescription()); if (p.getType() == RoutingParameterType.BOOLEAN) { - OsmandPreference pref = settings.getCustomRoutingBooleanProperty(p.getId(), p.getDefaultBoolean()); + OsmandPreference pref = settings.getCustomRoutingBooleanProperty(p.getId(), p.getDefaultBoolean()); + SwitchPreferenceEx switchPreferenceEx = (SwitchPreferenceEx) createSwitchPreferenceEx(pref.getId(), title, description, R.layout.preference_with_descr_dialog_and_switch); switchPreferenceEx.setDescription(description); switchPreferenceEx.setIcon(getRoutingPrefIcon(p.getId())); @@ -245,7 +298,8 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP for (Object o : vls) { svlss[i++] = o.toString(); } - OsmandPreference pref = settings.getCustomRoutingProperty(p.getId(), p.getType() == RoutingParameterType.NUMERIC ? "0.0" : "-"); + OsmandPreference pref = settings.getCustomRoutingProperty(p.getId(), p.getType() == RoutingParameterType.NUMERIC ? "0.0" : "-"); + ListPreferenceEx listPreferenceEx = (ListPreferenceEx) createListPreferenceEx(pref.getId(), p.getPossibleValueDescriptions(), svlss, title, R.layout.preference_with_descr); listPreferenceEx.setDescription(description); listPreferenceEx.setIcon(getRoutingPrefIcon(p.getId())); @@ -273,6 +327,17 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP setupRouteRecalcHeader(screen); setupSelectRouteRecalcDistance(screen); setupReverseDirectionRecalculation(screen); + addDivider(screen); + setupDevelopmentCategoryHeader(screen); + if (am.isDerivedRoutingFrom(ApplicationMode.PUBLIC_TRANSPORT)) { + setupOsmLiveForPublicTransportPref(); + setupNativePublicTransport(); + } + if (am.isDerivedRoutingFrom(ApplicationMode.CAR)) { + setupOsmLiveForRoutingPref(); + setupDisableComplexRoutingPref(); + } + setupFastRecalculationPref(); } private void addDivider(PreferenceScreen screen) { @@ -300,6 +365,13 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP screen.addPreference(routingCategory); } + private void setupDevelopmentCategoryHeader(PreferenceScreen screen) { + PreferenceCategory developmentCategory = new PreferenceCategory(requireContext()); + developmentCategory.setLayoutResource(R.layout.preference_category_with_descr); + developmentCategory.setTitle(R.string.development); + screen.addPreference(developmentCategory); + } + @Override public boolean onPreferenceClick(Preference preference) { if (preference.getKey().equals(settings.ROUTE_STRAIGHT_ANGLE.getId())) { @@ -421,10 +493,10 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP for (RoutingParameter parameter : otherRoutingParameters) { if (parameter.getType() == RoutingParameterType.BOOLEAN) { - OsmandSettings.CommonPreference pref = settings.getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); + CommonPreference pref = settings.getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); pref.addListener(booleanRoutingPrefListener); } else { - OsmandSettings.CommonPreference pref = settings.getCustomRoutingProperty(parameter.getId(), parameter.getType() == RoutingParameterType.NUMERIC ? "0.0" : "-"); + CommonPreference pref = settings.getCustomRoutingProperty(parameter.getId(), parameter.getType() == RoutingParameterType.NUMERIC ? "0.0" : "-"); pref.addListener(customRoutingPrefListener); } } @@ -436,15 +508,23 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP for (RoutingParameter parameter : otherRoutingParameters) { if (parameter.getType() == RoutingParameterType.BOOLEAN) { - OsmandSettings.CommonPreference pref = settings.getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); + CommonPreference pref = settings.getCustomRoutingBooleanProperty(parameter.getId(), parameter.getDefaultBoolean()); pref.removeListener(booleanRoutingPrefListener); } else { - OsmandSettings.CommonPreference pref = settings.getCustomRoutingProperty(parameter.getId(), parameter.getType() == RoutingParameterType.NUMERIC ? "0.0" : "-"); + CommonPreference pref = settings.getCustomRoutingProperty(parameter.getId(), parameter.getType() == RoutingParameterType.NUMERIC ? "0.0" : "-"); pref.removeListener(customRoutingPrefListener); } } } + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (settings.DISABLE_COMPLEX_ROUTING.getId().equals(preference.getKey()) && newValue instanceof Boolean) { + return onConfirmPreferenceChange(preference.getKey(), !(Boolean) newValue, getApplyQueryType()); // pref ui was inverted + } + return onConfirmPreferenceChange(preference.getKey(), newValue, getApplyQueryType()); + } + @Override public void onApplyPreferenceChange(String prefId, boolean applyToAllProfiles, Object newValue) { if ((RELIEF_SMOOTHNESS_FACTOR.equals(prefId) || DRIVING_STYLE.equals(prefId)) && newValue instanceof String) { @@ -472,7 +552,7 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP applyPreference(ROUTING_RECALC_DISTANCE, applyToAllProfiles, valueToSave); applyPreference(settings.DISABLE_OFFROUTE_RECALC.getId(), applyToAllProfiles, !enabled); updateRouteRecalcDistancePref(); - } else if (settings.DISABLE_WRONG_DIRECTION_RECALC.getId().equals(prefId)){ + } else if (settings.DISABLE_WRONG_DIRECTION_RECALC.getId().equals(prefId)) { applyPreference(settings.DISABLE_WRONG_DIRECTION_RECALC.getId(), applyToAllProfiles, newValue); } else { super.onApplyPreferenceChange(prefId, applyToAllProfiles, newValue); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java index df1af1a693..dfed2c5930 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java @@ -14,7 +14,7 @@ import net.osmand.plus.activities.SettingsBaseActivity; import net.osmand.plus.routing.RouteProvider.RouteService; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.StringPreference; import net.osmand.plus.settings.bottomsheets.VehicleParametersBottomSheet; import net.osmand.plus.settings.bottomsheets.VehicleSizeAssets; import net.osmand.plus.settings.preferences.ListPreferenceEx; @@ -72,7 +72,7 @@ public class VehicleParametersFragment extends BaseSettingsFragment implements O parameter.getDescription()); String defValue = parameter.getType() == RoutingParameterType.NUMERIC ? ROUTING_PARAMETER_NUMERIC_DEFAULT : ROUTING_PARAMETER_SYMBOLIC_DEFAULT; - OsmandSettings.StringPreference pref = (OsmandSettings.StringPreference) app.getSettings() + StringPreference pref = (StringPreference) app.getSettings() .getCustomRoutingProperty(parameterId, defValue); VehicleSizeAssets assets = VehicleSizeAssets.getAssets(parameterId, routerProfile); Object[] values = parameter.getPossibleValues(); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java index 4ff8f3262d..8013baeb63 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java @@ -18,6 +18,7 @@ import androidx.preference.SwitchPreferenceCompat; import net.osmand.AndroidUtils; import net.osmand.plus.dialogs.SpeedCamerasBottomSheet; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; @@ -107,7 +108,7 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr Float[] valuesKmh = new Float[]{-10f, -7f, -5f, 0f, 5f, 7f, 10f, 15f, 20f}; Float[] valuesMph = new Float[]{-7f, -5f, -3f, 0f, 3f, 5f, 7f, 10f, 15f}; String[] names; - if (settings.METRIC_SYSTEM.getModeValue(getSelectedAppMode()) == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS) { + if (settings.METRIC_SYSTEM.getModeValue(getSelectedAppMode()) == MetricsConstants.KILOMETERS_AND_METERS) { names = new String[valuesKmh.length]; for (int i = 0; i < names.length; i++) { names[i] = valuesKmh[i].intValue() + " " + getString(R.string.km_h); diff --git a/OsmAnd/src/net/osmand/plus/settings/preferences/ListPreferenceEx.java b/OsmAnd/src/net/osmand/plus/settings/preferences/ListPreferenceEx.java index 0743cd9c8b..07e5d088b9 100644 --- a/OsmAnd/src/net/osmand/plus/settings/preferences/ListPreferenceEx.java +++ b/OsmAnd/src/net/osmand/plus/settings/preferences/ListPreferenceEx.java @@ -8,7 +8,7 @@ import androidx.preference.DialogPreference; import androidx.preference.PreferenceDataStore; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings.PreferencesDataStore; +import net.osmand.plus.settings.backend.OsmAndPreferencesDataStore; public class ListPreferenceEx extends DialogPreference { @@ -126,8 +126,8 @@ public class ListPreferenceEx extends DialogPreference { private Object getPersistedValue(Object defaultValue) { PreferenceDataStore dataStore = getPreferenceDataStore(); - if (dataStore instanceof PreferencesDataStore) { - Object value = ((PreferencesDataStore) dataStore).getValue(getKey(), defaultValue); + if (dataStore instanceof OsmAndPreferencesDataStore) { + Object value = ((OsmAndPreferencesDataStore) dataStore).getValue(getKey(), defaultValue); if (value instanceof Enum) { return ((Enum) value).ordinal(); } else if (value instanceof ApplicationMode) { @@ -144,8 +144,8 @@ public class ListPreferenceEx extends DialogPreference { return; } PreferenceDataStore dataStore = getPreferenceDataStore(); - if (dataStore instanceof PreferencesDataStore) { - ((PreferencesDataStore) dataStore).putValue(getKey(), value); + if (dataStore instanceof OsmAndPreferencesDataStore) { + ((OsmAndPreferencesDataStore) dataStore).putValue(getKey(), value); } } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/preferences/MultiSelectBooleanPreference.java b/OsmAnd/src/net/osmand/plus/settings/preferences/MultiSelectBooleanPreference.java index 896e1864d1..1f69cd0989 100644 --- a/OsmAnd/src/net/osmand/plus/settings/preferences/MultiSelectBooleanPreference.java +++ b/OsmAnd/src/net/osmand/plus/settings/preferences/MultiSelectBooleanPreference.java @@ -6,7 +6,7 @@ import android.util.AttributeSet; import androidx.preference.MultiSelectListPreference; import androidx.preference.PreferenceDataStore; -import net.osmand.plus.settings.backend.OsmandSettings.PreferencesDataStore; +import net.osmand.plus.settings.backend.OsmAndPreferencesDataStore; import java.util.HashSet; import java.util.Set; @@ -79,8 +79,8 @@ public class MultiSelectBooleanPreference extends MultiSelectListPreference { return; } PreferenceDataStore dataStore = getPreferenceDataStore(); - if (dataStore instanceof PreferencesDataStore) { - PreferencesDataStore preferencesDataStore = (PreferencesDataStore) dataStore; + if (dataStore instanceof OsmAndPreferencesDataStore) { + OsmAndPreferencesDataStore preferencesDataStore = (OsmAndPreferencesDataStore) dataStore; for (String prefId : getPrefsIds()) { preferencesDataStore.putBoolean(prefId, getValues().contains(prefId)); @@ -96,8 +96,8 @@ public class MultiSelectBooleanPreference extends MultiSelectListPreference { Set enabledPrefs = new HashSet<>(); PreferenceDataStore dataStore = getPreferenceDataStore(); - if (dataStore instanceof PreferencesDataStore && getEntryValues() != null) { - PreferencesDataStore preferencesDataStore = (PreferencesDataStore) dataStore; + if (dataStore instanceof OsmAndPreferencesDataStore && getEntryValues() != null) { + OsmAndPreferencesDataStore preferencesDataStore = (OsmAndPreferencesDataStore) dataStore; for (String prefId : getPrefsIds()) { boolean enabled = preferencesDataStore.getBoolean(prefId, false); diff --git a/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesAction.java b/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesAction.java index 9c4ae901f7..bcbd80ff92 100644 --- a/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesAction.java +++ b/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesAction.java @@ -8,7 +8,7 @@ import android.widget.TextView; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.quickaction.QuickAction; @@ -46,7 +46,7 @@ public class ContourLinesAction extends QuickAction { OsmandApplication app = activity.getMyApplication(); RenderingRuleProperty contourLinesProp = app.getRendererRegistry().getCustomRenderingRuleProperty(CONTOUR_LINES_ATTR); if (contourLinesProp != null) { - final OsmandSettings.CommonPreference pref = app.getSettings().getCustomRenderProperty(contourLinesProp.getAttrName()); + final CommonPreference pref = app.getSettings().getCustomRenderProperty(contourLinesProp.getAttrName()); boolean selected = !pref.get().equals(CONTOUR_LINES_DISABLED_VALUE); if (selected && !plugin.isActive() && !plugin.needsInstallation()) { diff --git a/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesMenu.java b/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesMenu.java index 94a33cf6a8..be2631dc04 100644 --- a/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesMenu.java +++ b/OsmAnd/src/net/osmand/plus/srtmplugin/ContourLinesMenu.java @@ -7,6 +7,7 @@ import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; @@ -61,8 +62,8 @@ public class ContourLinesMenu { final String contourWidthName; final String contourDensityName; - final OsmandSettings.CommonPreference widthPref; - final OsmandSettings.CommonPreference densityPref; + final CommonPreference widthPref; + final CommonPreference densityPref; final RenderingRuleProperty contourWidthProp = app.getRendererRegistry().getCustomRenderingRuleProperty(CONTOUR_WIDTH_ATTR); if (contourWidthProp != null) { contourWidthName = SettingsActivity.getStringPropertyName(app, contourWidthProp.getAttrName(), @@ -82,8 +83,8 @@ public class ContourLinesMenu { densityPref = null; } - final OsmandSettings.CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); - final OsmandSettings.CommonPreference colorPref = settings.getCustomRenderProperty(colorSchemeProp.getAttrName()); + final CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); + final CommonPreference colorPref = settings.getCustomRenderProperty(colorSchemeProp.getAttrName()); final boolean selected = !pref.get().equals(CONTOUR_LINES_DISABLED_VALUE); final int toggleActionStringId = selected ? R.string.shared_string_on : R.string.shared_string_off; diff --git a/OsmAnd/src/net/osmand/plus/srtmplugin/SRTMPlugin.java b/OsmAnd/src/net/osmand/plus/srtmplugin/SRTMPlugin.java index 808c541523..5a186e75d0 100644 --- a/OsmAnd/src/net/osmand/plus/srtmplugin/SRTMPlugin.java +++ b/OsmAnd/src/net/osmand/plus/srtmplugin/SRTMPlugin.java @@ -21,8 +21,7 @@ import net.osmand.plus.DialogListItemAdapter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.TerrainMode; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.SettingsActivity; @@ -95,7 +94,9 @@ public class SRTMPlugin extends OsmandPlugin { @Override protected boolean pluginAvailable(OsmandApplication app) { - return super.pluginAvailable(app) || InAppPurchaseHelper.isSubscribedToLiveUpdates(app); + return super.pluginAvailable(app) + || InAppPurchaseHelper.isSubscribedToLiveUpdates(app) + || InAppPurchaseHelper.isContourLinesPurchased(app); } @Override @@ -234,7 +235,7 @@ public class SRTMPlugin extends OsmandPlugin { RenderingRuleProperty contourLinesProp = app.getRendererRegistry().getCustomRenderingRuleProperty(CONTOUR_LINES_ATTR); if (contourLinesProp != null) { - final OsmandSettings.CommonPreference pref = app.getSettings().getCustomRenderProperty(contourLinesProp.getAttrName()); + final CommonPreference pref = app.getSettings().getCustomRenderProperty(contourLinesProp.getAttrName()); if (!Algorithms.isEmpty(pref.get())) { contourLinesEnabled = !CONTOUR_LINES_DISABLED_VALUE.equals(pref.get()); } else { @@ -291,7 +292,7 @@ public class SRTMPlugin extends OsmandPlugin { public void run() { RenderingRuleProperty contourLinesProp = app.getRendererRegistry().getCustomRenderingRuleProperty(CONTOUR_LINES_ATTR); if (contourLinesProp != null) { - final OsmandSettings.CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); + final CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); boolean selected = !pref.get().equals(CONTOUR_LINES_DISABLED_VALUE); SRTMPlugin plugin = OsmandPlugin.getPlugin(SRTMPlugin.class); @@ -338,7 +339,7 @@ public class SRTMPlugin extends OsmandPlugin { RenderingRuleProperty contourLinesProp = app.getRendererRegistry().getCustomRenderingRuleProperty(CONTOUR_LINES_ATTR); if (contourLinesProp != null) { - final OsmandSettings.CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); + final CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); boolean contourLinesSelected = isContourLinesLayerEnabled(app); String descr = getPrefDescription(app, contourLinesProp, pref); adapter.addItem(new ContextMenuItem.ItemBuilder() @@ -359,7 +360,7 @@ public class SRTMPlugin extends OsmandPlugin { .setTitleId(R.string.shared_string_terrain, mapActivity) .setDescription(app.getString(terrainMode == TerrainMode.HILLSHADE ? R.string.shared_string_hillshade - : R.string.shared_string_slope)) + : R.string.download_slope_maps)) .setSelected(terrainEnabled) .setColor(terrainEnabled ? R.color.osmand_orange : ContextMenuItem.INVALID_ID) .setIcon(R.drawable.ic_action_hillshade_dark) @@ -407,7 +408,7 @@ public class SRTMPlugin extends OsmandPlugin { final Runnable callback) { RenderingRuleProperty contourLinesProp = app.getRendererRegistry().getCustomRenderingRuleProperty(CONTOUR_LINES_ATTR); if (contourLinesProp != null) { - final OsmandSettings.CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); + final CommonPreference pref = settings.getCustomRenderProperty(contourLinesProp.getAttrName()); CommonPreference zoomSetting = settings.CONTOUR_LINES_ZOOM; if (!isChecked) { zoomSetting.set(pref.get()); @@ -435,7 +436,7 @@ public class SRTMPlugin extends OsmandPlugin { } } - public String getPrefDescription(final Context ctx, final RenderingRuleProperty p, final OsmandSettings.CommonPreference pref) { + public String getPrefDescription(final Context ctx, final RenderingRuleProperty p, final CommonPreference pref) { if (!Algorithms.isEmpty(pref.get())) { return SettingsActivity.getStringPropertyValue(ctx, pref.get()); } else { @@ -445,7 +446,7 @@ public class SRTMPlugin extends OsmandPlugin { public void selectPropertyValue(final MapActivity activity, final RenderingRuleProperty p, - final OsmandSettings.CommonPreference pref, + final CommonPreference pref, final Runnable callback) { final String propertyDescr = SettingsActivity.getStringPropertyDescription(activity, p.getAttrName(), p.getName()); diff --git a/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java index b613d2ba13..3635aafe89 100644 --- a/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java +++ b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java @@ -43,7 +43,6 @@ import net.osmand.plus.download.DownloadValidationManager; import net.osmand.plus.download.IndexItem; import net.osmand.plus.helpers.FontCache; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.TerrainMode; import net.osmand.plus.widgets.style.CustomTypefaceSpan; import org.apache.commons.logging.Log; @@ -55,8 +54,8 @@ import java.util.List; import static net.osmand.plus.UiUtilities.CustomRadioButtonType.*; import static net.osmand.plus.download.DownloadActivityType.HILLSHADE_FILE; import static net.osmand.plus.download.DownloadActivityType.SLOPE_FILE; -import static net.osmand.plus.settings.backend.OsmandSettings.TerrainMode.HILLSHADE; -import static net.osmand.plus.settings.backend.OsmandSettings.TerrainMode.SLOPE; +import static net.osmand.plus.srtmplugin.TerrainMode.HILLSHADE; +import static net.osmand.plus.srtmplugin.TerrainMode.SLOPE; import static net.osmand.plus.srtmplugin.SRTMPlugin.TERRAIN_MAX_ZOOM; import static net.osmand.plus.srtmplugin.SRTMPlugin.TERRAIN_MIN_ZOOM; diff --git a/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainLayer.java b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainLayer.java index 8f75c68259..79e47de648 100644 --- a/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainLayer.java +++ b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainLayer.java @@ -18,7 +18,6 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.SQLiteTileSource; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.api.SQLiteAPI.SQLiteConnection; -import net.osmand.plus.settings.backend.OsmandSettings.TerrainMode; import net.osmand.plus.views.MapTileLayer; import net.osmand.util.Algorithms; @@ -31,7 +30,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import static net.osmand.plus.settings.backend.OsmandSettings.TerrainMode.HILLSHADE; +import static net.osmand.plus.srtmplugin.TerrainMode.HILLSHADE; public class TerrainLayer extends MapTileLayer { diff --git a/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainMode.java b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainMode.java new file mode 100644 index 0000000000..e9f3d20d9f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainMode.java @@ -0,0 +1,6 @@ +package net.osmand.plus.srtmplugin; + +public enum TerrainMode { + HILLSHADE, + SLOPE +} diff --git a/OsmAnd/src/net/osmand/plus/track/ShowStartFinishCard.java b/OsmAnd/src/net/osmand/plus/track/ShowStartFinishCard.java index 3941bdbb15..eaf145379c 100644 --- a/OsmAnd/src/net/osmand/plus/track/ShowStartFinishCard.java +++ b/OsmAnd/src/net/osmand/plus/track/ShowStartFinishCard.java @@ -10,8 +10,7 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.routepreparationmenu.cards.BaseCard; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; class ShowStartFinishCard extends BaseCard { diff --git a/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java index 81a43da20f..2276d57cd2 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java @@ -41,7 +41,7 @@ import net.osmand.plus.dialogs.GpxAppearanceAdapter; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener; import net.osmand.plus.track.SplitTrackAsyncTask.SplitTrackListener; import net.osmand.render.RenderingRulesStorage; @@ -185,7 +185,7 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement } if (color == 0) { RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer(); - OsmandSettings.CommonPreference prefColor = app.getSettings().getCustomRenderProperty(CURRENT_TRACK_COLOR_ATTR); + CommonPreference prefColor = app.getSettings().getCustomRenderProperty(CURRENT_TRACK_COLOR_ATTR); color = GpxAppearanceAdapter.parseTrackColor(renderer, prefColor.get()); } trackDrawInfo.setColor(color); diff --git a/OsmAnd/src/net/osmand/plus/transport/TransportLinesMenu.java b/OsmAnd/src/net/osmand/plus/transport/TransportLinesMenu.java index aff1e354e4..e47ce6c8d2 100644 --- a/OsmAnd/src/net/osmand/plus/transport/TransportLinesMenu.java +++ b/OsmAnd/src/net/osmand/plus/transport/TransportLinesMenu.java @@ -20,7 +20,7 @@ import net.osmand.CallbackWithObject; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; diff --git a/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java index d1c32ee4a9..580f0c3490 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java @@ -24,7 +24,7 @@ import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.data.RotatedTileBox; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.mapcontextmenu.MapContextMenu; diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 641944ce21..46d51bca33 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -50,7 +50,7 @@ import net.osmand.plus.mapcontextmenu.other.TrackChartPoints; import net.osmand.plus.render.OsmandRenderer; import net.osmand.plus.render.OsmandRenderer.RenderingContext; import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.track.SaveGpxAsyncTask; import net.osmand.plus.track.TrackDrawInfo; import net.osmand.plus.views.OsmandMapLayer; diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java index f1c3c1f13a..6a4cf344c7 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java @@ -44,6 +44,10 @@ import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmAndLocationSimulation; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.rastermaps.LayerTransparencySeekbarMode; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; @@ -63,9 +67,6 @@ import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchType; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings.LayerTransparencySeekbarMode; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.corenative.NativeCoreContext; @@ -1360,7 +1361,7 @@ public class MapControlsLayer extends OsmandMapLayer { @Override public boolean onLongClick(View notUseCouldBeNull) { - final OsmandSettings.OsmandPreference mapDensity = view.getSettings().MAP_DENSITY; + final OsmandPreference mapDensity = view.getSettings().MAP_DENSITY; final AlertDialog.Builder bld = new AlertDialog.Builder(view.getContext()); int p = (int) (mapDensity.get() * 100); final TIntArrayList tlist = new TIntArrayList(new int[]{25, 33, 50, 75, 100, 125, 150, 200, 300, 400}); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/RulerControlLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/RulerControlLayer.java index 5bd719b11b..8ba37ca98a 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/RulerControlLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/RulerControlLayer.java @@ -29,9 +29,9 @@ import net.osmand.data.QuadPoint; import net.osmand.data.RotatedTileBox; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.AngularConstants; -import net.osmand.plus.settings.backend.OsmandSettings.RulerMode; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.helpers.enums.AngularConstants; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.views.OsmandMapLayer; @@ -70,8 +70,8 @@ public class RulerControlLayer extends OsmandMapLayer { private QuadPoint cacheCenter; private float cacheMapDensity; - private OsmandSettings.OsmandPreference mapDensity; - private OsmandSettings.MetricsConstants cacheMetricSystem; + private OsmandPreference mapDensity; + private MetricsConstants cacheMetricSystem; private int cacheIntZoom; private LatLon cacheCenterLatLon; private long cacheMultiTouchEndTime; @@ -451,7 +451,7 @@ public class RulerControlLayer extends OsmandMapLayer { updateCenter(tb, center); } - OsmandSettings.MetricsConstants currentMetricSystem = app.getSettings().METRIC_SYSTEM.get(); + MetricsConstants currentMetricSystem = app.getSettings().METRIC_SYSTEM.get(); boolean updateCache = tb.getZoom() != cacheIntZoom || !tb.getCenterLatLon().equals(cacheCenterLatLon) || mapDensity.get() != cacheMapDensity || cacheMetricSystem != currentMetricSystem; @@ -822,4 +822,10 @@ public class RulerControlLayer extends OsmandMapLayer { public boolean drawInScreenPixels() { return false; } + + public enum RulerMode { + FIRST, + SECOND, + EMPTY + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java index ce20aa066e..2eb4f27956 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java @@ -21,6 +21,7 @@ import net.osmand.data.TransportStop; import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Way; import net.osmand.plus.base.PointImageDrawable; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; @@ -53,7 +54,7 @@ public class TransportStopsLayer extends OsmandMapLayer implements ContextMenuLa private MapLayerData> data; private TransportStopRoute stopRoute = null; - private OsmandSettings.CommonPreference showTransportStops; + private CommonPreference showTransportStops; private Path path; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java index 9979b3820b..565fb872c0 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java @@ -48,6 +48,7 @@ import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmAndLocationProvider.GPSInfo; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -63,11 +64,10 @@ import net.osmand.plus.routepreparationmenu.ShowAlongTheRouteBottomSheet; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.RulerMode; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.RulerControlLayer; +import net.osmand.plus.views.layers.RulerControlLayer.RulerMode; import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget; import net.osmand.render.RenderingRuleSearchRequest; import net.osmand.render.RenderingRulesStorage; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java index 1e968e4ae6..2cf6d23d93 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java @@ -17,7 +17,7 @@ import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java index e3a16e5975..f77bc27f36 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java @@ -16,6 +16,8 @@ import net.osmand.plus.MapMarkersHelper.MapMarker; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; @@ -23,8 +25,6 @@ import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.mapwidgets.widgets.AlarmWidget; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/AlarmWidget.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/AlarmWidget.java index a75422967d..2dc092139e 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/AlarmWidget.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/AlarmWidget.java @@ -16,6 +16,7 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.helpers.enums.DrivingRegion; import net.osmand.plus.helpers.WaypointHelper; import net.osmand.plus.routing.AlarmInfo; import net.osmand.plus.routing.RoutingHelper; @@ -40,7 +41,7 @@ public class AlarmWidget { private int imgId; private String cachedText; private String cachedBottomText; - private OsmandSettings.DrivingRegion cachedRegion; + private DrivingRegion cachedRegion; public AlarmWidget(final OsmandApplication app, MapActivity ma) { layout = ma.findViewById(R.id.map_alarm_warning); @@ -81,9 +82,9 @@ public class AlarmWidget { int locimgId = R.drawable.warnings_limit; String text = ""; String bottomText = ""; - OsmandSettings.DrivingRegion region = settings.DRIVING_REGION.get(); + DrivingRegion region = settings.DRIVING_REGION.get(); boolean americanType = region.isAmericanTypeSigns(); - boolean isCanadianRegion = region == OsmandSettings.DrivingRegion.CANADA; + boolean isCanadianRegion = region == DrivingRegion.CANADA; if (alarm.getType() == AlarmInfo.AlarmInfoType.SPEED_LIMIT) { if (isCanadianRegion) { locimgId = R.drawable.warnings_speed_limit_ca; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/RulerWidget.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/RulerWidget.java index 8d576c72cf..636ef0af5a 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/RulerWidget.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/RulerWidget.java @@ -10,7 +10,7 @@ import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/BearingWidgetState.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/BearingWidgetState.java index ce4761eab1..d3fc793ee1 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/BearingWidgetState.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/BearingWidgetState.java @@ -2,14 +2,14 @@ package net.osmand.plus.views.mapwidgets.widgetstates; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.OsmandPreference; public class BearingWidgetState extends WidgetState { public static final int BEARING_WIDGET_STATE_RELATIVE_BEARING = R.id.bearing_widget_state_relative_bearing; public static final int BEARING_WIDGET_STATE_MAGNETIC_BEARING = R.id.bearing_widget_state_magnetic_bearing; - private final OsmandSettings.OsmandPreference showRelativeBearing; + private final OsmandPreference showRelativeBearing; public BearingWidgetState(OsmandApplication ctx) { super(ctx); diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/CompassRulerWidgetState.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/CompassRulerWidgetState.java index eb77043746..39de1d653c 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/CompassRulerWidgetState.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/CompassRulerWidgetState.java @@ -2,7 +2,7 @@ package net.osmand.plus.views.mapwidgets.widgetstates; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; public class CompassRulerWidgetState extends WidgetState { diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/TimeWidgetState.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/TimeWidgetState.java index 3274245a0c..b42d5a970d 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/TimeWidgetState.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgetstates/TimeWidgetState.java @@ -2,7 +2,7 @@ package net.osmand.plus.views.mapwidgets.widgetstates; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandPreference; public class TimeWidgetState extends WidgetState { diff --git a/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java index 74c0de1508..44f301945f 100644 --- a/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java +++ b/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java @@ -12,7 +12,7 @@ import net.osmand.StateChangedListener; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.MetricsConstants; +import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.R; import net.osmand.plus.api.AudioFocusHelper; diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index 7b9d7a4972..d9bc2b0f9d 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -8,7 +8,7 @@ import android.os.Build; import net.osmand.PlatformUtil; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.routing.VoiceRouter; import org.apache.commons.logging.Log; @@ -91,7 +91,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen // Delay first prompt of each batch to allow BT SCO link being established, or when VOICE_PROMPT_DELAY is set >0 for the other stream types if (ctx != null) { Integer stream = ctx.getSettings().AUDIO_MANAGER_STREAM.getModeValue(getApplicationMode()); - OsmandSettings.OsmandPreference pref = ctx.getSettings().VOICE_PROMPT_DELAY[stream]; + OsmandPreference pref = ctx.getSettings().VOICE_PROMPT_DELAY[stream]; if (pref.getModeValue(getApplicationMode()) > 0) { try { Thread.sleep(pref.getModeValue(getApplicationMode())); diff --git a/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java index 8ac282c331..993d7bc038 100644 --- a/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java @@ -17,7 +17,7 @@ import androidx.appcompat.app.AlertDialog; import net.osmand.PlatformUtil; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.activities.SettingsActivity; import net.osmand.plus.routing.VoiceRouter; @@ -127,7 +127,7 @@ public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { // Delay first prompt of each batch to allow BT SCO link being established, or when VOICE_PROMPT_DELAY is set >0 for the other stream types if (ctx != null) { Integer streamModeValue = ctx.getSettings().AUDIO_MANAGER_STREAM.getModeValue(getApplicationMode()); - OsmandSettings.OsmandPreference pref = ctx.getSettings().VOICE_PROMPT_DELAY[streamModeValue]; + OsmandPreference pref = ctx.getSettings().VOICE_PROMPT_DELAY[streamModeValue]; int vpd = pref == null ? 0 : pref.getModeValue(getApplicationMode()); if (vpd > 0) { ttsRequests++; diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleBaseDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleBaseDialogFragment.java index 302140ba03..0e15c2b4a9 100644 --- a/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleBaseDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleBaseDialogFragment.java @@ -13,7 +13,6 @@ import androidx.annotation.NonNull; import net.osmand.AndroidUtils; import net.osmand.IndexConstants; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.wikivoyage.WikiBaseDialogFragment; @@ -94,7 +93,7 @@ public abstract class WikiArticleBaseDialogFragment extends WikiBaseDialogFragme protected void updateWebSettings() { - OsmandSettings.WikiArticleShowImages showImages = getSettings().WIKI_ARTICLE_SHOW_IMAGES.get(); + WikiArticleShowImages showImages = getSettings().WIKI_ARTICLE_SHOW_IMAGES.get(); WebSettings webSettings = contentWebView.getSettings(); switch (showImages) { case ON: diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleShowImages.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleShowImages.java new file mode 100644 index 0000000000..ea3d8a925b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleShowImages.java @@ -0,0 +1,15 @@ +package net.osmand.plus.wikipedia; + +import net.osmand.plus.R; + +public enum WikiArticleShowImages { + ON(R.string.shared_string_on), + OFF(R.string.shared_string_off), + WIFI(R.string.shared_string_wifi_only); + + public final int name; + + WikiArticleShowImages(int name) { + this.name = name; + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaOptionsBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaOptionsBottomSheetDialogFragment.java index 41edcb0a02..7c2b23867e 100644 --- a/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaOptionsBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaOptionsBottomSheetDialogFragment.java @@ -9,8 +9,7 @@ import androidx.appcompat.widget.PopupMenu; import androidx.fragment.app.Fragment; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.WikiArticleShowImages; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; @@ -28,7 +27,7 @@ public class WikipediaOptionsBottomSheetDialogFragment extends MenuBottomSheetDi @Override public void createMenuItems(Bundle savedInstanceState) { final OsmandApplication app = getMyApplication(); - final OsmandSettings.CommonPreference showImagesPref = app.getSettings().WIKI_ARTICLE_SHOW_IMAGES; + final CommonPreference showImagesPref = app.getSettings().WIKI_ARTICLE_SHOW_IMAGES; items.add(new TitleItem(getString(R.string.shared_string_options))); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageShowPicturesDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageShowPicturesDialogFragment.java index 07c748cd28..824a2e9e49 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageShowPicturesDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageShowPicturesDialogFragment.java @@ -18,7 +18,7 @@ import androidx.fragment.app.Fragment; import net.osmand.AndroidUtils; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings.WikiArticleShowImages; +import net.osmand.plus.wikipedia.WikiArticleShowImages; import net.osmand.plus.R; import net.osmand.plus.base.BottomSheetDialogFragment; import net.osmand.plus.helpers.AndroidUiHelper; diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java index be9d3f2e58..fa704688a0 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java @@ -50,7 +50,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import static net.osmand.plus.settings.backend.OsmandSettings.WikiArticleShowImages.OFF; +import static net.osmand.plus.wikipedia.WikiArticleShowImages.OFF; public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragment { diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java index 3b67249cf7..988598c359 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java @@ -15,8 +15,8 @@ import androidx.appcompat.widget.PopupMenu; import net.osmand.PicassoUtils; import net.osmand.plus.OnDialogFragmentResultListener; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandSettings.WikiArticleShowImages; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.wikipedia.WikiArticleShowImages; import net.osmand.plus.R; import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; @@ -44,7 +44,7 @@ public class WikivoyageOptionsBottomSheetDialogFragment extends MenuBottomSheetD if (app == null) { return; } - final OsmandSettings.CommonPreference showImagesPref = app.getSettings().WIKI_ARTICLE_SHOW_IMAGES; + final CommonPreference showImagesPref = app.getSettings().WIKI_ARTICLE_SHOW_IMAGES; final TravelDbHelper dbHelper = app.getTravelDbHelper(); items.add(new TitleItem(getString(R.string.shared_string_options))); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java b/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java index 902b883e3d..de9793d267 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java @@ -135,6 +135,7 @@ public class SearchRecyclerViewAdapter extends RecyclerView.Adapter