diff --git a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java index 4af1596c2c..f7be034cbe 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java @@ -39,6 +39,11 @@ public class Amenity extends MapObject { public static final String REF = "ref"; public static final String OSM_DELETE_VALUE = "delete"; public static final String OSM_DELETE_TAG = "osmand_change"; + public static final String IMAGE_TITLE = "image_title"; + public static final String IS_PART = "is_part"; + public static final String IS_AGGR_PART = "is_aggr_part"; + public static final String CONTENT_JSON = "content_json"; + private String subType; private PoiCategory type; diff --git a/OsmAnd/build.gradle.lib b/OsmAnd/build.gradle.lib index f29b9b2a10..419f7dd801 100644 --- a/OsmAnd/build.gradle.lib +++ b/OsmAnd/build.gradle.lib @@ -17,6 +17,7 @@ android { } lintOptions { + tasks.lint.enabled = false abortOnError false } @@ -42,8 +43,6 @@ android { res.srcDirs = ["res"] assets.srcDirs = ["assets"] } - debug { - } legacy { jniLibs.srcDirs = ["libc++"] @@ -339,23 +338,74 @@ task appStart(type: Exec) { // commandLine 'cmd', '/c', 'adb', 'shell', 'am', 'start', '-n', 'net.osmand.plus/net.osmand.plus.activities.MapActivity' } +artifacts { + archives(file("../OsmAnd-java/build/libs/OsmAnd-java-android-1.0.jar")) { + classifier "OsmAnd-java-android" + name 'OsmAnd' + type "jar" + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-x86-debug.aar")) { + classifier 'legacyX86Debug' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-x86-release.aar")) { + classifier 'legacyX86Release' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-arm64-debug.aar")) { + classifier 'legacyArm64Debug' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-arm64-release.aar")) { + classifier 'legacyArm64Release' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-armonly-debug.aar")) { + classifier 'legacyArmonlyDebug' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-armonly-release.aar")) { + classifier 'legacyArmonlyRelease' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-armv7-debug.aar")) { + classifier 'legacyArmv7Debug' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-armv7-release.aar")) { + classifier 'legacyArmv7Release' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-fat-debug.aar")) { + classifier 'legacyFatDebug' + name 'OsmAnd' + extension 'aar' + } + archives(file("$buildDir/outputs/aar/OsmAnd-legacy-fat-release.aar")) { + classifier 'legacyFatRelease' + name 'OsmAnd' + extension 'aar' + } +} + // Uploading artifacts to local path -group = "net.osmand" +group = 'net.osmand' uploadArchives { repositories.ivy { // credentials { // username "" // password "" // } - url = System.getenv("OSMAND_BINARIES_IVY_ROOT") ?: "./" - layout "pattern" , { - artifact "[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" - } -// def gitInfo = "git describe --long".execute().text.trim() -// def parsedGitInfo = Pattern.compile("v(\\d+\\.\\d+)-([\\d.]+)-(\\w+)").matcher(gitInfo) -// assert parsedGitInfo.matches() -// version = parsedGitInfo.group(1) + "-SNAPSHOT" + url = System.getenv("OSMAND_BINARIES_IVY_ROOT") ?: "./" version = "1.0-SNAPSHOT" } } diff --git a/OsmAnd/res/animator/appbar_always_elevated.xml b/OsmAnd/res/animator/appbar_always_elevated.xml new file mode 100644 index 0000000000..c8378c1c29 --- /dev/null +++ b/OsmAnd/res/animator/appbar_always_elevated.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml b/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml index 34e329d8a3..65fc03610c 100644 --- a/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml +++ b/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml @@ -33,7 +33,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" - android:maxLines="1" + android:maxLines="2" android:textAppearance="@style/TextAppearance.ListItemTitle" tools:text="Some title"/> diff --git a/OsmAnd/res/layout/enough_space_warning_card.xml b/OsmAnd/res/layout/enough_space_warning_card.xml index a39076eb99..d572d20366 100644 --- a/OsmAnd/res/layout/enough_space_warning_card.xml +++ b/OsmAnd/res/layout/enough_space_warning_card.xml @@ -36,6 +36,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:letterSpacing="@dimen/text_button_letter_spacing" + android:paddingBottom="@dimen/measurement_tool_menu_title_padding_bottom" android:text="@string/export_not_enough_space" android:textColor="?android:textColorPrimary" android:textSize="@dimen/default_list_text_size" diff --git a/OsmAnd/res/layout/follow_track_options.xml b/OsmAnd/res/layout/follow_track_options.xml index 0877fcc307..4523599e6c 100644 --- a/OsmAnd/res/layout/follow_track_options.xml +++ b/OsmAnd/res/layout/follow_track_options.xml @@ -75,7 +75,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/route_info_bg" - android:foreground="@drawable/bg_contextmenu_shadow" android:foregroundGravity="top|fill_horizontal"> + + + android:background="@android:color/transparent" + android:stateListAnimator="@animator/appbar_always_elevated"> diff --git a/OsmAnd/res/values-ar/phrases.xml b/OsmAnd/res/values-ar/phrases.xml index a1560af76a..bc5277a310 100644 --- a/OsmAnd/res/values-ar/phrases.xml +++ b/OsmAnd/res/values-ar/phrases.xml @@ -117,7 +117,7 @@ شرطة نوع العمل الفني : نافورة نجار - سباك + سباكة معرض سيارات ورشة تصليح السيارات خراطيش الطابعة @@ -702,7 +702,7 @@ تجميل الاظافر صالون تدليك صالون وشم - التنظيف الجاف + مغسلة ملابس غسيل الملابس تأجير سيارات مشاركة السيارات @@ -1369,7 +1369,7 @@ طب الأطفال الاجتماعي طب التوليد (القيصرية) طب الإدمان - بصريات + نظارات/بصريات علاج النطق الطب الرياضي الوخز بالإبر @@ -1739,7 +1739,7 @@ جصاص طبقة الباركيه دهان - أخصائي بصريات + محل نظارات/بصريات حرفة الصناعات المعدنية ناسخ مفاتيح صانع ومصلح المفاتيح @@ -2116,7 +2116,7 @@ سهل مقياس RTSA متضرره من الرياح - فيضة/شجيرات ربيعية + فيضة/روضة براح مستوى العبور معبر خط القطار diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml index 2ac5fa1dfd..e7f575f576 100644 --- a/OsmAnd/res/values-ar/strings.xml +++ b/OsmAnd/res/values-ar/strings.xml @@ -3940,25 +3940,25 @@ انتهت صلاحية اشتراك OsmAnd Live تم إيقاف اشتراك OsmAnd Live مؤقتًا اشتراك OsmAnd Live معلق - تسجيل الدخول ل OpenStreetMap - تسجيل الدخول إلى OpenStreetMap.org + تسجيل الدخول لخريطة الشارع المفتوح + تسجيل الدخول إلى خريطة الشارع المفتوح قم بتسجيل الدخول لرفع التغييرات الجديدة أو المعدلة. \n -\nإما باستخدام OAuth أو باستخدام اسم المستخدم وكلمة المرور. +\nإما باستخدام المصادقة أو باستخدام اسم المستخدم وكلمة المرور. سجل الدخول باسم المستخدم وكلمة المرور الحساب - اسم المستخدم + تسجيل الدخول \"إمكانية التتبع\" تعني أن التتبع لا يظهر في أي قوائم عامة ولكن نقاط التتبع المعالجة مع طوابع زمنية صادرة عنها(التي لا يمكن أن تكون مرتبطة بك مباشرة) ستظهر خلال التنزيلات من واجهة برمجة التطبيقات GPS API العامة. سجل تاريخ العلامات أرسل ملف GPX إلى OpenStreetMap أدخل العلامات مفصولة بفاصلة. \"خاص\"يعني أن التتبع لن يظهر في أي قوائم عامة ، ولكن نقاط التتبع الصادرة عنه ستظل متاحة من خلال واجهة برمجة تطبيقات GPS API العامة بدون طوابع زمنية ولكن لن يتم ترتيبها ترتيبًا زمنيًا. - سجّل الدخول باستخدام OpenStreetMap + تسجيل الدخول عبر الشارع المفتوح عام يعني أنه سيتم عرض التتبع بشكل عام في تتبعات GPS الخاصة بك وفي قوائم تتبع GPS العامة. البيانات المقدمة عبر API لا تشير إلى صفحة التتبع الخاصة بك. الطوابع الزمنية لنقاط التتبع غير متاحة من خلال واجهة برمجة تطبيقات GPS API العامة، والنقاط غير مرتبة ترتيبًا زمنيًا. ومع ذلك، لا يزال المستخدمون الآخرون قادرين على تنزيل التتبع الأولي من قائمة التتبع العامة وأي طوابع زمنية مضمنة فيه. متعرف عليه يعني أنه سيتم عرض التتبع بشكل عام في تتبعات GPS الخاصة بك وفي قوائم تتبع GPS العامة، أي سيتمكن المستخدمون الآخرون من تنزيل التتبع الأولي وربطه باسم المستخدم الخاص بك. ستشير البيانات التي يتم تقديمها عبر API نقاط التتبع إلى صفحة التتبع الأصلية الخاصة بك. الطوابع الزمنية لنقاط التتبع متاحة من خلال واجهة برمجة تطبيقات GPS API العامة. أغلاق ملاحظة OSM تعليق ملاحظة OSM - قم بتسجيل الدخول باستخدام طريقة OAuth الآمنة أو استخدم اسم المستخدم وكلمة المرور. + قم بتسجيل الدخول باستخدام طريقة المصادقة الآمنة أو استخدم اسم المستخدم وكلمة المرور. إضافة صورة سجل في \nOpenPlaceReviews.org @@ -3984,4 +3984,6 @@ حدد العناصر التي سيتم استيرادها. قم بالتبديل لاستخدام dev.openstreetmap.org بدلاً من openstreetmap.org لاختبار تحميل OSM Note / POI / GPX. استخدم dev.openstreetmap.org + لا يمكن تحميل الصورة، من فضلك، حاول مرة أخرى في وقت لاحق + تحديد صورة \ No newline at end of file diff --git a/OsmAnd/res/values-eo/strings.xml b/OsmAnd/res/values-eo/strings.xml index d6716c22f0..b85578312f 100644 --- a/OsmAnd/res/values-eo/strings.xml +++ b/OsmAnd/res/values-eo/strings.xml @@ -3955,12 +3955,12 @@ Ensaluti al OpenStreetMap Ensaluti al OpenStreetMap.org Ensaluti per OpenStreetMap - Ensalutu por alŝuti novajn aŭ modifitajn ŝanĝojn + Vi devas ensaluti por alŝuti novajn aŭ modifitajn ŝanĝojn. \n -\naŭ per la sekura metodo OAuth aŭ per enigi uzantnomon kaj pasvorton. - Ensaluti per uzantnomo kaj pasvorto +\nVi povas ensaluti per la sekura metodo OAuth aŭ per enigi salutnomon kaj pasvorton. + Ensaluti per salutnomo kaj pasvorto Konto - Uzantnomo + Salutnomo Historio de markoj Sendi GPX‑dosieron al OpenStreetMap Enigu etikedojn disigitajn per komo (,). @@ -3970,11 +3970,11 @@ “Spurebla” signifas, ke la GPX‑spuro ne montriĝos en publikaj listoj, sed pritraktitaj punktoj kun tempindikoj el ĝi (kiuj ne povos esti senpere ligitaj al vi) estos elŝuteblaj per la publika API GPS. Fermi OSM-rimarkon Komenti OSM-rimarkon - Ensalutii per la sekura metodo OAuth aŭ per uzi uzantnomon kaj pasvorton. + Vi povas ensalutii per la sekura metodo OAuth aŭ per uzi salutnomon kaj pasvorton. Aldoni foton Registri ĉe \nOpenPlaceReviews.org - Ensaluti en la paĝon de la malferm‑datuma projekto OpenPlaceReviews.org por alŝuti fotojn. + Fotoj estas liverataj de la malferm‑datuma projekto OpenPlaceReviews.org. Por alŝuti viajn fotojn, vi devas enlasaluti en la retejon. Krei novan konton Mi jam havas konton Serĉi en historio @@ -3996,4 +3996,6 @@ Elektu elementojn por enporti. Baskuli al dev.openstreetmap.org anstataŭ openstreetmap.org por testi alŝuti OSM‑rimarkojn, interesejojn, GPX‑spurojn. Uzi dev.openstreetmap.org + Ne povas alŝuti la bildon, reprovu poste + Elektu bildon \ 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 787dbafe21..e92749d57b 100644 --- a/OsmAnd/res/values-es-rAR/strings.xml +++ b/OsmAnd/res/values-es-rAR/strings.xml @@ -1528,7 +1528,7 @@ Enfoque hiperfocal Profundidad de campo extendido (EDOF) Enfoque al infinito - Modo de enfoque macro (primer plano) + Enfoque macro (primer plano) Enfoque continuo Reproducir sonido al fotografiar Define el sonido o silencio al fotografiar. @@ -3458,7 +3458,7 @@ Velocidad mínima Precisión mínima Desplazamiento mínimo - Restablecer ajustes del complemento a valores predefinidos + Restablecer complemento a valores predefinidos División de grabación Usar la aplicación del sistema Reproducir sonido al fotografiar diff --git a/OsmAnd/res/values-es-rUS/phrases.xml b/OsmAnd/res/values-es-rUS/phrases.xml index 1e57d33830..9980bdccd7 100644 --- a/OsmAnd/res/values-es-rUS/phrases.xml +++ b/OsmAnd/res/values-es-rUS/phrases.xml @@ -400,7 +400,7 @@ Buceo Submarinismo Canódromo - Equitación + Deportes ecuestres Hockey sobre césped Golf Gimnasia @@ -1234,7 +1234,7 @@ Paso a nivel peatonal;Cruce peatonal ferroviario Paso a nivel;Cruce ferroviario Observatorio de aves - Equitación + Centro ecuestre Jardín Césped Pastizal diff --git a/OsmAnd/res/values-es-rUS/strings.xml b/OsmAnd/res/values-es-rUS/strings.xml index 085b0608e1..6e8220cde8 100644 --- a/OsmAnd/res/values-es-rUS/strings.xml +++ b/OsmAnd/res/values-es-rUS/strings.xml @@ -1528,7 +1528,7 @@ Enfoque hiperfocal Profundidad de campo extendido (EDOF) Enfoque al infinito - Modo de enfoque macro (primer plano) + Enfoque macro (primer plano) Enfoque continuo Reproducir sonido al fotografiar Define el sonido o silencio al fotografiar. @@ -3458,7 +3458,7 @@ Velocidad mínima Precisión mínima Desplazamiento mínimo - Restablecer ajustes del complemento a valores predefinidos + Restablecer complemento a valores predefinidos División de grabación Usar la aplicación del sistema Reproducir sonido al fotografiar @@ -3948,17 +3948,17 @@ Emergencia Comodidad El archivo ya fue importado en OsmAnd - Nombre de usuario - Ingresar nombre de usuario y contraseña + Ingresar + Ingresar con usuario y contraseña Hay un problema con la suscripción. Pulsa el botón para ir a los ajustes de la suscripción de Google Play y corregir el método de pago. La suscripción a OsmAnd Live se ha pausado La suscripción a OsmAnd Live está en espera La suscripción a OsmAnd Live ha caducado Ingresar con OpenStreetMap Enviar archivo GPX a OpenStreetMap - Inicia sesión para subir los cambios nuevos o modificados, + Debes iniciar sesión para subir los cambios nuevos o modificados. \n -\nya sea con OAuth o usando el nombre de usuario y contraseña. +\nPuedes ingresar usando el método seguro de OAuth o con nombre de usuario y contraseña. Debes añadir al menos dos puntos Historial de marcadores Gestionar suscripción @@ -3973,10 +3973,10 @@ Añadir foto Ya tengo cuenta Crear nueva cuenta - Ingresa en el sitio web del proyecto de datos abiertos OpenPlaceReviews.org para subir más fotos. + Las fotos son provistas por el proyecto de datos abiertos OpenPlaceReviews.org. Para subir fotos debes registrarte en el sitio web. Registrarse en \nOpenPlaceReviews.org - Ingresa usando el método seguro de OAuth o usa el nombre de usuario y contraseña. + Puedes iniciar sesión con el método seguro de OAuth o usar el nombre de usuario y contraseña. Comentar nota de OSM Cerrar nota de OSM Historial de búsqueda @@ -3994,4 +3994,11 @@ \nOpenPlaceReviews (fotos de PDI); \nMapillary (imágenes a nivel de calle); \nWeb / Wikimedia (fotos de PDI incluidas en los datos de OpenStreetMap). + Cambia a la versión para desarrolladores «dev.openstreetmap.org» en lugar de openstreetmap.org para probar la carga de PDI, notas de OSM y archivos GPX. + Usar dev.openstreetmap.org + Elegir imagen + Marca los elementos que serán importados. + Marca los grupos que serán importados. + No se puede subir la imagen, inténtalo más tarde + Separador \ No newline at end of file diff --git a/OsmAnd/res/values-eu/strings.xml b/OsmAnd/res/values-eu/strings.xml index 42a06769a4..3a3565a83b 100644 --- a/OsmAnd/res/values-eu/strings.xml +++ b/OsmAnd/res/values-eu/strings.xml @@ -3815,7 +3815,7 @@ Area honi dagokio: %1$s x %2$s Zehaztu lorratz orokorreko grabazio erregistroaren tartea (mapan \'Bidaia grabaketa\' trepetaren bidez aktibatuta dago). Ireki gordetako lorratza \"Jarraigarria\" esan nahi du lorratza ez dela inolako zerrenda publikoetan agertzen, baina prozesatutako puntuak bere denbora markekin (zurekin zuzenean lotu ezin direnak) GPS API publikoaren deskargen bidez agertzen dira. - + Prestazioa OpenStreetMap-eko saio hasiera OpenStreetMap.org-eko saio hasiera @@ -3876,7 +3876,7 @@ Area honi dagokio: %1$s x %2$s \nbai OAuth-ekin edo zure erabiltzaile izena eta pasahitza erabiliz. Hasi saioa erabiltzaile izenarekin eta pasahitzarekin Kontua - Erabiltzaile-izena + Hasi saioa Kudeatu harpidetza Arazo bat dago zure harpidetzarekin. Egin klik botoian Google Play harpidetzaren ezarpenetara joateko zure ordainketa-metodoa konpontzeko. OsmAnd Live harpidetza iraungi egin da @@ -3998,4 +3998,6 @@ Area honi dagokio: %1$s x %2$s Markatzaileen historia Bidali GPX fitxategia OpenStreetMap-era Egin iruzkina OSM oharrean + Hautatu argazkia + Erabili dev.openstreetmap.org \ No newline at end of file diff --git a/OsmAnd/res/values-gl/strings.xml b/OsmAnd/res/values-gl/strings.xml index 62847375d7..c83b2f6df1 100644 --- a/OsmAnd/res/values-gl/strings.xml +++ b/OsmAnd/res/values-gl/strings.xml @@ -3978,15 +3978,15 @@ Lon %2$s A subscrición do OsmAnd Live foi detida A subscrición do OsmAnd Live está en espera \"Público\" significa que a pista amosarase de xeito público na túa listaxe de pistas e nas listaxes de pistas GPS públicas con marcas de tempo en bruto. Os datos servidos a través da API no farán referencia á túa pácina de pistas. As marcaxes de tempo dos puntos da pista non estarán dispoñíbeis a través da API pública de GPS, aínda que os puntos son ordenados de xeito cronolóxico. - Iniciar a sesión no OpenStreetMap - Iniciar a sesión no OpenStreetMap.org + Iniciar a sesión ó OpenStreetMap + Iniciar a sesión ó OpenStreetMap.org Iniciar a sesión co OpenStreetMap - Inicia a sesión para subir as modificacións novas, + Tes que iniciar sesión para subir as modificacións novas. \n -\nxa sexa co OAuth ou empregando o teu nome de usuario e contrasinal. - Iniciar a sesión co nome de usuario e contrasinal +\nPodes iniciar sesión empregando o método seguro do OAuth ou co nombre de usuario e contrasinal. + Iniciar a sesión co usuario e contrasinal Conta - Nome de usuario + Iniciar sesión Historial de marcaxes Enviar ficheiro GPX ó OpenStreetMap Insire etiquetas separadas por comas. @@ -3995,11 +3995,11 @@ Lon %2$s \"Identificábel\" significa que a pista amosarase de xeito público na túa listaxe de pistas e na listaxe de pistas GPS públicas, é dicir que outros usuarios poderán baixar a pista en bruto e asociala co seu nome de usuario. Os datos servidos a través da API de conxunto de puntos fará referencia á páxina orixinal da pista. Pechar nota do OSM Comentar nota do OSM - Inicia a sesión co método seguro do OAuth ou emprega o nome de usuario e contrasinal. + Podes iniciar a sesión co método seguro do OAuth ou emprega o nome de usuario e contrasinal. Engadir imaxe Rexistrarse no \nOpenPlaceReviews.org - Inicia a sesión no sitio web do proxecto de datos abertos do OpenPlaceReviews.org para subir máis imaxes. + As imaxes son fornecidas polo proxecto de datos abertos do OpenPlaceReviews.org. Para subir a túas imaxes precisas rexistrarte no sitio web. Crear nova conta Xa teño unha conta Historial de procura @@ -4011,4 +4011,16 @@ Lon %2$s Necesario para importar O teu dispositivo só ten %1$s libre. Por favor, libera algo de espazo ou desmarca algúns elementos a exportar. Sen espazo dabondo + Non é posíbel subir a imaxe, por favor, téntao novamente máis tarde + Seleccionar imaxe + Seleccionar os grupos que serán importados. + Seleccionar os elementos que serán importados. + Engadir ó Mapillary + Engadir ó OpenPlaceReviews + Mudar á versión para desenvolvedores dev.openstreetmap.org no canto do openstreetmap.org para probar a carga de PDI, notas do OSM e ficheiros GPX. + Empregar dev.openstreetmap.org + O OsmAnd amosa imaxes de diferentes fontes: +\nOpenPlaceReviews - imaxes POI; +\nMapillary - maxes a nivel de rúa; +\nWeb / Wikimedia - imaxes POI especificadas nos datos do OpenStreetMap. \ No newline at end of file diff --git a/OsmAnd/res/values-it/strings.xml b/OsmAnd/res/values-it/strings.xml index 76425946dd..a8c2b32d25 100644 --- a/OsmAnd/res/values-it/strings.xml +++ b/OsmAnd/res/values-it/strings.xml @@ -3995,6 +3995,6 @@ Seleziona gli oggetti da importare. Per testare l\'upload di Note OSM / PDI / GPX, invece di utilizzare invece di openstreetmap.org spostati su dev.openstreetmap.org. Utilizza dev.openstreetmap.org - Impossibile caricare l\'immagine, riprovare in seguito. + Impossibile caricare l\'immagine, riprovare in seguito Scegli la foto \ No newline at end of file diff --git a/OsmAnd/res/values-lv/strings.xml b/OsmAnd/res/values-lv/strings.xml index 2c392c4614..7e38654e34 100644 --- a/OsmAnd/res/values-lv/strings.xml +++ b/OsmAnd/res/values-lv/strings.xml @@ -3214,7 +3214,7 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Lietotāja vārds un parole Šie iestatījumi ir globāli un darbosies visos profilos OSM rediģēšana - Skatiet vēl neaugšupielādētos labojumus vai OSM kļūdas %1$s mapē. Augšupielādētie punkti vairs netiks rādīti. + Skatiet neaugšupielādētos labojumus vai OSM kļūdas %1$s mapē. Augšupielādētie punkti vairs netiek rādīti OsmAnd lietotnē. OSM Ikona, kas redzama naviģējot vai pārvietojoties. Ikona, kas redzama pārtraukumos. @@ -3283,7 +3283,7 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Pierakstīties ar OpenStreetMap Lai sūtītu labojumus vai jaunus datus, ir nepieciešams pierakstīties. \n -\nJūs varat pierakstīties, lietojot OAuth vai ar savu lietotāja vārdu un paroli. +\nJūs varat pierakstīties, lietojot drošo OAuth vai ar savu lietotāja vārdu un paroli. Pierakstīties ar lietotāja vādu un paroli Konts Lietotājs @@ -3297,11 +3297,11 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Ievadiet tagus, atdalītus ar komatu. Aizvērt OSM Note Komentēt OSM Note - Ielogojieties lietojot drošo OAuth metodi vai savu lietotāja vārdu un paroli. + Ielogojieties ar drošo OAuth metodi vai savu lietotāja vārdu un paroli. Pievienot foto Reģistrēties \nOpenPlaceReviews.org - Lai augšupielādētu vēl fotogrāfijas, ielogojieties atvērtā koda projekta OpenPlaceReviews.org vietnē. + Fotogrāfijas nodrošina atvērtā koda projekts OpenPlaceReviews.org. Lai augšupielādētu savas fotogrāfijas, pierakstieties interneta vietnē. Izveidot jaunu kontu Simboli Sports @@ -3324,4 +3324,44 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Nepieciešams importam Ierīcē ir tikai %1$s brīvās vietas. Lūdzu atbrīvojiet vietu vai izvēlieties mazāk detaļu eksportam. Nepietiek brīvās vietas + Visi iepriekšējie segmenti tiks pārrēķināti, lietojot izvēlēto profilu. + Atvērt saglabātu treku + Saglabāts + Nepieciešami vismaz divi punkti. + Atkārtot + Labots + Vārdi: Z - A + Vārdi: A -Z + Starta un finiša ikonas + Paldies par \'Contour lines\' pirkumu + Izvairīties no gājēju celiņiem + Izvairīties no gājēju celiņiem + Izstrāde + OsmAnd Live dati + OsmAnd Live dati + Divu-fāžu maršrutēšana auto navigācijai. + Pārslēgties uz Java (drošs) publiskā transporta maršruta aprēķināšanai + Ielogoties ar OAuth, lai lietotu osmedit iespējas + Ielogoties ar OAuth + Notīrīt OpenStreetMap OAuth token + Izlogošanās veiksmīga + Fails jau ir importēts + Lietot 2-fāžu A* maršrutēšanas algoritmu + Grafika + %1$s dati ir pieejami ceļiem, ir nepieciešams pārrēķināt maršrutu, lietojot \"maršrutēt caur punktiem\". + Gaidiet maršruta pārrēķināšanu. +\nGrafika būs redzama pēc brīža. + Vietējās kartes + %1$s — %2$s + Sprauga + Speciālais + Transports + Serviss + \"Publisks\" nozīmē, ka trase ir redzama jūsu GPS trasēs un GPS trašu publiskajos sarakstos, un publisko trašu sarakstos ar laika atzīmēm pamatformā. Dati caur API neatspoguļo jūsu trases lapu. Trašu punktu laika atzīmes nav pieejams caur publisko GPS API un trašu punkti nav hronoloģiski sakārtoti. + Nevar augšupielādēt bildi, lūdzu mēģiniet vēlreiz + Izvēlieties bildi + Izvēlieties grupas, ko importēt. + Izvēlieties lietas, ko importēt. + Lietot dev.openstreetmap.org nevis openstreetmap.org OSM piezīmju/ POI / GPX augšuielādei un testēšanai. + Lietot dev.openstreetmap.org \ No newline at end of file diff --git a/OsmAnd/res/values-nb/strings.xml b/OsmAnd/res/values-nb/strings.xml index 5863aba29d..c2f36f8a9e 100644 --- a/OsmAnd/res/values-nb/strings.xml +++ b/OsmAnd/res/values-nb/strings.xml @@ -3919,4 +3919,8 @@ Søkehistorikk Kajakk Motorbåt + Velg bilde + Legg til i Mapillary + Legg til i OpenPlaceReviews + Bruk dev.openstreetmap.org \ No newline at end of file diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index cd4664a6dd..d18f8ccd4a 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -3970,14 +3970,14 @@ Público significa que o rastreamento será mostrado publicamente nos Seus traços de GPS e em listas públicas de rastreamento de GPS. Os dados servidos através da API não fazem referência à sua página de rastreamento. As marcas temporais dos pontos de rastreamento não estão disponíveis através da API pública do GPS e os pontos não estão ordenados cronologicamente. No entanto, outros utilizadores ainda são capazes de descarregar o rastreamento bruto da lista pública de rastreamento e de quaisquer carimbos de tempo contidos nela. Privado significa que o rastreamento não aparecerá em nenhuma listagem pública, mas os pontos de rastreamento dele ainda estarão disponíveis através da API pública do GPS sem carimbos de tempo, mas não serão ordenados cronologicamente. Identificável significa que o rastreamento será mostrado publicamente nos Seus traços de GPS e em listas públicas de rastreamento de GPS, ou seja, outros utilizadores serão capazes de descarregar o rastreamento bruto e associá-lo ao seu nome de utilizador. Os dados servidos através da API dos pontos de rastreamento referenciarão à sua página de rastreamento original. As marcas de tempo dos pontos de rastreamento estão disponíveis através da API pública do GPS. - Rastreável significa que o rastreamento não aparecerá em nenhuma lista pública, mas os pontos de rastreamento a partir dele ainda estarão disponíveis através da API pública do GPS com carimbos de tempo. Outros utilizadores só poderão descarregar pontos de rastreamento processados do seu rastreamento que não podem ser associados diretamente a si. + \"Rastreável\" significa que o rastreamento não aparece em nenhuma listagem pública, mas pontos de rastreamento processados com carimbos de tempo a partir dele (que não podem ser associados diretamente a si) fazem através de descarregadas da API pública do GPS. Fechar nota do OSM Comentário de nota do OSM - Faça login a usar o método seguro OAuth ou use o seu nome de utilizador e a palavra-passe. + Pode fazer login pelo método seguro OAuth ou use o seu nome de utilizador e a palavra-passe. Adicionar fotos Cadastre-se em \nOpenPlaceReviews.org - Faça login no site do projeto de dados abertos OpenPlaceReviews.org para enviar ainda mais fotos. + As fotos são fornecidas pelo projeto de dados abertos OpenPlaceReviews.org. Para enviar as suas fotos precisa cadastrar-se no site. Criar uma conta Já tenho uma conta Histórico de pesquisa @@ -3998,4 +3998,7 @@ Seleccionar os grupos que serão importados. Seleccionar os objectos que serão importados. Utilizar dev.openstreetmap.org + Não é possível enviar a imagem, por favor, tente novamente mais tarde + Selecione a imagem + Mude para usar dev.openstreetmap.org ao invés de openstreetmap.org para testar enviar uma OSM Nota / POI / GPX. \ No newline at end of file diff --git a/OsmAnd/res/values-ru/phrases.xml b/OsmAnd/res/values-ru/phrases.xml index 66cbcb543b..fb1dc5e65d 100644 --- a/OsmAnd/res/values-ru/phrases.xml +++ b/OsmAnd/res/values-ru/phrases.xml @@ -3838,4 +3838,30 @@ Радиолокационная вышка Придорожная стоянка На крыше + Нет + Да + Нет + Да + Нет + Да + Представительство + Офис + Почетный консул + Генеральное консульство + Консульский офис + Во главе с консулом + Резиденция + Нунциатура + Миссия + Верховный комиссариат + Делегация + Филиал + Во главе с послом + Связь + Посольство + Гражданские услуги + Иммиграционные визы + Неиммиграционные визы + Связь + Посольство \ No newline at end of file diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index d577ff6f3c..1ae090253b 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -3168,7 +3168,7 @@ Пользовательский профиль навигации Специальная навигация Сторонняя навигация - "Лыжные гонки северные дисциплины" + Лыжные гонки и северные дисциплины Трассы для северных дисциплин или лыжных гонок. Персональный транспортёр Количество изменений @@ -3796,7 +3796,7 @@ Скачать карты Википедии Эндуро мотоцикл Мотороллер - Закрытая OSM-заметка + Закрытая заметка OSM Инвалидная коляска Карт Инвалидное кресло впереди @@ -3984,4 +3984,12 @@ Выберите импортируемые группы. Выберите импортируемые элементы. Использовать dev.openstreetmap.org + Можно войти в систему с помощью безопасного метода OAuth или используя свои имя пользователя и пароль. + Возникла проблема с подпиской. Нажмите кнопку, чтобы перейти к настройкам подписки Google Play и исправить способ оплаты. + Комментировать заметку OSM + Переключиться на использование dev.openstreetmap.org вместо openstreetmap.org для тестирования отправки заметок OSM / POI / GPX. + Фотографии предоставлены проектом открытых данных OpenPlaceReviews.org. Чтобы отправить свои фотографии, необходимо зарегистрироваться на сайте. + Невозможно отправить изображение, попробуйте позже + Выбор изображения + Каяк \ No newline at end of file diff --git a/OsmAnd/res/values-sc/strings.xml b/OsmAnd/res/values-sc/strings.xml index 60084891a0..dd4ed9cf6b 100644 --- a/OsmAnd/res/values-sc/strings.xml +++ b/OsmAnd/res/values-sc/strings.xml @@ -3948,12 +3948,12 @@ Intra in OpenStreetMap Intra in OpenStreetMap.org Intra cun OpenStreetMap - Intra pro carrigare modìficas noas o mudadas, + Depes intrare pro pòdere carrigare modìficas noas o mudadas. \n -\nimpreende su mètodu seguru OAuth o cun su nùmene de impreadore tuo e sa crae de intrada tua. - Intra cun unu nùmene de impreadore e una crae de intrada +\nPodes intrare impreende su mètodu seguru OAuth o cun s\'identificadore (su nùmene de impreadore) tuo e sa crae de intrada tua. + Imprea un\'identificadore e una crae de intrada Contu - Nùmene impreadore + Identificadore Amministra s\'abbonamentu B\'at unu problema cun s\'abbonamentu tuo. Incarca su butone pro andare a sas impostatziones de sos abbonamentos de Google Play pro acontzare sa manera de pagamentu tua. S\'abbonamentu de OsmAnd Live est iscadidu @@ -3968,11 +3968,11 @@ \"Arrastàbile\" cheret nàrrere chi sa rasta no at a èssere ammustrada in peruna lista pùblica, ma sos puntos suos cun sas datas (no ant a èssere assotziados diretamente cun tie) l\'ant a èssere pro mèdiu de iscarrigamentos dae s\'API GPS pùblica. Serra sa nota de OSM Cummenta sa nota de OSM - Intra impreende su mètodu seguru OAuth o cun su nùmene de impreadore tuo e sa crae de intrada tua. + Podes intrare impreende su mètodu seguru OAuth o cun su nùmene tuo e sa crae de intrada tua. Annanghe una fotografia Registra·ti in \nOpenPlaceReviews.org - Intra in su situ de su progetu a datos abertos OpenPlaceReviews.org. pro carrigare fintzas àteras fotografias. + Sas fotografias benint frunidas dae su progetu a datos abertos OpenPlaceReviews.org. Pro carrigare sas fotografias tuas ti depes registrare a su situ. Crea unu contu nou Tèngio giai unu contu Cronologia de chirca @@ -3994,4 +3994,6 @@ Ischerta sos elementos de importare. Cola a impreare dev.openstreetmap.org in logu de openstreetmap.org pro proare su carrigamentu de sas notas OSM / PDI / GPX. Imprea dev.openstreetmap.org + Impossìbile carrigare s\'immàgine, torra a proare prus a tardu + Ischerta un\'immàgine \ No newline at end of file diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml index dd3386b51e..a9a6d024db 100644 --- a/OsmAnd/res/values-uk/strings.xml +++ b/OsmAnd/res/values-uk/strings.xml @@ -2199,7 +2199,7 @@ Повороти (точки для проходження) на цьому маршруті Точки маршруту, цікаві місця та названі можливості Трек - Максимальна швидкість + Найбільша швидкість Середня швидкість Час в русі Загальний час @@ -3068,7 +3068,7 @@ Дані зібрано Натисніть ще раз для зміни орієнтації мапи Мінімальна швидкість - Максимальна швидкість + Макс. швидкість Усталена швидкість Змінити налаштування усталеної швидкості Встановити мінімальну/максимальну швидкість diff --git a/OsmAnd/res/values/sizes.xml b/OsmAnd/res/values/sizes.xml index c1addc1349..f91581adf6 100644 --- a/OsmAnd/res/values/sizes.xml +++ b/OsmAnd/res/values/sizes.xml @@ -397,6 +397,7 @@ 42dp 128dp 164dp + 68dp 6dp 12dp diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 72fb10e393..2a704b8798 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,7 @@ Thx - Hardy --> + %1$s * %2$s OsmAnd shows photos from several sources:\nOpenPlaceReviews - POI photos;\nMapillary - street-level imagery;\nWeb / Wikimedia - POI photos specified in OpenStreetMap data. Use dev.openstreetmap.org Switch to use "dev.openstreetmap.org" instead of "openstreetmap.org" to testing uploading OSM Note / POI / GPX. @@ -2467,6 +2468,7 @@ French Georgian German + German (casual) Greek Hebrew Hebrew diff --git a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java index e906d7b9bb..2fd12a11ac 100644 --- a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java +++ b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java @@ -27,7 +27,7 @@ public class AnalyticsHelper extends SQLiteOpenHelper { private final static Log LOG = PlatformUtil.getLog(AnalyticsHelper.class); - private final static String ANALYTICS_UPLOAD_URL = "https://test.osmand.net/api/submit_analytics"; + private final static String ANALYTICS_UPLOAD_URL = "https://osmand.net/api/submit_analytics"; private final static String ANALYTICS_FILE_NAME = "analytics.json"; private final static int DATA_PARCEL_SIZE = 500; // 500 events diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index a862182014..8f13681d69 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -66,6 +66,8 @@ import net.osmand.plus.voice.JSTTSCommandPlayerImpl; import net.osmand.plus.voice.MediaCommandPlayerImpl; import net.osmand.plus.voice.TTSCommandPlayerImpl; import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; +import net.osmand.plus.wikivoyage.data.TravelObfHelper; import net.osmand.render.RenderingRulesStorage; import net.osmand.router.RoutingConfiguration; import net.osmand.util.Algorithms; @@ -85,7 +87,6 @@ import java.util.Locale; import java.util.Map; import java.util.Random; -import btools.routingapp.BRouterServiceConnection; import btools.routingapp.IBRouterService; import static net.osmand.plus.AppVersionUpgradeOnInit.LAST_APP_VERSION; @@ -457,11 +458,14 @@ public class AppInitializer implements IProgress { app.mapMarkersHelper = startupInit(new MapMarkersHelper(app), MapMarkersHelper.class); app.searchUICore = startupInit(new QuickSearchHelper(app), QuickSearchHelper.class); app.mapViewTrackingUtilities = startupInit(new MapViewTrackingUtilities(app), MapViewTrackingUtilities.class); - app.travelDbHelper = new TravelDbHelper(app); + + //TODO cleanup after Travel migration complete + app.travelHelper = TravelObfHelper.checkIfObfFileExists(app) ? new TravelObfHelper(app) : new TravelDbHelper(app); if (app.getSettings().SELECTED_TRAVEL_BOOK.get() != null) { - app.travelDbHelper.initTravelBooks(); + app.travelHelper.initTravelBooks(); } - app.travelDbHelper = startupInit(app.travelDbHelper, TravelDbHelper.class); + app.travelHelper = startupInit(app.travelHelper, TravelHelper.class); + app.lockHelper = startupInit(new LockHelper(app), LockHelper.class); app.settingsHelper = startupInit(new SettingsHelper(app), SettingsHelper.class); app.quickActionRegistry = startupInit(new QuickActionRegistry(app.getSettings()), QuickActionRegistry.class); diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 00bdd97e2d..bbe45aab51 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -79,7 +79,7 @@ import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.backup.SettingsHelper; import net.osmand.plus.voice.CommandPlayer; -import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.router.GeneralRouter; import net.osmand.router.RoutingConfiguration; import net.osmand.router.RoutingConfiguration.Builder; @@ -148,7 +148,7 @@ public class OsmandApplication extends MultiDexApplication { OsmandRegions regions; GeocodingLookupService geocodingLookupService; QuickSearchHelper searchUICore; - TravelDbHelper travelDbHelper; + TravelHelper travelHelper; InAppPurchaseHelper inAppPurchaseHelper; MapViewTrackingUtilities mapViewTrackingUtilities; LockHelper lockHelper; @@ -481,8 +481,8 @@ public class OsmandApplication extends MultiDexApplication { return searchUICore; } - public TravelDbHelper getTravelDbHelper() { - return travelDbHelper; + public TravelHelper getTravelHelper() { + return travelHelper; } public InAppPurchaseHelper getInAppPurchaseHelper() { diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index 5a6a7560a5..00bd656b63 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -76,6 +76,7 @@ import net.osmand.plus.views.layers.MapControlsLayer; import net.osmand.plus.wikipedia.WikipediaDialogFragment; import net.osmand.plus.wikivoyage.WikivoyageWelcomeDialogFragment; import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity; import net.osmand.router.GeneralRouter; import net.osmand.util.Algorithms; @@ -924,9 +925,9 @@ public class MapActivityActions implements DialogProvider { @Override public boolean onContextMenuClick(ArrayAdapter adapter, int itemId, int pos, boolean isChecked, int[] viewCoordinates) { MapActivity.clearPrevActivityIntent(); - TravelDbHelper travelDbHelper = getMyApplication().getTravelDbHelper(); - travelDbHelper.initTravelBooks(); - if (travelDbHelper.getSelectedTravelBook() == null) { + TravelHelper travelHelper = getMyApplication().getTravelHelper(); + travelHelper.initTravelBooks(); + if (travelHelper.getSelectedTravelBook() == null) { WikivoyageWelcomeDialogFragment.showInstance(mapActivity.getSupportFragmentManager()); } else { Intent intent = new Intent(mapActivity, WikivoyageExploreActivity.class); diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index e1317fb630..c42e4de8aa 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -930,11 +930,21 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { stopRecording(activity, false); } + @Override + public void mapActivityResume(MapActivity activity) { + this.mapActivity = activity; + if (Build.VERSION.SDK_INT < 29) { + runAction(activity); + } + } + @Override public void mapActivityResumeOnTop(MapActivity activity) { this.mapActivity = activity; -// ((AudioManager) activity.getSystemService(Context.AUDIO_SERVICE)).registerMediaButtonEventReceiver( -// new ComponentName(activity, MediaRemoteControlReceiver.class)); + runAction(activity); + } + + private void runAction(MapActivity activity) { if (runAction != -1) { takeAction(activity, actionLon, actionLat, runAction); runAction = -1; diff --git a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java index ccd166818a..22e0607c7f 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java +++ b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java @@ -417,7 +417,9 @@ public class AvoidSpecificRoads { AvoidRoadInfo other = (AvoidRoadInfo) obj; return Math.abs(latitude - other.latitude) < 0.00001 && Math.abs(longitude - other.longitude) < 0.00001 - && Algorithms.objectEquals(name, other.name); + && Algorithms.objectEquals(name, other.name) + && Algorithms.objectEquals(appModeKey, other.appModeKey) + && id == other.id; } } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java index 26dfdaefa7..503e52ecf0 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java @@ -14,6 +14,8 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.net.Uri; import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; @@ -27,14 +29,12 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.view.ContextThemeWrapper; import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; - import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.data.Amenity; @@ -42,11 +42,7 @@ import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.data.QuadRect; import net.osmand.osm.io.NetworkUtils; -import net.osmand.plus.OsmAndFormatter; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.R; -import net.osmand.plus.UiUtilities; +import net.osmand.plus.*; import net.osmand.plus.activities.ActivityResultListener; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.FontCache; @@ -69,7 +65,6 @@ import net.osmand.plus.widgets.TextViewEx; import net.osmand.plus.widgets.tools.ClickableSpanTouchListener; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; - import org.apache.commons.logging.Log; import org.openplacereviews.opendb.util.exception.FailedVerificationException; @@ -77,13 +72,7 @@ import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static net.osmand.plus.mapcontextmenu.builders.cards.ImageCard.GetImageCardsTask.GetImageCardsListener; @@ -119,6 +108,8 @@ public class MenuBuilder { private String preferredMapLang; private String preferredMapAppLang; private boolean transliterateNames; + private View view; + private View photoButton; private final OpenDBAPI openDBAPI = new OpenDBAPI(); private String[] placeId = new String[0]; @@ -131,6 +122,14 @@ public class MenuBuilder { @Override public void onPlaceIdAcquired(String[] placeId) { MenuBuilder.this.placeId = placeId; + if (placeId.length < 2) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + photoButton.setVisibility(View.GONE); + } + }); + } } @Override @@ -239,6 +238,7 @@ public class MenuBuilder { } public void build(View view) { + this.view = view; firstRow = true; hidden = false; buildTopInternal(view); @@ -412,7 +412,10 @@ public class MenuBuilder { } }); //TODO This feature is under development - view.setVisibility(View.VISIBLE); + if (!OsmandPlugin.isDevelopment()) { + view.setVisibility(View.GONE); + } + photoButton = view; return view; } @@ -430,7 +433,9 @@ public class MenuBuilder { OnActivityResultListener() { @Override public void onResult(int resultCode, Intent resultData) { - handleSelectedImage(view, resultData.getData()); + if (resultData != null) { + handleSelectedImage(view, resultData.getData()); + } } })); } @@ -443,10 +448,12 @@ public class MenuBuilder { try { inputStream = app.getContentResolver().openInputStream(uri); if (inputStream != null) { - uploadImageToPlace(view, inputStream); + uploadImageToPlace(inputStream); } } catch (Exception e) { LOG.error(e); + String str = app.getString(R.string.cannot_upload_image); + showToastMessage(str); } finally { Algorithms.closeStream(inputStream); } @@ -455,7 +462,7 @@ public class MenuBuilder { t.start(); } - private void uploadImageToPlace(View view, InputStream image) { + private void uploadImageToPlace(InputStream image) { InputStream serverData = new ByteArrayInputStream(compressImage(image)); final String baseUrl = OPRWebviewActivity.getBaseUrl(app); String url = baseUrl + "api/ipfs/image"; @@ -463,26 +470,60 @@ public class MenuBuilder { if (response != null) { int res = 0; try { + StringBuilder error = new StringBuilder(); res = openDBAPI.uploadImage( placeId, baseUrl, OPRWebviewActivity.getPrivateKeyFromCookie(app), OPRWebviewActivity.getUsernameFromCookie(app), - response); + response, error); + if (res != 200) { + showToastMessage(error.toString()); + } else { + //ok, continue + } } catch (FailedVerificationException e) { LOG.error(e); - app.showToastMessage(R.string.cannot_upload_image); + checkTokenAndShowScreen(); } if (res != 200) { //image was uploaded but not added to blockchain - app.showToastMessage(R.string.cannot_upload_image); + checkTokenAndShowScreen(); } else { - app.showToastMessage(R.string.successfully_uploaded_pattern, 1, 1); + String str = app.getString(R.string.successfully_uploaded_pattern, 1, 1); + showToastMessage(str); //refresh the image execute(new GetImageCardsTask(mapActivity, getLatLon(), getAdditionalCardParams(), imageCardListener)); } } else { - app.showToastMessage(R.string.cannot_upload_image); + checkTokenAndShowScreen(); + } + } + + private void showToastMessage(final String str) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Toast.makeText(mapActivity.getBaseContext(), str, Toast.LENGTH_LONG).show(); + } + }); + } + + //This method runs on non main thread + private void checkTokenAndShowScreen() { + final String baseUrl = OPRWebviewActivity.getBaseUrl(app); + final String name = OPRWebviewActivity.getUsernameFromCookie(app); + final String privateKey = OPRWebviewActivity.getPrivateKeyFromCookie(app); + if (openDBAPI.checkPrivateKeyValid(baseUrl, name, privateKey)) { + String str = app.getString(R.string.cannot_upload_image); + showToastMessage(str); + } else { + app.runInUIThread(new Runnable() { + @Override + public void run() { + OprStartFragment.showInstance(mapActivity.getSupportFragmentManager()); + } + }); } } @@ -1186,4 +1227,4 @@ public class MenuBuilder { public static

void execute(AsyncTask task, P... requests) { task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, requests); } -} \ No newline at end of file +} diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java index 29fb2e578d..12b1b0d497 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java @@ -48,7 +48,9 @@ import net.osmand.util.OpeningHoursParser; import org.apache.commons.logging.Log; +import java.io.UnsupportedEncodingException; import java.math.RoundingMode; +import java.net.URLDecoder; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.ArrayList; @@ -784,20 +786,29 @@ public class AmenityMenuBuilder extends MenuBuilder { String wikidataValue = amenity.getAdditionalInfo(Amenity.WIKIDATA); String wikimediaValue = amenity.getAdditionalInfo(Amenity.WIKIMEDIA_COMMONS); if (!Algorithms.isEmpty(imageValue)) { - params.put("osm_image", imageValue); + params.put("osm_image", getDecodedAdditionalInfo(imageValue)); } if (!Algorithms.isEmpty(mapillaryValue)) { - params.put("osm_mapillary_key", mapillaryValue); + params.put("osm_mapillary_key", getDecodedAdditionalInfo(mapillaryValue)); } if (!Algorithms.isEmpty(wikidataValue)) { - params.put(Amenity.WIKIDATA, wikidataValue); + params.put(Amenity.WIKIDATA, getDecodedAdditionalInfo(wikidataValue)); } if (!Algorithms.isEmpty(wikimediaValue)) { - params.put(Amenity.WIKIMEDIA_COMMONS, wikimediaValue); + params.put(Amenity.WIKIMEDIA_COMMONS, getDecodedAdditionalInfo(wikimediaValue)); } return params; } + private String getDecodedAdditionalInfo(String additionalInfo) { + try { + return URLDecoder.decode(additionalInfo, "UTF-8"); + } catch (UnsupportedEncodingException e) { + LOG.error(e); + } + return additionalInfo; + } + private CollapsableView getPoiTypeCollapsableView(final Context context, boolean collapsed, @NonNull final List categoryTypes, final boolean poiAdditional, AmenityInfoRow textRow) { diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/IPFSImageCard.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/IPFSImageCard.java index 0d58add034..63f02d10fe 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/IPFSImageCard.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/IPFSImageCard.java @@ -1,8 +1,8 @@ package net.osmand.plus.mapcontextmenu.builders.cards; + import android.view.View; import androidx.core.content.ContextCompat; -import net.osmand.AndroidNetworkUtils; import net.osmand.PlatformUtil; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; @@ -12,7 +12,6 @@ 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) { @@ -23,6 +22,7 @@ public class IPFSImageCard extends ImageCard { } catch (JSONException e) { LOG.error(e); } + String BASE_URL = mapActivity.getString(R.string.opr_base_url) + "api/ipfs/image-ipfs?cid="; url = BASE_URL + cid; imageHiresUrl = BASE_URL + cid; imageUrl = BASE_URL + cid; diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java index 3650f6d820..74797620f9 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java @@ -27,6 +27,7 @@ import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; @@ -567,11 +568,11 @@ public class MapMarkersHelper { @NonNull public List getGroupsForSavedArticlesTravelBook() { List res = new ArrayList<>(); - TravelDbHelper travelDbHelper = ctx.getTravelDbHelper(); - if (travelDbHelper.getSelectedTravelBook() != null) { - List savedArticles = travelDbHelper.getLocalDataHelper().getSavedArticles(); + TravelHelper travelHelper = ctx.getTravelHelper(); + if (travelHelper.getSelectedTravelBook() != null) { + List savedArticles = travelHelper.getLocalDataHelper().getSavedArticles(); for (TravelArticle art : savedArticles) { - String gpxName = travelDbHelper.getGPXName(art); + String gpxName = travelHelper.getGPXName(art); File path = ctx.getAppPath(IndexConstants.GPX_TRAVEL_DIR + gpxName); LOG.debug("Article group " + path.getAbsolutePath() + " " + path.exists()); MapMarkersGroup search = getMapMarkerGroupById(getMarkerGroupId(path), MapMarkersGroup.GPX_TYPE); diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersGroupsAdapter.java b/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersGroupsAdapter.java index 30639c768b..4cb0b9f690 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersGroupsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersGroupsAdapter.java @@ -37,6 +37,7 @@ import net.osmand.plus.mapmarkers.SelectWptCategoriesBottomSheetDialogFragment; import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment; import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; import java.io.File; import java.text.SimpleDateFormat; @@ -163,11 +164,11 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter savedArticles = travelDbHelper.getLocalDataHelper().getSavedArticles(); + TravelHelper travelHelper = mapActivity.getMyApplication().getTravelHelper(); + if (travelHelper.getSelectedTravelBook() != null) { + List savedArticles = travelHelper.getLocalDataHelper().getSavedArticles(); for (TravelArticle art : savedArticles) { - String gpxName = travelDbHelper.getGPXName(art); + String gpxName = travelHelper.getGPXName(art); File path = mapActivity.getMyApplication().getAppPath(IndexConstants.GPX_TRAVEL_DIR + gpxName); if (path.getAbsolutePath().equals(group.getGpxPath())) { group.setWikivoyageArticle(art); diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java index 7612583067..d27ceba057 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackActivityFragmentAdapter.java @@ -486,7 +486,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener { String title = metadata.getArticleTitle(); String lang = metadata.getArticleLang(); if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) { - return app.getTravelDbHelper().getArticle(title, lang); + return app.getTravelHelper().getArticle(title, lang); } return null; } diff --git a/OsmAnd/src/net/osmand/plus/openplacereviews/OPRWebviewActivity.java b/OsmAnd/src/net/osmand/plus/openplacereviews/OPRWebviewActivity.java index 14fd181f54..62e030ecca 100644 --- a/OsmAnd/src/net/osmand/plus/openplacereviews/OPRWebviewActivity.java +++ b/OsmAnd/src/net/osmand/plus/openplacereviews/OPRWebviewActivity.java @@ -11,19 +11,21 @@ import android.webkit.CookieManager; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.TextView; - import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; - import net.osmand.AndroidUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.OsmandActionBarActivity; import net.osmand.plus.settings.backend.OsmandSettings; +import java.util.ArrayList; +import java.util.List; + public class OPRWebviewActivity extends OsmandActionBarActivity { public static final String KEY_LOGIN = "LOGIN_KEY"; - public static String KEY_TITLE = "TITLE_KEY"; + public static final String KEY_TITLE = "TITLE_KEY"; + private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)"; private WebView webView; private boolean isLogin = false; @@ -43,8 +45,13 @@ public class OPRWebviewActivity extends OsmandActionBarActivity { return getBaseUrl(ctx) + "signup"; } - public static String getFinishUrl(Context ctx) { - return getCookieUrl(ctx); + public static List getFinishUrls(Context ctx) { + String googleOAuthFinishUrl = getBaseUrl(ctx) + "auth?code=4"; + String profileUrl = getCookieUrl(ctx); + List urls = new ArrayList<>(); + urls.add(googleOAuthFinishUrl); + urls.add(profileUrl); + return urls; } public void onCreate(Bundle savedInstanceState) { @@ -76,6 +83,7 @@ public class OPRWebviewActivity extends OsmandActionBarActivity { } }); webView = findViewById(R.id.printDialogWebview); + webView.getSettings().setUserAgentString(USER_AGENT); webView.setWebViewClient(new CloseOnSuccessWebViewClient()); webView.getSettings().setJavaScriptEnabled(true); WebView.setWebContentsDebuggingEnabled(true); @@ -124,8 +132,10 @@ public class OPRWebviewActivity extends OsmandActionBarActivity { public class CloseOnSuccessWebViewClient extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { - if (url.contains(getFinishUrl(OPRWebviewActivity.this)) && isLogin) { - finish(); + for (String furl : getFinishUrls(OPRWebviewActivity.this)) { + if (url.contains(furl) && isLogin) { + finish(); + } } super.onPageFinished(view, url); } diff --git a/OsmAnd/src/net/osmand/plus/openplacereviews/OprStartFragment.java b/OsmAnd/src/net/osmand/plus/openplacereviews/OprStartFragment.java index 00aba01997..d91164ff18 100644 --- a/OsmAnd/src/net/osmand/plus/openplacereviews/OprStartFragment.java +++ b/OsmAnd/src/net/osmand/plus/openplacereviews/OprStartFragment.java @@ -18,6 +18,7 @@ import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import net.osmand.PlatformUtil; +import net.osmand.plus.BuildConfig; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.BaseOsmAndFragment; @@ -74,7 +75,7 @@ public class OprStartFragment extends BaseOsmAndFragment { private void setURLSpan(View v) { String desc = requireContext().getString(R.string.register_on_openplacereviews_desc); SpannableString ss = new SpannableString(desc); - ss.setSpan(new URLSpanNoUnderline("https://" + openPlaceReviewsUrl), desc.indexOf(openPlaceReviewsUrl), + ss.setSpan(new URLSpanNoUnderline(getActivity().getString(R.string.opr_base_url)), desc.indexOf(openPlaceReviewsUrl), desc.indexOf(openPlaceReviewsUrl) + openPlaceReviewsUrl.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); v.findViewById(R.id.start_opr_description).setText(ss); v.findViewById(R.id.start_opr_description).setMovementMethod(LinkMovementMethod.getInstance()); diff --git a/OsmAnd/src/net/osmand/plus/osmedit/opr/OpenDBAPI.java b/OsmAnd/src/net/osmand/plus/osmedit/opr/OpenDBAPI.java index 7c891c25c9..36d6e82b11 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/opr/OpenDBAPI.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/opr/OpenDBAPI.java @@ -68,7 +68,7 @@ public class OpenDBAPI { response.toString().contains(LOGIN_SUCCESS_MESSAGE); } - public int uploadImage(String[] placeId, String baseUrl, String privateKey, String username, String image) throws FailedVerificationException { + public int uploadImage(String[] placeId, String baseUrl, String privateKey, String username, String image, StringBuilder sb) throws FailedVerificationException { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { Security.removeProvider("BC"); Security.addProvider(new BouncyCastleProvider()); @@ -92,11 +92,9 @@ public class OpenDBAPI { List ids = new ArrayList<>(Arrays.asList(placeId)); Map change = new TreeMap<>(); Map images = new TreeMap<>(); - Map outdoor = new TreeMap<>(); - outdoor.put("outdoor", imageResponseList); - images.put("append", outdoor); + images.put("append", imageMap); change.put("version", "increment"); - change.put("images", images); + change.put("images.review", images); edit.put("id", ids); edit.put("change", change); edit.put("current", new Object()); @@ -134,6 +132,7 @@ public class OpenDBAPI { String strCurrentLine; while ((strCurrentLine = br.readLine()) != null) { log.error(strCurrentLine); + sb.append(strCurrentLine); } } return rc; @@ -149,4 +148,4 @@ public class OpenDBAPI { public String cid; public String extension; } -} \ No newline at end of file +} diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java index d805cde0df..7730010808 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java @@ -156,6 +156,7 @@ public class FollowTrackFragment extends ContextMenuScrollFragment implements Ca if (view != null) { ImageButton closeButton = view.findViewById(R.id.close_button); buttonsShadow = view.findViewById(R.id.buttons_shadow); + sortButton = view.findViewById(R.id.sort_button); closeButton.setImageDrawable(getContentIcon(AndroidUtils.getNavigationIconResId(app))); closeButton.setOnClickListener(new View.OnClickListener() { @Override @@ -219,6 +220,7 @@ public class FollowTrackFragment extends ContextMenuScrollFragment implements Ca if (Algorithms.isEmpty(fileName)) { fileName = app.getString(R.string.shared_string_gpx_track); } + sortButton.setVisibility(View.GONE); GPXInfo gpxInfo = new GPXInfo(fileName, file != null ? file.lastModified() : 0, file != null ? file.length() : 0); TrackEditCard importTrackCard = new TrackEditCard(mapActivity, gpxInfo); importTrackCard.setListener(this); @@ -268,6 +270,7 @@ public class FollowTrackFragment extends ContextMenuScrollFragment implements Ca tracksCard = new TracksToFollowCard(mapActivity, list, defaultCategory); tracksCard.setListener(FollowTrackFragment.this); getCardsContainer().addView(tracksCard.build(mapActivity)); + sortButton.setVisibility(View.VISIBLE); } } } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java index bf86c42ee6..13135c0821 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java @@ -10,7 +10,6 @@ public enum ExportSettingsType { GLOBAL(R.string.general_settings_2, R.drawable.ic_action_settings), QUICK_ACTIONS(R.string.configure_screen_quick_action, R.drawable.ic_quick_action), POI_TYPES(R.string.poi_dialog_poi_type, R.drawable.ic_action_info_dark), - SEARCH_HISTORY(R.string.shared_string_search_history, R.drawable.ic_action_history), AVOID_ROADS(R.string.avoid_road, R.drawable.ic_action_alert), FAVORITES(R.string.shared_string_favorites, R.drawable.ic_action_favorite), TRACKS(R.string.shared_string_tracks, R.drawable.ic_action_route_distance), @@ -19,6 +18,7 @@ public enum ExportSettingsType { MULTIMEDIA_NOTES(R.string.audionotes_plugin_name, R.drawable.ic_grouped_by_type), ACTIVE_MARKERS(R.string.map_markers, R.drawable.ic_action_flag), HISTORY_MARKERS(R.string.markers_history, R.drawable.ic_action_flag), + SEARCH_HISTORY(R.string.shared_string_search_history, R.drawable.ic_action_history), CUSTOM_RENDER_STYLE(R.string.shared_string_rendering_style, R.drawable.ic_action_map_style), CUSTOM_ROUTING(R.string.shared_string_routing, R.drawable.ic_action_route_distance), MAP_SOURCES(R.string.quick_action_map_source_title, R.drawable.ic_map), @@ -48,12 +48,13 @@ public enum ExportSettingsType { public boolean isSettingsCategory() { return this == PROFILE || this == GLOBAL || this == QUICK_ACTIONS || this == POI_TYPES - || this == SEARCH_HISTORY || this == AVOID_ROADS; + || this == AVOID_ROADS; } public boolean isMyPlacesCategory() { return this == FAVORITES || this == TRACKS || this == OSM_EDITS || this == OSM_NOTES - || this == MULTIMEDIA_NOTES || this == ACTIVE_MARKERS || this == HISTORY_MARKERS; + || this == MULTIMEDIA_NOTES || this == ACTIVE_MARKERS || this == HISTORY_MARKERS + || this == SEARCH_HISTORY; } public boolean isResourcesCategory() { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/AvoidRoadsSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/AvoidRoadsSettingsItem.java index 61b5f28541..6507ab874e 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/AvoidRoadsSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/AvoidRoadsSettingsItem.java @@ -68,13 +68,9 @@ public class AvoidRoadsSettingsItem extends CollectionSettingsItem(newItems); for (AvoidSpecificRoads.AvoidRoadInfo duplicate : duplicateItems) { - if (shouldReplace) { - LatLon latLon = new LatLon(duplicate.latitude, duplicate.longitude); - if (settings.removeImpassableRoad(latLon)) { - settings.addImpassableRoad(duplicate); - } - } else { - settings.addImpassableRoad(renameItem(duplicate)); + LatLon latLon = new LatLon(duplicate.latitude, duplicate.longitude); + if (settings.removeImpassableRoad(latLon)) { + settings.addImpassableRoad(duplicate); } } for (AvoidSpecificRoads.AvoidRoadInfo avoidRoad : appliedItems) { @@ -87,7 +83,12 @@ public class AvoidRoadsSettingsItem extends CollectionSettingsItem historyEntries = SearchHistoryHelper.getInstance(app).getHistoryEntries(false); - if (!historyEntries.isEmpty()) { - settingsItems.put(ExportSettingsType.SEARCH_HISTORY, historyEntries); - } Map impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads(); if (!impassableRoads.isEmpty()) { settingsItems.put(ExportSettingsType.AVOID_ROADS, new ArrayList<>(impassableRoads.values())); @@ -606,6 +602,10 @@ public class SettingsHelper { markersGroup.setMarkers(markersHistory); myPlacesItems.put(ExportSettingsType.HISTORY_MARKERS, Collections.singletonList(markersGroup)); } + List historyEntries = SearchHistoryHelper.getInstance(app).getHistoryEntries(false); + if (!historyEntries.isEmpty()) { + myPlacesItems.put(ExportSettingsType.SEARCH_HISTORY, historyEntries); + } return myPlacesItems; } @@ -821,10 +821,10 @@ public class SettingsHelper { List routingFilesList = new ArrayList<>(); List renderFilesList = new ArrayList<>(); List multimediaFilesList = new ArrayList<>(); - List tracksFilesList = new ArrayList<>(); List ttsVoiceFilesList = new ArrayList<>(); List voiceFilesList = new ArrayList<>(); List mapFilesList = new ArrayList<>(); + List tracksFilesList = new ArrayList<>(); List avoidRoads = new ArrayList<>(); List globalSettingsItems = new ArrayList<>(); List notesPointList = new ArrayList<>(); @@ -848,7 +848,7 @@ public class SettingsHelper { } else if (fileItem.getSubtype() == FileSubtype.MULTIMEDIA_NOTES) { multimediaFilesList.add(fileItem.getFile()); } else if (fileItem.getSubtype() == FileSubtype.GPX) { - tracksFilesList.add(fileItem.getFile()); + tracksFilesList.add(fileItem); } else if (fileItem.getSubtype().isMap()) { mapFilesList.add(fileItem); } else if (fileItem.getSubtype() == FileSubtype.TTS_VOICE) { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java index 7f72761066..66c40d03cc 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java @@ -60,7 +60,7 @@ class SettingsImporter { try { SettingsItemsFactory itemsFactory = new SettingsItemsFactory(app, itemsJson); List settingsItemList = itemsFactory.getItems(); - getFilesSize(file, settingsItemList); + updateFilesInfo(file, settingsItemList); items.addAll(settingsItemList); } catch (IllegalArgumentException e) { SettingsHelper.LOG.error("Error parsing items: " + itemsJson, e); @@ -81,7 +81,7 @@ class SettingsImporter { return items; } - private void getFilesSize(@NonNull File file, List settingsItemList) throws IOException { + private void updateFilesInfo(@NonNull File file, List settingsItemList) throws IOException { ZipFile zipfile = new ZipFile(file.getPath()); Enumeration zipEnum = zipfile.entries(); while (zipEnum.hasMoreElements()) { @@ -90,7 +90,9 @@ class SettingsImporter { for (SettingsItem settingsItem : settingsItemList) { if (settingsItem instanceof FileSettingsItem && zipEntry.getName().equals(settingsItem.getFileName())) { - ((FileSettingsItem) settingsItem).setSize(size); + FileSettingsItem fileSettingsItem = (FileSettingsItem) settingsItem; + fileSettingsItem.setSize(size); + fileSettingsItem.setLastModified(zipEntry.getTime()); break; } } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java index 090767a493..65863f0e90 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java @@ -22,9 +22,13 @@ public abstract class SettingsItemWriter { public abstract boolean writeToStream(@NonNull OutputStream outputStream) throws IOException; public void writeEntry(String fileName, @NonNull ZipOutputStream zos) throws IOException { - ZipEntry entry = new ZipEntry(fileName); + ZipEntry entry = createNewEntry(fileName); zos.putNextEntry(entry); writeToStream(zos); zos.closeEntry(); } + + public ZipEntry createNewEntry(String fileName) { + return new ZipEntry(fileName); + } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java index d95b6cffea..86b7389f6e 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java @@ -2,15 +2,14 @@ package net.osmand.plus.settings.fragments; import android.content.Context; import android.content.DialogInterface; -import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.widget.ExpandableListView; import android.widget.LinearLayout; +import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -31,6 +30,7 @@ import net.osmand.plus.settings.backend.ExportSettingsCategory; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.fragments.ExportSettingsAdapter.OnItemSelectedListener; import net.osmand.plus.widgets.TextViewEx; +import net.osmand.util.Algorithms; import java.io.File; import java.util.ArrayList; @@ -60,6 +60,7 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem protected ExpandableListView expandableList; protected ExportSettingsAdapter adapter; + protected boolean exportMode; protected boolean nightMode; private boolean wasDrawerDisabled; @@ -73,6 +74,17 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem super.onCreate(savedInstanceState); app = requireMyApplication(); nightMode = !app.getSettings().isLightContent(); + + requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + if (hasSelectedData()) { + showExitDialog(); + } else { + dismissFragment(); + } + } + }); } @Nullable @@ -101,26 +113,15 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem continueBtn = root.findViewById(R.id.continue_button); UiUtilities.setupDialogButton(nightMode, continueBtn, DialogButtonType.PRIMARY, getString(R.string.shared_string_continue)); - continueBtn.setOnClickListener(new View.OnClickListener() { + root.findViewById(R.id.continue_button_container).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - onContinueButtonClickAction(); - } - }); - - ViewTreeObserver treeObserver = buttonsContainer.getViewTreeObserver(); - treeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (buttonsContainer != null) { - ViewTreeObserver vts = buttonsContainer.getViewTreeObserver(); - int height = buttonsContainer.getMeasuredHeight(); - expandableList.setPadding(0, 0, 0, height); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - vts.removeOnGlobalLayoutListener(this); - } else { - vts.removeGlobalOnLayoutListener(this); + if (expandableList.getHeaderViewsCount() <= 1) { + if (hasSelectedData()) { + onContinueButtonClickAction(); } + } else { + expandableList.smoothScrollToPosition(0); } } }); @@ -185,7 +186,11 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showExitDialog(); + if (hasSelectedData()) { + showExitDialog(); + } else { + dismissFragment(); + } } }); } @@ -204,16 +209,25 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem continueBtn.setEnabled(false); } else { updateWarningHeaderVisibility(false); - continueBtn.setEnabled(adapter.hasSelectedData()); + continueBtn.setEnabled(hasSelectedData()); } itemsSizeContainer.setVisibility(View.VISIBLE); } else { updateWarningHeaderVisibility(false); itemsSizeContainer.setVisibility(View.INVISIBLE); - continueBtn.setEnabled(adapter.hasSelectedData()); + continueBtn.setEnabled(hasSelectedData()); } } + public boolean hasSelectedData() { + for (List items : selectedItemsMap.values()) { + if (!Algorithms.isEmpty(items)) { + return true; + } + } + return false; + } + private void updateWarningHeaderVisibility(boolean visible) { if (visible) { if (expandableList.getHeaderViewsCount() < 2) { @@ -255,13 +269,24 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem updateAvailableSpace(); } + protected List getItemsForType(ExportSettingsType type) { + for (SettingsCategoryItems categoryItems : dataList.values()) { + if (categoryItems.getTypes().contains(type)) { + return (List) categoryItems.getItemsForType(type); + } + } + return null; + } + + protected List getSelectedItemsForType(ExportSettingsType type) { + return (List) selectedItemsMap.get(type); + } + @Override - public void onTypeClicked(ExportSettingsCategory category, ExportSettingsType type) { + public void onTypeClicked(ExportSettingsType type) { FragmentManager fragmentManager = getFragmentManager(); if (fragmentManager != null && type != ExportSettingsType.GLOBAL && type != ExportSettingsType.SEARCH_HISTORY) { - List items = (List) dataList.get(category).getItemsForType(type); - List selectedItems = (List) selectedItemsMap.get(type); - ExportItemsBottomSheet.showInstance(type, selectedItems, items, fragmentManager, this); + ExportItemsBottomSheet.showInstance(fragmentManager, type, this, exportMode); } } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java index 93dff80cce..3ced930a17 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java @@ -14,11 +14,15 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; +import net.osmand.GPXUtilities.GPXTrackAnalysis; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.ITileSource; import net.osmand.map.TileSourceManager.TileSourceTemplate; import net.osmand.plus.FavouritesDbHelper.FavoriteGroup; +import net.osmand.plus.GPXDatabase.GpxDataItem; +import net.osmand.plus.GpxDbHelper.GpxDataItemCallback; +import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.SQLiteTileSource; @@ -28,6 +32,7 @@ import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton.Builder; +import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; @@ -47,6 +52,7 @@ import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.backup.FileSettingsItem; +import net.osmand.plus.settings.backend.backup.FileSettingsItem.FileSubtype; import net.osmand.plus.settings.backend.backup.GlobalSettingsItem; import net.osmand.plus.settings.fragments.ExportSettingsAdapter.OnItemSelectedListener; import net.osmand.util.Algorithms; @@ -67,18 +73,43 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { public static final String TAG = ExportItemsBottomSheet.class.getSimpleName(); private static final Log LOG = PlatformUtil.getLog(ExportItemsBottomSheet.class); + private static final String SETTINGS_TYPE_KEY = "settings_type_key"; + private static final String EXPORT_MODE_KEY = "export_mode_key"; + private OsmandApplication app; private UiUtilities uiUtilities; private ExportSettingsType type; - private List allItems; - private List selectedItems = new ArrayList<>(); + private final List allItems = new ArrayList<>(); + private final List selectedItems = new ArrayList<>(); private TextView selectedSize; private ThreeStateCheckbox checkBox; private int activeColorRes; private int secondaryColorRes; + private boolean exportMode; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + exportMode = savedInstanceState.getBoolean(EXPORT_MODE_KEY); + type = ExportSettingsType.valueOf(savedInstanceState.getString(SETTINGS_TYPE_KEY)); + } + Fragment target = getTargetFragment(); + if (target instanceof BaseSettingsListFragment) { + BaseSettingsListFragment fragment = (BaseSettingsListFragment) target; + List items = fragment.getItemsForType(type); + if (items != null) { + allItems.addAll(items); + } + List selectedItemsForType = fragment.getSelectedItemsForType(type); + if (selectedItemsForType != null) { + selectedItems.addAll(selectedItemsForType); + } + } + } @Override public void createMenuItems(Bundle savedInstanceState) { @@ -111,11 +142,18 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { }) .setTag(object); setupBottomSheetItem(builder, object); - item[0] = (BottomSheetItemWithCompoundButton) builder.create(); + item[0] = builder.create(); items.add(item[0]); } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(EXPORT_MODE_KEY, exportMode); + outState.putString(SETTINGS_TYPE_KEY, type.name()); + } + private BaseBottomSheetItem createTitleItem() { LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); View view = themedInflater.inflate(R.layout.settings_group_title, null); @@ -153,7 +191,30 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); - selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selectedItems.size(), allItems.size())); + String description; + if (type == ExportSettingsType.OFFLINE_MAPS && !selectedItems.isEmpty()) { + String size = AndroidUtils.formatSize(app, calculateSelectedItemsSize()); + String selected = getString(R.string.ltr_or_rtl_combine_via_slash, selectedItems.size(), allItems.size()); + description = getString(R.string.ltr_or_rtl_combine_via_comma, selected, size); + } else { + description = getString(R.string.ltr_or_rtl_combine_via_slash, selectedItems.size(), allItems.size()); + } + selectedSize.setText(description); + } + + private long calculateSelectedItemsSize() { + long itemsSize = 0; + for (int i = 0; i < allItems.size(); i++) { + Object object = allItems.get(i); + if (selectedItems.contains(object)) { + if (object instanceof FileSettingsItem) { + itemsSize += ((FileSettingsItem) object).getSize(); + } else if (object instanceof File) { + itemsSize += ((File) object).length(); + } + } + } + return itemsSize; } private void updateItems(boolean checked) { @@ -184,15 +245,13 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { dismiss(); } - public static void showInstance(@NonNull ExportSettingsType type, List selectedItems, List allItems, @NonNull FragmentManager fm, @Nullable Fragment target) { + public static void showInstance(@NonNull FragmentManager fm, @NonNull ExportSettingsType type, + @NonNull BaseSettingsListFragment target, boolean exportMode) { try { if (!fm.isStateSaved() && fm.findFragmentByTag(TAG) == null) { ExportItemsBottomSheet fragment = new ExportItemsBottomSheet(); fragment.type = type; - fragment.allItems = (List) allItems; - if (selectedItems != null) { - fragment.selectedItems.addAll(selectedItems); - } + fragment.exportMode = exportMode; fragment.setTargetFragment(target, 0); fragment.show(fm, TAG); } @@ -211,7 +270,7 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { return null; } - private void setupBottomSheetItem(BottomSheetItemWithCompoundButton.Builder builder, Object object) { + private void setupBottomSheetItem(Builder builder, Object object) { if (object instanceof ApplicationModeBean) { ApplicationModeBean modeBean = (ApplicationModeBean) object; String profileName = modeBean.userProfileName; @@ -254,10 +313,11 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setTitle(tileSource.getName()); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_map, activeColorRes)); } else if (object instanceof File) { - setupBottomSheetItemForFile(builder, (File) object); + File file = (File) object; + setupBottomSheetItemForFile(builder, file, file.lastModified(), file.length()); } else if (object instanceof FileSettingsItem) { - FileSettingsItem fileSettingsItem = (FileSettingsItem) object; - setupBottomSheetItemForFile(builder, fileSettingsItem.getFile()); + FileSettingsItem item = (FileSettingsItem) object; + setupBottomSheetItemForFile(builder, item.getFile(), item.getLastModified(), item.getSize()); } else if (object instanceof AvoidRoadInfo) { AvoidRoadInfo avoidRoadInfo = (AvoidRoadInfo) object; builder.setTitle(avoidRoadInfo.name); @@ -275,6 +335,9 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setTitle(group.getDisplayName(app)); int color = group.getColor() == 0 ? ContextCompat.getColor(app, R.color.color_favorite) : group.getColor(); builder.setIcon(uiUtilities.getPaintedIcon(R.drawable.ic_action_folder, color)); + int points = group.getPoints().size(); + String itemsDescr = getString(R.string.shared_string_gpx_points); + builder.setDescription(getString(R.string.ltr_or_rtl_combine_via_colon, itemsDescr, points)); } else if (object instanceof GlobalSettingsItem) { GlobalSettingsItem globalSettingsItem = (GlobalSettingsItem) object; builder.setTitle(globalSettingsItem.getPublicName(app)); @@ -285,9 +348,12 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setTitle(getString(R.string.map_markers)); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_flag, activeColorRes)); } else if (ExportSettingsType.HISTORY_MARKERS.name().equals(markersGroup.getId())) { - builder.setTitle(getString(R.string.map_markers)); + builder.setTitle(getString(R.string.markers_history)); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_history, activeColorRes)); } + int selectedMarkers = markersGroup.getMarkers().size(); + String itemsDescr = getString(R.string.shared_string_items); + builder.setDescription(getString(R.string.ltr_or_rtl_combine_via_colon, itemsDescr, selectedMarkers)); } else if (object instanceof HistoryEntry) { HistoryEntry historyEntry = (HistoryEntry) object; builder.setTitle(historyEntry.getName().getName()); @@ -295,8 +361,8 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { } } - private void setupBottomSheetItemForFile(Builder builder, File file) { - FileSettingsItem.FileSubtype fileSubtype = FileSettingsItem.FileSubtype.getSubtypeByPath(app, file.getPath()); + private void setupBottomSheetItemForFile(Builder builder, File file, long lastModified, long size) { + FileSubtype fileSubtype = FileSubtype.getSubtypeByPath(app, file.getPath()); builder.setTitle(file.getName()); if (file.getAbsolutePath().contains(IndexConstants.RENDERERS_DIR)) { builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_map_style, activeColorRes)); @@ -304,6 +370,8 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_route_distance, activeColorRes)); } else if (file.getAbsolutePath().contains(IndexConstants.GPX_INDEX_DIR)) { builder.setTitle(GpxUiHelper.getGpxTitle(file.getName())); + builder.setTag(file); + builder.setDescription(getTrackDescr(file, lastModified, size)); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_route_distance, activeColorRes)); } else if (file.getAbsolutePath().contains(IndexConstants.AV_INDEX_DIR)) { int iconId = AudioVideoNotesPlugin.getIconIdForRecordingFile(file); @@ -316,6 +384,85 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { || fileSubtype == FileSettingsItem.FileSubtype.VOICE) { builder.setTitle(FileNameTranslationHelper.getFileNameWithRegion(app, file.getName())); builder.setIcon(uiUtilities.getIcon(fileSubtype.getIconId(), activeColorRes)); + + if (fileSubtype.isMap()) { + String mapDescription = getMapDescription(file); + String formattedSize = AndroidUtils.formatSize(app, size); + if (mapDescription != null) { + builder.setDescription(getString(R.string.ltr_or_rtl_combine_via_star, mapDescription, formattedSize)); + } else { + builder.setDescription(formattedSize); + } + } } } + + private final GpxDataItemCallback gpxDataItemCallback = new GpxDataItemCallback() { + @Override + public boolean isCancelled() { + return false; + } + + @Override + public void onGpxDataItemReady(GpxDataItem item) { + for (BaseBottomSheetItem bottomSheetItem : items) { + if (Algorithms.objectEquals(item.getFile(), bottomSheetItem.getTag())) { + ((BottomSheetItemWithDescription) bottomSheetItem).setDescription(getTrackDescrForDataItem(item)); + break; + } + } + } + }; + + private String getTrackDescr(@NonNull File file, long lastModified, long size) { + String folder = ""; + File parent = file.getParentFile(); + if (parent != null) { + folder = Algorithms.capitalizeFirstLetter(parent.getName()); + } + if (exportMode) { + GpxDataItem dataItem = getDataItem(file, gpxDataItemCallback); + if (dataItem != null) { + return getTrackDescrForDataItem(dataItem); + } + } else { + String date = OsmAndFormatter.getFormattedDate(app, lastModified); + String formattedSize = AndroidUtils.formatSize(app, size); + String descr = getString(R.string.ltr_or_rtl_combine_via_bold_point, folder, date); + return getString(R.string.ltr_or_rtl_combine_via_comma, descr, formattedSize); + } + return null; + } + + private String getTrackDescrForDataItem(@NonNull GpxDataItem dataItem) { + GPXTrackAnalysis analysis = dataItem.getAnalysis(); + if (analysis != null) { + File parent = dataItem.getFile().getParentFile(); + String folder = Algorithms.capitalizeFirstLetter(parent.getName()); + String dist = OsmAndFormatter.getFormattedDistance(analysis.totalDistance, app); + String points = analysis.wptPoints + " " + getString(R.string.shared_string_gpx_points).toLowerCase(); + String descr = getString(R.string.ltr_or_rtl_combine_via_bold_point, folder, dist); + return getString(R.string.ltr_or_rtl_combine_via_comma, descr, points); + } + return null; + } + + private GpxDataItem getDataItem(File file, @Nullable GpxDataItemCallback callback) { + return app.getGpxDbHelper().getItem(file, callback); + } + + private String getMapDescription(File file) { + if (file.isDirectory() || file.getName().endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT)) { + return getString(R.string.online_map); + } else if (file.getName().endsWith(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)) { + return getString(R.string.download_roads_only_item); + } else if (file.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { + return getString(R.string.download_wikipedia_maps); + } else if (file.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + return getString(R.string.download_srtm_maps); + } else if (file.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { + return getString(R.string.download_regular_maps); + } + return null; + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java index 2c45919f2c..1eec036171 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java @@ -18,6 +18,7 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.settings.backend.ExportSettingsCategory; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.backup.FileSettingsItem; @@ -54,6 +55,9 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { private final boolean nightMode; private final int activeColorRes; private final int secondaryColorRes; + private final int groupViewHeight; + private final int childViewHeight; + private final int listBottomPadding; ExportSettingsAdapter(OsmandApplication app, OnItemSelectedListener listener, boolean nightMode) { this.app = app; @@ -63,6 +67,9 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { themedInflater = UiUtilities.getInflater(app, nightMode); activeColorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light; secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light; + groupViewHeight = app.getResources().getDimensionPixelSize(R.dimen.setting_list_item_group_height); + childViewHeight = app.getResources().getDimensionPixelSize(R.dimen.setting_list_item_large_height); + listBottomPadding = app.getResources().getDimensionPixelSize(R.dimen.fab_recycler_view_padding_bottom); } @Override @@ -70,6 +77,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { View group = convertView; if (group == null) { group = themedInflater.inflate(R.layout.profile_data_list_item_group, parent, false); + group.findViewById(R.id.item_container).setMinimumHeight(groupViewHeight); } final ExportSettingsCategory category = itemsTypes.get(groupPosition); final SettingsCategoryItems items = itemsMap.get(category); @@ -108,6 +116,9 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { } }); + boolean addPadding = !isExpanded && groupPosition == getGroupCount() - 1; + group.setPadding(0, 0, 0, addPadding ? listBottomPadding : 0); + adjustIndicator(app, groupPosition, isExpanded, group, nightMode); AndroidUiHelper.updateVisibility(group.findViewById(R.id.divider), isExpanded); AndroidUiHelper.updateVisibility(group.findViewById(R.id.card_top_divider), true); @@ -122,6 +133,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { View child = convertView; if (child == null) { child = themedInflater.inflate(R.layout.profile_data_list_item_group, parent, false); + child.findViewById(R.id.item_container).setMinimumHeight(childViewHeight); } final ExportSettingsCategory category = itemsTypes.get(groupPosition); final SettingsCategoryItems categoryItems = itemsMap.get(category); @@ -157,7 +169,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { @Override public void onClick(View v) { if (listener != null) { - listener.onTypeClicked(category, type); + listener.onTypeClicked(type); } } }); @@ -174,7 +186,8 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { notifyDataSetChanged(); } }); - + boolean addPadding = isLastChild && groupPosition == getGroupCount() - 1; + child.setPadding(0, 0, 0, addPadding ? listBottomPadding : 0); AndroidUiHelper.updateVisibility(child.findViewById(R.id.card_bottom_divider), isLastChild); return child; @@ -247,17 +260,6 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { notifyDataSetChanged(); } - public boolean hasSelectedData() { - boolean hasSelectedData = false; - for (ExportSettingsType key : selectedItemsMap.keySet()) { - if (!selectedItemsMap.get(key).isEmpty()) { - hasSelectedData = true; - break; - } - } - return hasSelectedData; - } - public List getData() { List selectedItems = new ArrayList<>(); for (List items : selectedItemsMap.values()) { @@ -314,6 +316,10 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { itemsSize += ((FileSettingsItem) object).getSize(); } else if (object instanceof File) { itemsSize += ((File) object).length(); + } else if (object instanceof MapMarkersGroup) { + int selectedMarkers = ((MapMarkersGroup) object).getMarkers().size(); + String itemsDescr = app.getString(R.string.shared_string_items); + return app.getString(R.string.ltr_or_rtl_combine_via_colon, itemsDescr, selectedMarkers); } } } @@ -339,7 +345,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { void onCategorySelected(ExportSettingsCategory type, boolean selected); - void onTypeClicked(ExportSettingsCategory category, ExportSettingsType type); + void onTypeClicked(ExportSettingsType type); } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java index 1245290834..ad4e23aa11 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java @@ -11,7 +11,6 @@ import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; -import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; @@ -73,14 +72,8 @@ public class ExportSettingsFragment extends BaseSettingsListFragment { progressMax = savedInstanceState.getInt(PROGRESS_MAX_KEY); progressValue = savedInstanceState.getInt(PROGRESS_VALUE_KEY); } + exportMode = true; dataList = app.getSettingsHelper().getSettingsByCategory(globalExport); - - requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - showExitDialog(); - } - }); } @Nullable diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java index bb629308b9..18e4f3cec5 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java @@ -12,7 +12,6 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentManager; @@ -101,13 +100,8 @@ public class ImportSettingsFragment extends BaseSettingsListFragment { if (savedInstanceState != null) { duplicateStartTime = savedInstanceState.getLong(DUPLICATES_START_TIME_KEY); } + exportMode = false; settingsHelper = app.getSettingsHelper(); - requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - showExitDialog(); - } - }); ImportAsyncTask importTask = settingsHelper.getImportTask(); if (importTask != null) { diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java index fe47a8f370..7886ff2a7e 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java @@ -67,7 +67,7 @@ public class WikivoyageWebViewClient extends WebViewClient { if (url.contains(WIKIVOAYAGE_DOMAIN) && isWebPage) { String lang = WikiArticleHelper.getLang(url); String articleName = WikiArticleHelper.getArticleNameFromUrl(url, lang); - long articleId = app.getTravelDbHelper().getArticleId(articleName, lang); + long articleId = app.getTravelHelper().getArticleId(articleName, lang); if (articleId != 0) { WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, articleId, lang); } else { @@ -116,7 +116,7 @@ public class WikivoyageWebViewClient extends WebViewClient { fragmentManager.popBackStackImmediate(); - File path = app.getTravelDbHelper().createGpxFile(article); + File path = app.getTravelHelper().createGpxFile(article); GPXUtilities.GPXFile gpxFile = article.getGpxFile(); gpxFile.path = path.getAbsolutePath(); app.getSelectedGpxHelper().setGpxFileToDisplay(gpxFile); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java index fa704688a0..d23200f40b 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleDialogFragment.java @@ -42,6 +42,7 @@ import net.osmand.plus.wikivoyage.WikivoyageShowPicturesDialogFragment; import net.osmand.plus.wikivoyage.WikivoyageWebViewClient; import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import net.osmand.util.Algorithms; @@ -148,8 +149,8 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme if (article == null || activity == null || fm == null) { return; } - TravelDbHelper dbHelper = getMyApplication().getTravelDbHelper(); - File path = dbHelper.createGpxFile(article); + TravelHelper travelHelper = getMyApplication().getTravelHelper(); + File path = travelHelper.createGpxFile(article); Intent newIntent = new Intent(activity, getMyApplication().getAppCustomization().getTrackActivity()); newIntent.putExtra(TrackActivity.TRACK_FILE_NAME, path.getAbsolutePath()); newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -234,7 +235,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme private void updateSaveButton() { if (article != null) { - final TravelLocalDataHelper helper = getMyApplication().getTravelDbHelper().getLocalDataHelper(); + final TravelLocalDataHelper helper = getMyApplication().getTravelHelper().getLocalDataHelper(); final boolean saved = helper.isArticleSaved(article); Drawable icon = getActiveIcon(saved ? R.drawable.ic_action_read_later_fill : R.drawable.ic_action_read_later); saveBtn.setText(getString(saved ? R.string.shared_string_remove : R.string.shared_string_bookmark)); @@ -246,7 +247,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme if (saved) { helper.removeArticleFromSaved(article); } else { - getMyApplication().getTravelDbHelper().createGpxFile(article); + getMyApplication().getTravelHelper().createGpxFile(article); helper.addArticleToSaved(article); } updateSaveButton(); @@ -301,7 +302,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme selectedLang = langs.get(0); } articleToolbarText.setText(""); - article = getMyApplication().getTravelDbHelper().getArticle(tripId, selectedLang); + article = getMyApplication().getTravelHelper().getArticle(tripId, selectedLang); if (article == null) { return; } @@ -314,7 +315,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme trackButton.setVisibility(View.GONE); } - TravelLocalDataHelper ldh = getMyApplication().getTravelDbHelper().getLocalDataHelper(); + TravelLocalDataHelper ldh = getMyApplication().getTravelHelper().getLocalDataHelper(); ldh.addToHistory(article); updateSaveButton(); @@ -370,7 +371,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme @NonNull FragmentManager fm, @NonNull String title, @NonNull String lang) { - long cityId = app.getTravelDbHelper().getArticleId(title, lang); + long cityId = app.getTravelHelper().getArticleId(title, lang); return showInstance(app, fm, cityId, lang); } @@ -378,7 +379,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme @NonNull FragmentManager fm, long cityId, @Nullable String selectedLang) { - ArrayList langs = app.getTravelDbHelper().getArticleLangs(cityId); + ArrayList langs = app.getTravelHelper().getArticleLangs(cityId); return showInstance(fm, cityId, langs, selectedLang); } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleNavigationFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleNavigationFragment.java index 886e128d04..11ca183c50 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleNavigationFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/article/WikivoyageArticleNavigationFragment.java @@ -73,13 +73,13 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr return; } - article = getMyApplication().getTravelDbHelper().getArticle(cityId, selectedLang); + article = getMyApplication().getTravelHelper().getArticle(cityId, selectedLang); if (article == null) { return; } parentsList = new ArrayList<>(Arrays.asList(article.getAggregatedPartOf().split(","))); - LinkedHashMap> navigationMap = getMyApplication().getTravelDbHelper().getNavigationMap(article); + LinkedHashMap> navigationMap = getMyApplication().getTravelHelper().getNavigationMap(article); items.add(new TitleItem(getString(R.string.shared_string_navigation))); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java index df591f4020..0ca212164d 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java @@ -14,6 +14,7 @@ import net.osmand.IndexConstants; import net.osmand.Location; import net.osmand.OsmAndCollator; import net.osmand.PlatformUtil; +import net.osmand.data.Amenity; import net.osmand.data.LatLon; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -43,7 +44,7 @@ import java.util.Set; import gnu.trove.map.hash.TLongObjectHashMap; -public class TravelDbHelper { +public class TravelDbHelper implements TravelHelper { private static final Log LOG = PlatformUtil.getLog(TravelDbHelper.class); @@ -646,7 +647,7 @@ public class TravelDbHelper { return file; } - private static class PopularArticle { + protected static class PopularArticle { long tripId; String title; String lang; @@ -654,11 +655,11 @@ public class TravelDbHelper { int order; double lat; double lon; - + public boolean isLocationSpecified() { return !Double.isNaN(lat) && !Double.isNaN(lon); } - + public static PopularArticle readArticle(SQLiteCursor cursor) { PopularArticle res = new PopularArticle(); res.title = cursor.getString(0); @@ -670,5 +671,17 @@ public class TravelDbHelper { res.popIndex = cursor.isNull(6) ? 0 : cursor.getInt(6); return res; } + + public static PopularArticle readArticleFromAmenity(Amenity a, String lang) { + PopularArticle res = new PopularArticle(); + res.title = a.getName(lang); + res.lat = a.getLocation().getLatitude(); + res.lon = a.getLocation().getLongitude(); + res.tripId = a.getId(); + res.lang = lang; + res.order = -1; + res.popIndex = 0; + return res; + } } } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelHelper.java new file mode 100644 index 0000000000..065aff9e3f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelHelper.java @@ -0,0 +1,53 @@ +package net.osmand.plus.wikivoyage.data; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; + +import java.io.File; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public interface TravelHelper { + + TravelLocalDataHelper getLocalDataHelper(); + + void initTravelBooks(); + + void loadDataForSelectedTravelBook(); + + File getSelectedTravelBook(); + + List getExistingTravelBooks(); + + void selectTravelBook(File f); + + @NonNull + List search(final String searchQuery); + + @NonNull + List getPopularArticles(); + + @NonNull + List loadPopularArticles(); + + LinkedHashMap> getNavigationMap( + final TravelArticle article); + + TravelArticle getArticle(long cityId, String lang); + + TravelArticle getArticle(String title, String lang); + + long getArticleId(String title, String lang); + + ArrayList getArticleLangs(long cityId); + + String formatTravelBookName(File tb); + + String getGPXName(TravelArticle article); + + File createGpxFile(TravelArticle article); +} diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelLocalDataHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelLocalDataHelper.java index 6127c3e5d5..68f2f7cc2c 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelLocalDataHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelLocalDataHelper.java @@ -260,7 +260,7 @@ public class TravelLocalDataHelper { if (oldVersion < 3) { conn.execSQL("ALTER TABLE " + HISTORY_TABLE_NAME + " ADD " + HISTORY_COL_TRAVEL_BOOK + " TEXT"); conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_TRAVEL_BOOK + " TEXT"); - File selectedTravelBook = context.getTravelDbHelper().getSelectedTravelBook(); + File selectedTravelBook = context.getTravelHelper().getSelectedTravelBook(); if (selectedTravelBook != null) { Object[] args = new Object[]{selectedTravelBook.getName()}; conn.execSQL("UPDATE " + HISTORY_TABLE_NAME + " SET " + HISTORY_COL_TRAVEL_BOOK + " = ?", args); @@ -452,7 +452,7 @@ public class TravelLocalDataHelper { @Nullable private String getSelectedTravelBookName() { - File selectedTravelBook = context.getTravelDbHelper().getSelectedTravelBook(); + File selectedTravelBook = context.getTravelHelper().getSelectedTravelBook(); if (selectedTravelBook != null) { return selectedTravelBook.getName(); } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java new file mode 100644 index 0000000000..3f6b27129e --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java @@ -0,0 +1,301 @@ +package net.osmand.plus.wikivoyage.data; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.Collator; +import net.osmand.GPXUtilities; +import net.osmand.IndexConstants; +import net.osmand.OsmAndCollator; +import net.osmand.PlatformUtil; +import net.osmand.ResultMatcher; +import net.osmand.binary.BinaryIndexPart; +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.binary.BinaryMapPoiReaderAdapter; +import net.osmand.data.Amenity; +import net.osmand.data.LatLon; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.util.Algorithms; +import net.osmand.util.MapUtils; + +import org.apache.commons.logging.Log; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; + + + +public class TravelObfHelper implements TravelHelper{ + + private static final Log LOG = PlatformUtil.getLog(TravelObfHelper.class); + + private static final String WIKIVOYAGE_OBF = "Wikivoyage.obf"; + public static final String ROUTE_ARTICLE = "route_article"; + + private final OsmandApplication application; + private Collator collator; + private TravelLocalDataHelper localDataHelper; + + private File selectedTravelBook = null; + private List existingTravelBooks = new ArrayList<>(); + private List popularArticles = new ArrayList(); + + private BinaryMapIndexReader index = null; + + + public TravelObfHelper(OsmandApplication application) { + this.application = application; + collator = OsmAndCollator.primaryCollator(); + localDataHelper = new TravelLocalDataHelper(application); + } + + public static boolean checkIfObfFileExists(OsmandApplication app) { + File[] files = app.getAppPath(IndexConstants.WIKIVOYAGE_INDEX_DIR).listFiles(); + if (files != null) { + for (File f : files) { + if (f.getName().equals(WIKIVOYAGE_OBF)) { + return true; + } + } + } + return false; + } + + @Override + public TravelLocalDataHelper getLocalDataHelper() { + return localDataHelper; + } + + /** TODO + * 1. implement regional travelbooks + * 2. check settings for default? + */ + public void initTravelBooks() { + List files = getPossibleFiles(); + String travelBook = application.getSettings().SELECTED_TRAVEL_BOOK.get(); + existingTravelBooks.clear(); + if (files != null && !files.isEmpty()) { + for (File f : files) { + existingTravelBooks.add(f); + if (selectedTravelBook == null) { + selectedTravelBook = f; + } else if (Algorithms.objectEquals(travelBook, f.getName())) { + selectedTravelBook = f; + } + } + selectedTravelBook = files.get(0); + } else { + selectedTravelBook = null; + } + + } + + /** + * todo: get all obf files from folder, may be we should add some suffix like 'wikivoyage' + * to filenames to distinguish from other maps? Or add some checks right there. + */ + @Nullable + private List getPossibleFiles() { + File[] files = application.getAppPath(IndexConstants.WIKIVOYAGE_INDEX_DIR).listFiles(); + if (files != null) { + List res = new ArrayList<>(); + for (File file : files) { + if (file.getName().equals("Wikivoyage.obf")) { + res.add(file); + LOG.debug(String.format("FIle name: %s", file.getAbsolutePath())); + } + } + return res; + } + return null; + } + + public void loadDataForSelectedTravelBook() { + localDataHelper.refreshCachedData(); + loadPopularArticles(); + } + + @Override + public File getSelectedTravelBook() { + return selectedTravelBook; + } + + @Override + public List getExistingTravelBooks() { + return existingTravelBooks; + } + + @Override + public void selectTravelBook(File f) { + //todo + } + + @NonNull + @Override + public List search(String searchQuery) { + return null; + } + + @NonNull + public List getPopularArticles() { + return popularArticles; + } + + //TODO for now it reads any articles, since we didn't have popular articles in the obf + @NonNull + public List loadPopularArticles() { + String language = application.getLanguage(); + final List articles = new ArrayList<>(); + try { + BinaryMapIndexReader bookIndexReader = getBookBinaryIndex(); + if (bookIndexReader == null) { + popularArticles = new ArrayList<>(); + return popularArticles; + } + LatLon ll = application.getMapViewTrackingUtilities().getMapLocation(); + float coeff = 2; + BinaryMapIndexReader.SearchRequest req = + BinaryMapIndexReader.buildSearchPoiRequest( + MapUtils.get31TileNumberX(ll.getLongitude() - coeff), + MapUtils.get31TileNumberX(ll.getLongitude() + coeff), + MapUtils.get31TileNumberY(ll.getLatitude() + coeff), + MapUtils.get31TileNumberY(ll.getLatitude() - coeff), + -1, + BinaryMapIndexReader.ACCEPT_ALL_POI_TYPE_FILTER, + new ResultMatcher() { + int count = 0; + + @Override + public boolean publish(Amenity object) { + //TODO need more logical way to filter results + if (object.getSubType().equals(ROUTE_ARTICLE)) { + articles.add(object); + } + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + }); + + bookIndexReader.searchPoi(req); + bookIndexReader.close(); + + if (articles.size() > 0) { + Iterator it = articles.iterator(); + while (it.hasNext()) { + Amenity a = it.next(); + if (!a.getName(language).equals("")) { + popularArticles.add(readArticle(a, language)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return popularArticles; + } + + + private TravelArticle readArticle(Amenity amenity, String lang) { + TravelArticle res = new TravelArticle(); + + res.title = amenity.getName(lang).equals("") ? amenity.getName() : amenity.getName(lang); + res.content = amenity.getDescription(lang); + res.isPartOf = amenity.getTagContent(Amenity.IS_PART, lang) == null ? "" : amenity.getTagContent(Amenity.IS_PART, lang); + res.lat = amenity.getLocation().getLatitude(); + res.lon = amenity.getLocation().getLongitude(); + res.imageTitle = amenity.getTagContent(Amenity.IMAGE_TITLE, lang) == null ? "" : amenity.getTagContent(Amenity.IMAGE_TITLE, lang); + res.tripId = amenity.getId(); //? + res.originalId = 0; //? + res.lang = lang; + res.contentsJson = amenity.getTagContent(Amenity.CONTENT_JSON, lang) == null ? "" : amenity.getTagContent(Amenity.CONTENT_JSON, lang); + res.aggregatedPartOf = amenity.getTagContent(Amenity.IS_AGGR_PART, lang) == null ? "" : amenity.getTagContent(Amenity.IS_AGGR_PART, lang); + +// crash in some places, need to fix it +// try { +// String gpxContent = amenity.getAdditionalInfo("gpx_info"); +// res.gpxFile = GPXUtilities.loadGPXFile(new ByteArrayInputStream(gpxContent.getBytes("UTF-8"))); +// } catch (IOException e) { +// LOG.error(e.getMessage(), e); +// } + + return res; + } + + private BinaryMapIndexReader getBookBinaryIndex() throws IOException { + application.getSettings().SELECTED_TRAVEL_BOOK.set(selectedTravelBook.getName()); + try { + RandomAccessFile r = new RandomAccessFile(selectedTravelBook.getAbsolutePath(), "r"); + BinaryMapIndexReader index = new BinaryMapIndexReader(r, selectedTravelBook); + for (BinaryIndexPart p : index.getIndexes()) { + if (p instanceof BinaryMapPoiReaderAdapter.PoiRegion) { + return index; + } + } + } catch (IOException e) { + System.err.println("File doesn't have valid structure : " + selectedTravelBook.getName() + " " + e.getMessage()); + throw e; + } + return null; + } + + @Override + public LinkedHashMap> getNavigationMap(TravelArticle article) { + return null; + } + + @Override + public TravelArticle getArticle(long cityId, String lang) { + return null; + } + + @Override + public TravelArticle getArticle(String title, String lang) { + return null; + } + + @Override + public long getArticleId(String title, String lang) { + return 0; + } + + @Override + public ArrayList getArticleLangs(long cityId) { + return null; + } + + @Override + public String formatTravelBookName(File tb) { + if (tb == null) { + return application.getString(R.string.shared_string_none); + } + String nm = tb.getName(); + return nm.substring(0, nm.indexOf('.')).replace('_', ' '); + } + + @Override + public String getGPXName(TravelArticle article) { + return article.getTitle().replace('/', '_').replace('\'', '_') + .replace('\"', '_') + IndexConstants.GPX_FILE_EXT; + } + + @Override + public File createGpxFile(TravelArticle article) { + final GPXUtilities.GPXFile gpx = article.getGpxFile(); + File file = application.getAppPath(IndexConstants.GPX_TRAVEL_DIR + getGPXName(article)); + if (!file.exists()) { + GPXUtilities.writeGpxFile(file, gpx); + } + return file; + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java index 88e814773b..2419f02636 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java @@ -29,6 +29,7 @@ import net.osmand.plus.download.DownloadValidationManager; import net.osmand.plus.download.IndexItem; import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import net.osmand.plus.wikivoyage.explore.travelcards.ArticleTravelCard; import net.osmand.plus.wikivoyage.explore.travelcards.BaseTravelCard; @@ -92,7 +93,7 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv super.onResume(); OsmandApplication app = getMyApplication(); if (app != null) { - app.getTravelDbHelper().getLocalDataHelper().addListener(this); + app.getTravelHelper().getLocalDataHelper().addListener(this); } WikivoyageExploreActivity exploreActivity = getExploreActivity(); if (exploreActivity != null) { @@ -105,7 +106,7 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv super.onPause(); OsmandApplication app = getMyApplication(); if (app != null) { - app.getTravelDbHelper().getLocalDataHelper().removeListener(this); + app.getTravelHelper().getLocalDataHelper().removeListener(this); } } @@ -135,9 +136,9 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv public void downloadHasFinished() { OsmandApplication app = getMyApplication(); if (app != null) { - TravelDbHelper travelDbHelper = app.getTravelDbHelper(); - if (travelDbHelper.getSelectedTravelBook() == null) { - app.getTravelDbHelper().initTravelBooks(); + TravelHelper travelHelper = app.getTravelHelper(); + if (travelHelper.getSelectedTravelBook() == null) { + app.getTravelHelper().initTravelBooks(); WikivoyageExploreActivity exploreActivity = getExploreActivity(); if (exploreActivity != null) { exploreActivity.populateData(); @@ -181,10 +182,10 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv if (!Version.isPaidVersion(app)) { items.add(new OpenBetaTravelCard(app, nightMode, fm)); } - if (app.getTravelDbHelper().getSelectedTravelBook() != null) { + if (app.getTravelHelper().getSelectedTravelBook() != null) { items.add(new HeaderTravelCard(app, nightMode, getString(R.string.popular_destinations))); - List popularArticles = app.getTravelDbHelper().getPopularArticles(); + List popularArticles = app.getTravelHelper().getPopularArticles(); for (TravelArticle article : popularArticles) { items.add(new ArticleTravelCard(app, nightMode, article, fm)); } @@ -238,7 +239,7 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv boolean outdated = mainIndexItem != null && mainIndexItem.isOutdated(); boolean needsDownloading = mainIndexItem != null && !mainIndexItem.isDownloaded(); - File selectedTravelBook = app.getTravelDbHelper().getSelectedTravelBook(); + File selectedTravelBook = app.getTravelHelper().getSelectedTravelBook(); if (selectedTravelBook == null || needsDownloading || (outdated && SHOW_TRAVEL_UPDATE_CARD)) { boolean showOtherMaps = false; @@ -389,7 +390,7 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv IndexItem mainItem = app.getDownloadThread().getIndexes().getWikivoyageItem(fileName); List neededItems = new ArrayList<>(); - for (TravelArticle article : app.getTravelDbHelper().getLocalDataHelper().getSavedArticles()) { + for (TravelArticle article : app.getTravelHelper().getLocalDataHelper().getSavedArticles()) { LatLon latLon = new LatLon(article.getLat(), article.getLon()); try { for (DownloadActivityType type : types) { diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/SavedArticlesRvAdapter.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/SavedArticlesRvAdapter.java index bc8025cba9..6a4864d147 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/SavedArticlesRvAdapter.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/SavedArticlesRvAdapter.java @@ -195,7 +195,7 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter { private WeakReference activityRef; - private TravelDbHelper travelDbHelper; + private TravelHelper travelHelper; LoadWikivoyageData(WikivoyageExploreActivity activity) { - travelDbHelper = activity.getMyApplication().getTravelDbHelper(); + travelHelper = activity.getMyApplication().getTravelHelper(); activityRef = new WeakReference<>(activity); } @Override protected Void doInBackground(Void... params) { - travelDbHelper.loadDataForSelectedTravelBook(); + travelHelper.loadDataForSelectedTravelBook(); return null; } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java index 988598c359..fe3021ba51 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/WikivoyageOptionsBottomSheetDialogFragment.java @@ -25,6 +25,7 @@ import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerHalfItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.wikivoyage.data.TravelDbHelper; +import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import java.io.File; @@ -45,13 +46,13 @@ public class WikivoyageOptionsBottomSheetDialogFragment extends MenuBottomSheetD return; } final CommonPreference showImagesPref = app.getSettings().WIKI_ARTICLE_SHOW_IMAGES; - final TravelDbHelper dbHelper = app.getTravelDbHelper(); + final TravelHelper travelHelper = app.getTravelHelper(); items.add(new TitleItem(getString(R.string.shared_string_options))); - if (dbHelper.getExistingTravelBooks().size() > 1) { + if (travelHelper.getExistingTravelBooks().size() > 1) { BaseBottomSheetItem selectTravelBook = new BottomSheetItemWithDescription.Builder() - .setDescription(dbHelper.formatTravelBookName(dbHelper.getSelectedTravelBook())) + .setDescription(travelHelper.formatTravelBookName(travelHelper.getSelectedTravelBook())) .setDescriptionColorId(nightMode ? R.color.wikivoyage_active_dark : R.color.wikivoyage_active_light) .setIcon(getContentIcon(R.drawable.ic_action_travel)) .setTitle(getString(R.string.shared_string_travel_book)) @@ -125,7 +126,7 @@ public class WikivoyageOptionsBottomSheetDialogFragment extends MenuBottomSheetD public void onClick(View v) { OsmandApplication app = getMyApplication(); if (app != null) { - TravelLocalDataHelper ldh = app.getTravelDbHelper().getLocalDataHelper(); + TravelLocalDataHelper ldh = app.getTravelHelper().getLocalDataHelper(); ldh.clearHistory(); } dismiss(); @@ -149,11 +150,11 @@ public class WikivoyageOptionsBottomSheetDialogFragment extends MenuBottomSheetD return; } - final TravelDbHelper dbHelper = app.getTravelDbHelper(); - final List list = dbHelper.getExistingTravelBooks(); + final TravelHelper travelHelper = app.getTravelHelper(); + final List list = travelHelper.getExistingTravelBooks(); String[] ls = new String[list.size()]; for (int i = 0; i < ls.length; i++) { - ls[i] = dbHelper.formatTravelBookName(list.get(i)); + ls[i] = travelHelper.formatTravelBookName(list.get(i)); } new AlertDialog.Builder(ctx) @@ -161,7 +162,7 @@ public class WikivoyageOptionsBottomSheetDialogFragment extends MenuBottomSheetD .setItems(ls, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - dbHelper.selectTravelBook(list.get(which)); + travelHelper.selectTravelBook(list.get(which)); sendResult(TRAVEL_BOOK_CHANGED); } }) diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/ArticleTravelCard.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/ArticleTravelCard.java index 5b782c5757..084209674a 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/ArticleTravelCard.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/ArticleTravelCard.java @@ -91,7 +91,7 @@ public class ArticleTravelCard extends BaseTravelCard { private void updateSaveButton(final ArticleTravelVH holder) { if (article != null) { - final TravelLocalDataHelper helper = app.getTravelDbHelper().getLocalDataHelper(); + final TravelLocalDataHelper helper = app.getTravelHelper().getLocalDataHelper(); final boolean saved = helper.isArticleSaved(article); Drawable icon = getActiveIcon(saved ? R.drawable.ic_action_read_later_fill : R.drawable.ic_action_read_later); holder.rightButton.setText(saved ? R.string.shared_string_remove : R.string.shared_string_bookmark); @@ -103,7 +103,7 @@ public class ArticleTravelCard extends BaseTravelCard { if (saved) { helper.removeArticleFromSaved(article); } else { - app.getTravelDbHelper().createGpxFile(article); + app.getTravelHelper().createGpxFile(article); helper.addArticleToSaved(article); } updateSaveButton(holder); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/menu/WikivoyageWptPtMenuController.java b/OsmAnd/src/net/osmand/plus/wikivoyage/menu/WikivoyageWptPtMenuController.java index 1ea04be8b4..9ad66116cb 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/menu/WikivoyageWptPtMenuController.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/menu/WikivoyageWptPtMenuController.java @@ -42,7 +42,7 @@ public class WikivoyageWptPtMenuController extends WptPtMenuController { String title = metadata != null ? metadata.getArticleTitle() : null; String lang = metadata != null ? metadata.getArticleLang() : null; if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) { - return mapActivity.getMyApplication().getTravelDbHelper().getArticle(title, lang); + return mapActivity.getMyApplication().getTravelHelper().getArticle(title, lang); } return null; } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchDialogFragment.java index 826d938562..f0aafde679 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchDialogFragment.java @@ -151,7 +151,7 @@ public class WikivoyageSearchDialogFragment extends WikiBaseDialogFragment { private void setAdapterItems(@Nullable List items) { if (items == null || items.isEmpty()) { - TravelLocalDataHelper ldh = getMyApplication().getTravelDbHelper().getLocalDataHelper(); + TravelLocalDataHelper ldh = getMyApplication().getTravelHelper().getLocalDataHelper(); adapter.setHistoryItems(ldh.getAllHistory()); } else { adapter.setItems(items); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchHelper.java index 147cfbd91e..e25feedfc1 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/search/WikivoyageSearchHelper.java @@ -42,7 +42,7 @@ public class WikivoyageSearchHelper { } if (!isCancelled()) { - List results = application.getTravelDbHelper().search(query); + List results = application.getTravelHelper().search(query); if (!isCancelled()) { rm.publish(results); }