diff --git a/.gitignore b/.gitignore
index 5798e73f27..33e746a3d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,10 @@ OsmAndCore_*.aar
.project
out/
+# Huawei
+agconnect-services.json
+OsmAndHms.jks
+
# Android Studio
/.idea
*.iml
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl b/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl
index 3edd8c94da..72a3035b60 100644
--- a/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl
+++ b/OsmAnd-api/src/net/osmand/aidlapi/IOsmAndAidlInterface.aidl
@@ -20,6 +20,8 @@ import net.osmand.aidlapi.mapmarker.UpdateMapMarkerParams;
import net.osmand.aidlapi.calculateroute.CalculateRouteParams;
+import net.osmand.aidlapi.profile.ExportProfileParams;
+
import net.osmand.aidlapi.gpx.ImportGpxParams;
import net.osmand.aidlapi.gpx.ShowGpxParams;
import net.osmand.aidlapi.gpx.StartGpxRecordingParams;
@@ -103,6 +105,8 @@ import net.osmand.aidlapi.events.AKeyEventsParams;
import net.osmand.aidlapi.info.AppInfoParams;
+import net.osmand.aidlapi.profile.ExportProfileParams;
+
// NOTE: Add new methods at the end of file!!!
interface IOsmAndAidlInterface {
@@ -867,4 +871,6 @@ interface IOsmAndAidlInterface {
AppInfoParams getAppInfo();
boolean setMapMargins(in MapMarginsParams params);
+
+ boolean exportProfile(in ExportProfileParams params);
}
\ No newline at end of file
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java
index 2e1543321a..af48552bc4 100644
--- a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java
+++ b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java
@@ -4,6 +4,8 @@ public interface OsmAndCustomizationConstants {
// Navigation Drawer:
String DRAWER_ITEM_ID_SCHEME = "drawer.action.";
+ String DRAWER_SWITCH_PROFILE_ID = DRAWER_ITEM_ID_SCHEME + "switch_profile";
+ String DRAWER_CONFIGURE_PROFILE_ID = DRAWER_ITEM_ID_SCHEME + "configure_profile";
String DRAWER_DASHBOARD_ID = DRAWER_ITEM_ID_SCHEME + "dashboard";
String DRAWER_MAP_MARKERS_ID = DRAWER_ITEM_ID_SCHEME + "map_markers";
String DRAWER_MY_PLACES_ID = DRAWER_ITEM_ID_SCHEME + "my_places";
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java b/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java
index 1118a17f5c..ad122f60c6 100644
--- a/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java
+++ b/OsmAnd-api/src/net/osmand/aidlapi/copyfile/CopyFileParams.java
@@ -9,12 +9,21 @@ import net.osmand.aidlapi.AidlParams;
public class CopyFileParams extends AidlParams {
+ public static final String DESTINATION_DIR_KEY = "destinationDir";
+ public static final String FILE_NAME_KEY = "fileName";
+ public static final String FILE_PART_DATA_KEY = "filePartData";
+ public static final String START_TIME_KEY = "startTime";
+ public static final String DONE_KEY = "done";
+ private String destinationDir;
private String fileName;
private byte[] filePartData;
private long startTime;
private boolean done;
- public CopyFileParams(@NonNull String fileName, @NonNull byte[] filePartData, long startTime, boolean done) {
+ public CopyFileParams(@NonNull String destinationDir, @NonNull String fileName, @NonNull byte[] filePartData,
+ long startTime, boolean done) {
+
+ this.destinationDir = destinationDir;
this.fileName = fileName;
this.filePartData = filePartData;
this.startTime = startTime;
@@ -37,6 +46,10 @@ public class CopyFileParams extends AidlParams {
}
};
+ public String getDestinationDir() {
+ return destinationDir;
+ }
+
public String getFileName() {
return fileName;
}
@@ -55,23 +68,26 @@ public class CopyFileParams extends AidlParams {
@Override
public void writeToBundle(Bundle bundle) {
- bundle.putString("fileName", fileName);
- bundle.putByteArray("filePartData", filePartData);
- bundle.putLong("startTime", startTime);
- bundle.putBoolean("done", done);
+ bundle.putString(DESTINATION_DIR_KEY, destinationDir);
+ bundle.putString(FILE_NAME_KEY, fileName);
+ bundle.putByteArray(FILE_PART_DATA_KEY, filePartData);
+ bundle.putLong(START_TIME_KEY, startTime);
+ bundle.putBoolean(DONE_KEY, done);
}
@Override
protected void readFromBundle(Bundle bundle) {
- fileName = bundle.getString("fileName");
- filePartData = bundle.getByteArray("filePartData");
- startTime = bundle.getLong("startTime");
- done = bundle.getBoolean("done");
+ destinationDir = bundle.getString(DESTINATION_DIR_KEY);
+ fileName = bundle.getString(FILE_NAME_KEY);
+ filePartData = bundle.getByteArray(FILE_PART_DATA_KEY);
+ startTime = bundle.getLong(START_TIME_KEY);
+ done = bundle.getBoolean(DONE_KEY);
}
@Override
public String toString() {
return "CopyFileParams {" +
+ " destinationDir=" + destinationDir +
" fileName=" + fileName +
", filePartData size=" + filePartData.length +
", startTime=" + startTime +
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java b/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java
index 36959ef776..00c68851e7 100644
--- a/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java
+++ b/OsmAnd-api/src/net/osmand/aidlapi/customization/ProfileSettingsParams.java
@@ -5,15 +5,31 @@ import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
+import net.osmand.aidlapi.profile.AExportSettingsType;
+
+import java.util.ArrayList;
+
+import static net.osmand.aidlapi.profile.ExportProfileParams.SETTINGS_TYPE_KEY;
public class ProfileSettingsParams extends AidlParams {
+ public static final String VERSION_KEY = "version";
+ public static final String REPLACE_KEY = "replace";
+ public static final String LATEST_CHANGES_KEY = "latestChanges";
+ public static final String PROFILE_SETTINGS_URI_KEY = "profileSettingsUri";
private Uri profileSettingsUri;
private String latestChanges;
private int version;
+ private ArrayList settingsTypeKeyList = new ArrayList<>();
+ boolean replace;
- public ProfileSettingsParams(Uri profileSettingsUri, String latestChanges, int version) {
+ public ProfileSettingsParams(Uri profileSettingsUri, ArrayList settingsTypeList, boolean replace,
+ String latestChanges, int version) {
this.profileSettingsUri = profileSettingsUri;
+ for (AExportSettingsType settingsType : settingsTypeList) {
+ settingsTypeKeyList.add(settingsType.name());
+ }
+ this.replace = replace;
this.latestChanges = latestChanges;
this.version = version;
}
@@ -46,17 +62,29 @@ public class ProfileSettingsParams extends AidlParams {
return profileSettingsUri;
}
+ public ArrayList getSettingsTypeKeys() {
+ return settingsTypeKeyList;
+ }
+
+ public boolean isReplace() {
+ return replace;
+ }
+
@Override
public void writeToBundle(Bundle bundle) {
- bundle.putInt("version", version);
- bundle.putString("latestChanges", latestChanges);
- bundle.putParcelable("profileSettingsUri", profileSettingsUri);
+ bundle.putInt(VERSION_KEY, version);
+ bundle.putString(LATEST_CHANGES_KEY, latestChanges);
+ bundle.putParcelable(PROFILE_SETTINGS_URI_KEY, profileSettingsUri);
+ bundle.putStringArrayList(SETTINGS_TYPE_KEY, settingsTypeKeyList);
+ bundle.putBoolean(REPLACE_KEY, replace);
}
@Override
protected void readFromBundle(Bundle bundle) {
- version = bundle.getInt("version");
- latestChanges = bundle.getString("latestChanges");
- profileSettingsUri = bundle.getParcelable("profileSettingsUri");
+ version = bundle.getInt(VERSION_KEY);
+ latestChanges = bundle.getString(LATEST_CHANGES_KEY);
+ profileSettingsUri = bundle.getParcelable(PROFILE_SETTINGS_URI_KEY);
+ settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY);
+ replace = bundle.getBoolean(REPLACE_KEY);
}
}
\ No newline at end of file
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.aidl b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.aidl
new file mode 100644
index 0000000000..99c59bba2a
--- /dev/null
+++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.aidl
@@ -0,0 +1,3 @@
+package net.osmand.aidlapi.profile;
+
+parcelable AExportSettingsType;
\ No newline at end of file
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.java b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.java
new file mode 100644
index 0000000000..23c0189615
--- /dev/null
+++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/AExportSettingsType.java
@@ -0,0 +1,11 @@
+package net.osmand.aidlapi.profile;
+
+public enum AExportSettingsType {
+ PROFILE,
+ QUICK_ACTIONS,
+ POI_TYPES,
+ MAP_SOURCES,
+ CUSTOM_RENDER_STYLE,
+ CUSTOM_ROUTING,
+ AVOID_ROADS;
+}
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.aidl b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.aidl
new file mode 100644
index 0000000000..0dfefce6be
--- /dev/null
+++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.aidl
@@ -0,0 +1,3 @@
+package net.osmand.aidlapi.profile;
+
+parcelable ExportProfileParams;
\ No newline at end of file
diff --git a/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.java b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.java
new file mode 100644
index 0000000000..931f52eeb8
--- /dev/null
+++ b/OsmAnd-api/src/net/osmand/aidlapi/profile/ExportProfileParams.java
@@ -0,0 +1,61 @@
+package net.osmand.aidlapi.profile;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import net.osmand.aidlapi.AidlParams;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ExportProfileParams extends AidlParams {
+
+ public static final String PROFILE_KEY = "profile";
+ public static final String SETTINGS_TYPE_KEY = "settings_type";
+ private String profile;
+ private ArrayList settingsTypeKeyList = new ArrayList<>();
+
+ public ExportProfileParams(String profile, ArrayList settingsTypeList) {
+
+ this.profile = profile;
+ for (AExportSettingsType settingsType : settingsTypeList) {
+ settingsTypeKeyList.add(settingsType.name());
+ }
+ }
+
+ public ExportProfileParams(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public ExportProfileParams createFromParcel(Parcel in) {
+ return new ExportProfileParams(in);
+ }
+
+ @Override
+ public ExportProfileParams[] newArray(int size) {
+ return new ExportProfileParams[size];
+ }
+ };
+
+ public String getProfile() {
+ return profile;
+ }
+
+ public List getSettingsTypeKeys() {
+ return settingsTypeKeyList;
+ }
+
+ @Override
+ public void writeToBundle(Bundle bundle) {
+ bundle.putString(PROFILE_KEY, profile);
+ bundle.putStringArrayList(SETTINGS_TYPE_KEY, settingsTypeKeyList);
+ }
+
+ @Override
+ protected void readFromBundle(Bundle bundle) {
+ profile = bundle.getString(PROFILE_KEY);
+ settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY);
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java
index 54464afc32..bff7db3a6d 100644
--- a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java
+++ b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java
@@ -739,7 +739,7 @@ public class RoutePlannerFrontEnd {
res = searchRouteImpl(ctx, points, routeDirection);
}
if (ctx.calculationProgress != null) {
- ctx.calculationProgress.timeToCalculate += (System.nanoTime() - timeToCalculate);
+ ctx.calculationProgress.timeToCalculate = (System.nanoTime() - timeToCalculate);
}
BinaryRoutePlanner.printDebugMemoryInformation(ctx);
if (res != null) {
diff --git a/OsmAnd-telegram/AndroidManifest.xml b/OsmAnd-telegram/AndroidManifest.xml
index 7b2a96c236..73e2e856ca 100644
--- a/OsmAnd-telegram/AndroidManifest.xml
+++ b/OsmAnd-telegram/AndroidManifest.xml
@@ -20,7 +20,7 @@
android:screenOrientation="unspecified"
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml
index 928ad6f319..1c5738a313 100644
--- a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml
+++ b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml
@@ -447,6 +447,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OsmAnd-telegram/res/layout/item_description_long.xml b/OsmAnd-telegram/res/layout/item_description_long.xml
new file mode 100644
index 0000000000..6face5220f
--- /dev/null
+++ b/OsmAnd-telegram/res/layout/item_description_long.xml
@@ -0,0 +1,18 @@
+
+
diff --git a/OsmAnd-telegram/res/values-de/strings.xml b/OsmAnd-telegram/res/values-de/strings.xml
index 8c12570da5..0767c598d8 100644
--- a/OsmAnd-telegram/res/values-de/strings.xml
+++ b/OsmAnd-telegram/res/values-de/strings.xml
@@ -267,4 +267,8 @@
Letzte Antwort: vor %1$svor %1$sERR
+ Export
+ Logcat-Puffer
+ Protokolle der Anwendung einsehen und freigeben
+ Bericht senden
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-et/strings.xml b/OsmAnd-telegram/res/values-et/strings.xml
index 126490e593..0ddb7950a7 100644
--- a/OsmAnd-telegram/res/values-et/strings.xml
+++ b/OsmAnd-telegram/res/values-et/strings.xml
@@ -267,4 +267,8 @@
Viimane vastus: %1$s tagasi%1$s tagasiERR
+ Ekspordi
+ Logcati puhver
+ Vaata ja jaga rakenduse detailseid logisid
+ Saada ettekanne
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-fr/strings.xml b/OsmAnd-telegram/res/values-fr/strings.xml
index 4397948508..4b9d45edfb 100644
--- a/OsmAnd-telegram/res/values-fr/strings.xml
+++ b/OsmAnd-telegram/res/values-fr/strings.xml
@@ -267,4 +267,8 @@
Définissez l\'heure à laquelle les contacts et groupes sélectionnés verront votre position en temps réel.OsmAnd connectdepuis
+ Buffer Logcat
+ Vérifier et partager les logs détaillés de l\'application
+ Exporter
+ Envoyer le rapport
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-he/strings.xml b/OsmAnd-telegram/res/values-he/strings.xml
index 99c18d4cb2..65da2f52a1 100644
--- a/OsmAnd-telegram/res/values-he/strings.xml
+++ b/OsmAnd-telegram/res/values-he/strings.xml
@@ -268,4 +268,8 @@
תגובה אחרונה: לפני %1$sלפני %1$sשגיאה
+ ייצוא
+ מכלא Logcat
+ בדיקה ושיתוף יומני תיעוד מפורטים של היישומים
+ שליחת דיווח
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-hu/strings.xml b/OsmAnd-telegram/res/values-hu/strings.xml
index 8c257bb040..95d655b5cb 100644
--- a/OsmAnd-telegram/res/values-hu/strings.xml
+++ b/OsmAnd-telegram/res/values-hu/strings.xml
@@ -268,4 +268,8 @@
Utolsó válasz: %1$sEnnyivel ezelőtt: %1$sHIBA
+ Exportálás
+ Logcat-puffer (hibanapló)
+ Az alkalmazás részletes naplóinak ellenőrzése és megosztása
+ Jelentés küldése
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-pt-rBR/strings.xml b/OsmAnd-telegram/res/values-pt-rBR/strings.xml
index c1aa3b8e55..d792f9748f 100644
--- a/OsmAnd-telegram/res/values-pt-rBR/strings.xml
+++ b/OsmAnd-telegram/res/values-pt-rBR/strings.xml
@@ -267,4 +267,8 @@
Última resposta: %1$s atrás%1$s atrásERR
+ Exportar
+ Buffer de Logcat
+ Verifique e compartilhe registros detalhados do aplicativo
+ Enviar o relatório
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-pt/strings.xml b/OsmAnd-telegram/res/values-pt/strings.xml
index 6076bfee91..21dfc6b4e9 100644
--- a/OsmAnd-telegram/res/values-pt/strings.xml
+++ b/OsmAnd-telegram/res/values-pt/strings.xml
@@ -108,7 +108,7 @@
Ainda não encontradoReenvie o localÚltima localização disponível
- Status de compartilhamento
+ Estado de compartilhamentoCompartilhamento: %1$sAtivadoSem conexão GPS
diff --git a/OsmAnd-telegram/res/values-ru/strings.xml b/OsmAnd-telegram/res/values-ru/strings.xml
index daff0e950b..e12c0245b5 100644
--- a/OsmAnd-telegram/res/values-ru/strings.xml
+++ b/OsmAnd-telegram/res/values-ru/strings.xml
@@ -75,7 +75,7 @@
По расстояниюПо имениПо группе
- Сортировать
+ СортировкаСортировать поОтстановить всеВыход
@@ -267,4 +267,8 @@
Последнее обновление от Telegram: %1$s назадПоследний ответ: %1$sПоследнее обновление от Telegram: %1$s
+ Экспорт
+ Буфер Logcat
+ Проверьте и поделитесь подробными журналами приложения
+ Отправить отчёт
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-sc/strings.xml b/OsmAnd-telegram/res/values-sc/strings.xml
index 9846061b82..82a4e8aade 100644
--- a/OsmAnd-telegram/res/values-sc/strings.xml
+++ b/OsmAnd-telegram/res/values-sc/strings.xml
@@ -268,4 +268,8 @@
Ùrtima risposta: %1$s a como%1$s a comoERR
+ Esporta
+ Buffer de Logcat
+ Verìfica e cumpartzi sos registros de s\'aplicatzione fatos a sa minuda
+ Imbia resumu
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-tr/strings.xml b/OsmAnd-telegram/res/values-tr/strings.xml
index 086306b609..87ba56717d 100644
--- a/OsmAnd-telegram/res/values-tr/strings.xml
+++ b/OsmAnd-telegram/res/values-tr/strings.xml
@@ -267,4 +267,8 @@
Son cevap: %1$s önce%1$s önceHATA
+ Dışa aktar
+ Logcat tamponu
+ Uygulamanın ayrıntılı günlük kayıtlarına göz atın ve paylaşın
+ Rapor gönder
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-uk/strings.xml b/OsmAnd-telegram/res/values-uk/strings.xml
index 1ff2bebb09..e370c05360 100644
--- a/OsmAnd-telegram/res/values-uk/strings.xml
+++ b/OsmAnd-telegram/res/values-uk/strings.xml
@@ -118,7 +118,7 @@
НалаштуванняЗастосунок не має дозволу до отримання даних позиціювання.Будь ласка, увімкніть «Позиціювання» у системних налаштуваннях
- Фоновий режим
+ Режим тлаOsmAnd Tracker працює у фоновому режимі з вимкненим екраном.ВідстаньПоділитися позицією
@@ -267,4 +267,8 @@
Остання відповідь: %1$s тому%1$s томуПМЛК
+ Експорт
+ Буфер logcat
+ Переглянути та надіслати докладний журнал застосунку
+ Надіслати звіт
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values-zh-rTW/strings.xml b/OsmAnd-telegram/res/values-zh-rTW/strings.xml
index f1f06a0daa..7739c65184 100644
--- a/OsmAnd-telegram/res/values-zh-rTW/strings.xml
+++ b/OsmAnd-telegram/res/values-zh-rTW/strings.xml
@@ -270,4 +270,8 @@
最後回應:%1$s 前%1$s 前ERR
+ 傳送報告
+ 匯出
+ Logcat 緩衝
+ 檢查及分享應用程式的詳細紀錄
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/values/dimens.xml b/OsmAnd-telegram/res/values/dimens.xml
index 7f4942dc3e..60976ff822 100644
--- a/OsmAnd-telegram/res/values/dimens.xml
+++ b/OsmAnd-telegram/res/values/dimens.xml
@@ -27,6 +27,7 @@
89dp48dp
+ 44dp42dp56dp
diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml
index 2b5eca334e..8bccf957ce 100644
--- a/OsmAnd-telegram/res/values/strings.xml
+++ b/OsmAnd-telegram/res/values/strings.xml
@@ -1,5 +1,9 @@
+ Send report
+ Check and share detailed logs of the app
+ Logcat buffer
+ ExportERRLast update from Telegram: %1$sLast response: %1$s
diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt
index ee6bbb29d4..b6927dc610 100644
--- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt
+++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt
@@ -3,16 +3,20 @@ package net.osmand.telegram
import android.app.Application
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.os.Build
import android.os.Handler
+import net.osmand.PlatformUtil
+import net.osmand.telegram.ui.TrackerLogcatActivity
import net.osmand.telegram.helpers.*
import net.osmand.telegram.helpers.OsmandAidlHelper.OsmandHelperListener
import net.osmand.telegram.helpers.OsmandAidlHelper.UpdatesListener
import net.osmand.telegram.notifications.NotificationHelper
import net.osmand.telegram.utils.AndroidUtils
import net.osmand.telegram.utils.UiUtils
+import java.io.File
class TelegramApplication : Application() {
@@ -200,4 +204,33 @@ class TelegramApplication : Application() {
fun runInUIThread(action: (() -> Unit), delay: Long) {
uiHandler.postDelayed(action, delay)
}
+
+ fun sendCrashLog(file: File) {
+ val intent = Intent(Intent.ACTION_SEND)
+ intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("crash@osmand.net"))
+ intent.putExtra(Intent.EXTRA_STREAM, AndroidUtils.getUriForFile(this, file))
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ intent.type = "vnd.android.cursor.dir/email"
+ intent.putExtra(Intent.EXTRA_SUBJECT, "OsmAnd bug")
+ val text = StringBuilder()
+ text.append("\nDevice : ").append(Build.DEVICE)
+ text.append("\nBrand : ").append(Build.BRAND)
+ text.append("\nModel : ").append(Build.MODEL)
+ text.append("\nProduct : ").append(Build.PRODUCT)
+ text.append("\nBuild : ").append(Build.DISPLAY)
+ text.append("\nVersion : ").append(Build.VERSION.RELEASE)
+ text.append("\nApp : ").append(getString(R.string.app_name_short))
+ try {
+ val info = packageManager.getPackageInfo(packageName, 0)
+ if (info != null) {
+ text.append("\nApk Version : ").append(info.versionName).append(" ").append(info.versionCode)
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ PlatformUtil.getLog(TrackerLogcatActivity::class.java).error("", e)
+ }
+ intent.putExtra(Intent.EXTRA_TEXT, text.toString())
+ val chooserIntent = Intent.createChooser(intent, getString(R.string.send_report))
+ chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(chooserIntent)
+ }
}
diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt
index d8ec28a9c0..d9ec335a0e 100644
--- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt
+++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt
@@ -110,7 +110,7 @@ private const val PROXY_ENABLED = "proxy_enabled"
private const val PROXY_PREFERENCES_KEY = "proxy_preferences"
private const val SHARING_INITIALIZATION_TIME = 60 * 2L // 2 minutes
-private const val WAITING_TDLIB_TIME = 3 // 3 seconds
+private const val WAITING_TDLIB_TIME = 7 // 7 seconds
private const val GPS_UPDATE_EXPIRED_TIME = 60 * 3L // 3 minutes
@@ -540,14 +540,24 @@ class TelegramSettings(private val app: TelegramApplication) {
if (initTime && initSending) {
initializing = true
} else {
+ var waitingTimeError = false
val maxWaitingTime = WAITING_TDLIB_TIME * MAX_MESSAGES_IN_TDLIB_PER_CHAT * max(1, chatsCount)
- val textSharingError = !shareInfo.lastTextMessageHandled && currentTime - shareInfo.lastSendTextMessageTime > maxWaitingTime
- val mapSharingError = !shareInfo.lastMapMessageHandled && currentTime - shareInfo.lastSendMapMessageTime > maxWaitingTime
- if (shareInfo.hasSharingError
- || (shareTypeValue == SHARE_TYPE_MAP_AND_TEXT && (textSharingError || mapSharingError))
- || textSharingError && (shareTypeValue == SHARE_TYPE_TEXT)
- || mapSharingError && (shareTypeValue == SHARE_TYPE_MAP)
- ) {
+ val textSharingWaitingTime = currentTime - shareInfo.lastSendTextMessageTime
+ val mapSharingWaitingTime = currentTime - shareInfo.lastSendMapMessageTime
+ val textSharingError = !shareInfo.lastTextMessageHandled && textSharingWaitingTime > maxWaitingTime
+ val mapSharingError = !shareInfo.lastMapMessageHandled && mapSharingWaitingTime > maxWaitingTime
+ if ((shareTypeValue == SHARE_TYPE_MAP_AND_TEXT && (textSharingError || mapSharingError))
+ || textSharingError && (shareTypeValue == SHARE_TYPE_TEXT)
+ || mapSharingError && (shareTypeValue == SHARE_TYPE_MAP)) {
+ waitingTimeError = true
+ log.debug("Send chats error for share type \"$shareTypeValue\"" +
+ "\nMax waiting time: ${maxWaitingTime}s" +
+ "\nLast text message handled: ${shareInfo.lastTextMessageHandled}" +
+ "\nText sharing waiting time: ${textSharingWaitingTime}s" +
+ "\nLast map message handled: ${shareInfo.lastMapMessageHandled}" +
+ "\nMap sharing waiting time: ${mapSharingWaitingTime}s")
+ }
+ if (shareInfo.hasSharingError || waitingTimeError) {
sendChatsErrors = true
locationTime = max(shareInfo.lastTextSuccessfulSendTime, shareInfo.lastMapSuccessfulSendTime)
chatsIds.add(shareInfo.chatId)
diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt
index e4d5188a2b..704a797af2 100644
--- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt
+++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt
@@ -776,6 +776,7 @@ class TelegramHelper private constructor() {
client?.send(TdApi.CreatePrivateChat(userId, false)) { obj ->
when (obj.constructor) {
TdApi.Error.CONSTRUCTOR -> {
+ log.debug("createPrivateChatWithUser ERROR $obj")
val error = obj as TdApi.Error
if (error.code != IGNORED_ERROR_CODE) {
shareInfo.hasSharingError = true
@@ -969,7 +970,7 @@ class TelegramHelper private constructor() {
val messageType = if (isBot) MESSAGE_TYPE_BOT else MESSAGE_TYPE_TEXT
when (obj.constructor) {
TdApi.Error.CONSTRUCTOR -> {
- log.debug("handleTextLocationMessageUpdate - ERROR")
+ log.debug("handleTextLocationMessageUpdate - ERROR $obj")
val error = obj as TdApi.Error
if (error.code != IGNORED_ERROR_CODE) {
shareInfo.hasSharingError = true
diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt
index a5bf5993e9..447275815e 100644
--- a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt
+++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt
@@ -213,6 +213,12 @@ class SettingsDialogFragment : BaseDialogFragment() {
DisconnectTelegramBottomSheet.showInstance(childFragmentManager)
}
+ mainView.findViewById(R.id.logcat_row).setOnClickListener {
+ val intent = Intent(activity, TrackerLogcatActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ app.startActivity(intent)
+ }
+
return mainView
}
diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/TrackerLogcatActivity.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/TrackerLogcatActivity.kt
new file mode 100644
index 0000000000..ee62ab4407
--- /dev/null
+++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/TrackerLogcatActivity.kt
@@ -0,0 +1,271 @@
+package net.osmand.telegram.ui
+
+import android.os.AsyncTask
+import android.os.Bundle
+import android.view.*
+import android.widget.ProgressBar
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.Toolbar
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import net.osmand.PlatformUtil
+import net.osmand.telegram.R
+import net.osmand.telegram.TelegramApplication
+import java.io.*
+import java.lang.ref.WeakReference
+import java.util.*
+
+class TrackerLogcatActivity : AppCompatActivity() {
+ private var logcatAsyncTask: LogcatAsyncTask? = null
+ private val logs: MutableList = ArrayList()
+ private var adapter: LogcatAdapter? = null
+ private val LEVELS = arrayOf("D", "I", "W", "E")
+ private var filterLevel = 1
+ private lateinit var recyclerView: RecyclerView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ val app: TelegramApplication = getApplication() as TelegramApplication
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_tracker_logcat)
+
+ val toolbar = findViewById(R.id.toolbar).apply {
+ navigationIcon = app.uiUtils.getThemedIcon(R.drawable.ic_arrow_back)
+ setNavigationOnClickListener { onBackPressed() }
+ }
+ setSupportActionBar(toolbar)
+ setupIntermediateProgressBar()
+
+ adapter = LogcatAdapter()
+ recyclerView = findViewById(R.id.recycler_view) as RecyclerView
+ recyclerView!!.layoutManager = LinearLayoutManager(this)
+ recyclerView!!.adapter = adapter
+ }
+
+ protected fun setupIntermediateProgressBar() {
+ val progressBar = ProgressBar(this)
+ progressBar.visibility = View.GONE
+ progressBar.isIndeterminate = true
+ val supportActionBar = supportActionBar
+ if (supportActionBar != null) {
+ supportActionBar.setDisplayShowCustomEnabled(true)
+ supportActionBar.customView = progressBar
+ setSupportProgressBarIndeterminateVisibility(false)
+ }
+ }
+
+ override fun setSupportProgressBarIndeterminateVisibility(visible: Boolean) {
+ val supportActionBar = supportActionBar
+ if (supportActionBar != null) {
+ supportActionBar.customView.visibility = if (visible) View.VISIBLE else View.GONE
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ startLogcatAsyncTask()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ stopLogcatAsyncTask()
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ val app: TelegramApplication = applicationContext as TelegramApplication
+ val share: MenuItem = menu.add(0, SHARE_ID, 0, R.string.shared_string_export)
+ share.icon = app.uiUtils.getThemedIcon(R.drawable.ic_action_share)
+ share.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ val level = menu.add(0, LEVEL_ID, 0, "")
+ level.title = getFilterLevel()
+ level.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ private fun getFilterLevel(): String {
+ return "*:" + LEVELS[filterLevel]
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ val itemId = item.itemId
+ when (itemId) {
+ android.R.id.home -> {
+ finish()
+ return true
+ }
+ LEVEL_ID -> {
+ filterLevel++
+ if (filterLevel >= LEVELS.size) {
+ filterLevel = 0
+ }
+ item.title = getFilterLevel()
+ stopLogcatAsyncTask()
+ logs.clear()
+ adapter!!.notifyDataSetChanged()
+ startLogcatAsyncTask()
+ return true
+ }
+ SHARE_ID -> {
+ startSaveLogsAsyncTask()
+ return true
+ }
+ }
+ return false
+ }
+
+ private fun startSaveLogsAsyncTask() {
+ val saveLogsAsyncTask = SaveLogsAsyncTask(this, logs)
+ saveLogsAsyncTask.execute()
+ }
+
+ private fun startLogcatAsyncTask() {
+ logcatAsyncTask = LogcatAsyncTask(this, getFilterLevel())
+ logcatAsyncTask!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
+ }
+
+ private fun stopLogcatAsyncTask() {
+ if (logcatAsyncTask != null && logcatAsyncTask!!.status == AsyncTask.Status.RUNNING) {
+ logcatAsyncTask!!.cancel(false)
+ logcatAsyncTask!!.stopLogging()
+ }
+ }
+
+ private inner class LogcatAdapter : RecyclerView.Adapter() {
+ override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ val inflater = LayoutInflater.from(viewGroup.context)
+ val itemView = inflater.inflate(R.layout.item_description_long, viewGroup, false) as TextView
+ itemView.gravity = Gravity.CENTER_VERTICAL
+ return LogViewHolder(itemView)
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ if (holder is LogViewHolder) {
+ val log = getLog(position)
+ holder.logTextView.text = log
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return logs.size
+ }
+
+ private fun getLog(position: Int): String {
+ return logs[position]
+ }
+
+ private inner class LogViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val logTextView: TextView = itemView.findViewById(R.id.description)
+ }
+ }
+
+ class SaveLogsAsyncTask internal constructor(logcatActivity: TrackerLogcatActivity, logs: Collection) : AsyncTask() {
+ private val logcatActivity: WeakReference
+ private val logs: Collection
+
+ override fun onPreExecute() {
+ val activity = logcatActivity.get()
+ activity?.setSupportProgressBarIndeterminateVisibility(true)
+ }
+
+ override fun doInBackground(vararg voids: Void?): File {
+ val app: TelegramApplication = logcatActivity.get()?.applicationContext as TelegramApplication
+ val file = File(app.getExternalFilesDir(null), LOGCAT_PATH)
+ try {
+ if (file.exists()) {
+ file.delete()
+ }
+ val stringBuilder = StringBuilder()
+ for (log in logs) {
+ stringBuilder.append(log)
+ stringBuilder.append("\n")
+ }
+ if (file.parentFile.canWrite()) {
+ val writer = BufferedWriter(FileWriter(file, true))
+ writer.write(stringBuilder.toString())
+ writer.close()
+ }
+ } catch (e: Exception) {
+ log.error(e)
+ }
+ return file
+ }
+
+ override fun onPostExecute(file: File?) {
+ val activity = logcatActivity.get()
+ if (activity != null && file != null) {
+ val app: TelegramApplication = activity.applicationContext as TelegramApplication
+ activity.setSupportProgressBarIndeterminateVisibility(false)
+ app.sendCrashLog(file)
+ }
+ }
+
+ init {
+ this.logcatActivity = WeakReference(logcatActivity)
+ this.logs = logs
+ }
+ }
+
+ class LogcatAsyncTask internal constructor(logcatActivity: TrackerLogcatActivity?, filterLevel: String) : AsyncTask() {
+ private var processLogcat: Process? = null
+ private val logcatActivity: WeakReference
+ private val filterLevel: String
+
+ override fun doInBackground(vararg voids: Void?): Void? {
+ try {
+ val filter = android.os.Process.myPid().toString()
+ val command = arrayOf("logcat", filterLevel, "--pid=$filter", "-T", MAX_BUFFER_LOG.toString())
+ processLogcat = Runtime.getRuntime().exec(command)
+ val bufferedReader = BufferedReader(InputStreamReader(processLogcat?.inputStream))
+ var line: String?
+ while (bufferedReader.readLine().also { line = it } != null && logcatActivity.get() != null) {
+ if (isCancelled) {
+ break
+ }
+ publishProgress(line)
+ }
+ stopLogging()
+ } catch (e: IOException) { // ignore
+ } catch (e: Exception) {
+ log.error(e)
+ }
+ return null
+ }
+
+ override fun onProgressUpdate(vararg values: String?) {
+ if (values.size > 0 && !isCancelled) {
+ val activity = logcatActivity.get()
+ if (activity != null) {
+ val autoscroll = !activity.recyclerView!!.canScrollVertically(1)
+ for (s in values) {
+ if (s != null) {
+ activity.logs.add(s)
+ }
+ }
+ activity.adapter!!.notifyDataSetChanged()
+ if (autoscroll) {
+ activity.recyclerView!!.scrollToPosition(activity.logs.size - 1)
+ }
+ }
+ }
+ }
+
+ fun stopLogging() {
+ if (processLogcat != null) {
+ processLogcat!!.destroy()
+ }
+ }
+
+ init {
+ this.logcatActivity = WeakReference(logcatActivity)
+ this.filterLevel = filterLevel
+ }
+ }
+
+ companion object {
+ private const val LOGCAT_PATH = "logcat.log"
+ private const val MAX_BUFFER_LOG = 10000
+ private const val SHARE_ID = 0
+ private const val LEVEL_ID = 1
+ private val log = PlatformUtil.getLog(TrackerLogcatActivity::class.java)
+ }
+}
diff --git a/OsmAnd/.gitignore b/OsmAnd/.gitignore
index e3071e5fbf..8bfbd5b688 100644
--- a/OsmAnd/.gitignore
+++ b/OsmAnd/.gitignore
@@ -13,10 +13,13 @@ libs/it.unibo.alice.tuprolog-tuprolog-3.2.1.jar
libs/commons-codec-commons-codec-1.11.jar
libs/OsmAndCore_android-0.1-SNAPSHOT.jar
+# Huawei
libs/huawei-*.jar
huaweidrmlib/
HwDRM_SDK_*
drm_strings.xml
+agconnect-services.json
+OsmAndHms.jks
# copy_widget_icons.sh
res/drawable-large/map_*
diff --git a/OsmAnd/AndroidManifest-freehuawei.xml b/OsmAnd/AndroidManifest-freehuawei.xml
index e96bb7fe47..c557d5fe2a 100644
--- a/OsmAnd/AndroidManifest-freehuawei.xml
+++ b/OsmAnd/AndroidManifest-freehuawei.xml
@@ -2,24 +2,31 @@
-
-
-
-
+
+
+
+
+
+
+
+
-
+ tools:replace="android:authorities"
+ android:authorities="net.osmand.huawei.fileprovider"/>
-
\ No newline at end of file
diff --git a/OsmAnd/AndroidManifest-huawei.xml b/OsmAnd/AndroidManifest-huawei.xml
deleted file mode 100644
index bc847980cd..0000000000
--- a/OsmAnd/AndroidManifest-huawei.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle
index ddac6aee41..ee9d4dd684 100644
--- a/OsmAnd/build.gradle
+++ b/OsmAnd/build.gradle
@@ -40,6 +40,15 @@ android {
keyAlias "osmand"
keyPassword System.getenv("OSMAND_APK_PASSWORD")
}
+
+ publishingHuawei {
+ storeFile file("/var/lib/jenkins/osmand_hw_key")
+ storePassword System.getenv("OSMAND_HW_APK_PASSWORD")
+ keyAlias "osmand"
+ keyPassword System.getenv("OSMAND_HW_APK_PASSWORD")
+ v1SigningEnabled true
+ v2SigningEnabled true
+ }
}
defaultConfig {
@@ -107,19 +116,26 @@ android {
debug {
manifest.srcFile "AndroidManifest-debug.xml"
}
+ full {
+ java.srcDirs = ["src-google"]
+ }
+ fulldev {
+ java.srcDirs = ["src-google"]
+ }
free {
+ java.srcDirs = ["src-google"]
manifest.srcFile "AndroidManifest-free.xml"
}
freedev {
+ java.srcDirs = ["src-google"]
manifest.srcFile "AndroidManifest-freedev.xml"
}
freecustom {
+ java.srcDirs = ["src-google"]
manifest.srcFile "AndroidManifest-freecustom.xml"
}
- huawei {
- manifest.srcFile "AndroidManifest-huawei.xml"
- }
freehuawei {
+ java.srcDirs = ["src-huawei"]
manifest.srcFile "AndroidManifest-freehuawei.xml"
}
@@ -185,21 +201,16 @@ android {
dimension "version"
applicationId "net.osmand.plus"
}
- fulldev {
- dimension "version"
- applicationId "net.osmand.plus"
- resConfig "en"
- //resConfigs "xxhdpi", "nodpi"
- }
- huawei {
+ fulldev {
dimension "version"
- applicationId "net.osmand.plus.huawei"
+ applicationId "net.osmand.plus"
+ resConfig "en"
+ // resConfigs "xxhdpi", "nodpi"
}
freehuawei {
dimension "version"
applicationId "net.osmand.huawei"
}
-
// CoreVersion
legacy {
dimension "coreversion"
@@ -223,7 +234,11 @@ android {
release {
buildConfigField "String", "OSM_OAUTH_CONSUMER_KEY", "\"Ti2qq3fo4i4Wmuox3SiWRIGq3obZisBHnxmcM05y\""
buildConfigField "String", "OSM_OAUTH_CONSUMER_SECRET", "\"lxulb3HYoMmd2cC4xxNe1dyfRMAY8dS0eNihJ0DM\""
- signingConfig signingConfigs.publishing
+ if (gradle.startParameter.taskNames.toString().contains("huawei")) {
+ signingConfig signingConfigs.publishingHuawei
+ } else {
+ signingConfig signingConfigs.publishing
+ }
}
}
@@ -280,46 +295,6 @@ task downloadWorldMiniBasemap {
}
}
-task downloadHuaweiDrmZip {
- doLast {
- ant.get(src: 'https://obs.cn-north-2.myhwclouds.com/hms-ds-wf/sdk/HwDRM_SDK_2.5.2.300_ADT.zip', dest: 'HwDRM_SDK_2.5.2.300_ADT.zip', skipexisting: 'true')
- ant.unzip(src: 'HwDRM_SDK_2.5.2.300_ADT.zip', dest: 'huaweidrmlib/')
- }
-}
-
-task copyHuaweiDrmLibs(type: Copy) {
- dependsOn downloadHuaweiDrmZip
- from "huaweidrmlib/HwDRM_SDK_2.5.2.300_ADT/libs"
- into "libs"
-}
-
-task copyHuaweiDrmValues(type: Copy) {
- dependsOn downloadHuaweiDrmZip
- from "huaweidrmlib/HwDRM_SDK_2.5.2.300_ADT/res"
- into "res"
-}
-
-task downloadPrebuiltHuaweiDrm {
- dependsOn copyHuaweiDrmLibs, copyHuaweiDrmValues
-}
-
-task cleanHuaweiDrmLibs(type: Delete) {
- delete "huaweidrmlib"
- delete fileTree("libs").matching {
- include "**/huawei-*.jar"
- }
-}
-
-task cleanHuaweiDrmValues(type: Delete) {
- delete fileTree("res").matching {
- include "**/drm_strings.xml"
- }
-}
-
-task cleanPrebuiltHuaweiDrm {
- dependsOn cleanHuaweiDrmLibs, cleanHuaweiDrmValues
-}
-
task collectVoiceAssets(type: Sync) {
from "../../resources/voice"
into "assets/voice"
@@ -401,8 +376,6 @@ task copyLargePOIIcons(type: Sync) {
}
}
-
-
task copyWidgetIconsXhdpi(type: Sync) {
from "res/drawable-xxhdpi/"
into "res/drawable-large-xhdpi/"
@@ -448,15 +421,6 @@ task collectExternalResources {
copyWidgetIconsXhdpi,
copyPoiCategories,
downloadWorldMiniBasemap
-
- Gradle gradle = getGradle()
- String tskReqStr = gradle.getStartParameter().getTaskRequests().toString().toLowerCase()
- // Use Drm SDK only for huawei build
- if (tskReqStr.contains("huawei")) {
- dependsOn downloadPrebuiltHuaweiDrm
- } else {
- dependsOn cleanPrebuiltHuaweiDrm
- }
}
// Legacy core build
@@ -507,10 +471,16 @@ task cleanupDuplicatesInCore() {
file("libs/x86_64/libc++_shared.so").renameTo(file("libc++/x86_64/libc++_shared.so"))
}
}
+
afterEvaluate {
android.applicationVariants.all { variant ->
variant.javaCompiler.dependsOn(collectExternalResources, buildOsmAndCore, cleanupDuplicatesInCore)
}
+ Gradle gradle = getGradle()
+ String tskReqStr = gradle.getStartParameter().getTaskRequests().toString().toLowerCase()
+ if (tskReqStr.contains("huawei")) {
+ apply plugin: 'com.huawei.agconnect'
+ }
}
task appStart(type: Exec) {
@@ -520,7 +490,6 @@ task appStart(type: Exec) {
// commandLine 'cmd', '/c', 'adb', 'shell', 'am', 'start', '-n', 'net.osmand.plus/net.osmand.plus.activities.MapActivity'
}
-
dependencies {
implementation project(path: ':OsmAnd-java', configuration: 'android')
implementation project(':OsmAnd-api')
@@ -572,6 +541,5 @@ dependencies {
}
implementation 'com.jaredrummler:colorpicker:1.1.0'
- huaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar')
- freehuaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar')
+ freehuaweiImplementation 'com.huawei.hms:iap:5.0.2.300'
}
diff --git a/OsmAnd/res/drawable/ic_action_direction_arrow.xml b/OsmAnd/res/drawable/ic_action_direction_arrow.xml
index 5e97fe8338..49bec48f22 100644
--- a/OsmAnd/res/drawable/ic_action_direction_arrow.xml
+++ b/OsmAnd/res/drawable/ic_action_direction_arrow.xml
@@ -1,9 +1,9 @@
+ android:viewportHeight="12">
diff --git a/OsmAnd/res/layout/bottom_buttons_vertical.xml b/OsmAnd/res/layout/bottom_buttons_vertical.xml
new file mode 100644
index 0000000000..0de7d48389
--- /dev/null
+++ b/OsmAnd/res/layout/bottom_buttons_vertical.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/layout/bottom_sheet_item_with_switch.xml b/OsmAnd/res/layout/bottom_sheet_item_with_switch.xml
index 955c2937b2..53b21954ee 100644
--- a/OsmAnd/res/layout/bottom_sheet_item_with_switch.xml
+++ b/OsmAnd/res/layout/bottom_sheet_item_with_switch.xml
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="@dimen/bottom_sheet_list_item_height"
+ android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="@dimen/bottom_sheet_list_item_height"
@@ -28,7 +28,7 @@
android:layout_marginRight="@dimen/content_padding"
android:layout_weight="1"
android:ellipsize="end"
- android:maxLines="1"
+ android:maxLines="4"
android:textAppearance="@style/TextAppearance.ListItemTitle"
tools:text="Some Title"/>
diff --git a/OsmAnd/res/layout/bottom_sheet_menu_base.xml b/OsmAnd/res/layout/bottom_sheet_menu_base.xml
index 5110455a7c..d265ba938c 100644
--- a/OsmAnd/res/layout/bottom_sheet_menu_base.xml
+++ b/OsmAnd/res/layout/bottom_sheet_menu_base.xml
@@ -35,10 +35,8 @@
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_gravity="bottom"
- android:visibility="gone"
- android:background="@drawable/bg_contextmenu_shadow_top_light" />
+ android:background="@drawable/bg_contextmenu_shadow_top_light"
+ android:visibility="gone" />
-
-
-
+
\ No newline at end of file
diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml
index f3bbf28ac6..c1426b8483 100644
--- a/OsmAnd/res/values-ar/strings.xml
+++ b/OsmAnd/res/values-ar/strings.xml
@@ -721,9 +721,9 @@
عكس اتجاه المساراستخدم الوجهة الحاليةيمر على طول المسار باكمله
- خريطة التنقل متوفرة حالياً لهذا الموقع.
+ خريطة التنقل متوفرة لهذا الموقع فعلها عبر
\n
-\nلتفعليها \'القائمة\' ← \'ضبط الخريطة\' ← \'مصدر الخريطة\' ← \'الخريطة المحملة\'.
+\n\'القائمة\' ← \'ضبط الخريطة\' ← \'مصدر الخريطة\' ← \'الخريطة المحملة\'.مصدر التوجيه الصوتياختيار قناة لتشغيل التوجيه الصوتي.صوت المكالمة الهاتفية ( كما يحاول قطع ستريو بلوتوث السيارة )
@@ -1645,7 +1645,7 @@
الطريق محظورتحديداعكس نقطة الانطلاق والوصول
- أيقونات POI
+ أيقونات نقاط الاهتمامالنوعغير محددقسم مسجل
@@ -1972,7 +1972,7 @@
بطاقة الذاكرة غير متاحة.
\nلن تكون قادرا على رؤية الخرائط أو العثور على أماكن.بطاقة الذاكرة في وضع القراءة فقط.
-\n يمكنك فقط مشاهدة الخريطة المحملة مسبقا ولا يمكنك التحميل من الإنترنت.
+\n يمكنك فقط مشاهدة الخريطة المحملة مسبقاً ولا يمكنك التحميل من الإنترنت.انعطف يميناً بشكل حادانعطف يساراً بشكل حادقم بالدوران وواصل
@@ -3891,4 +3891,6 @@
آخر تعديلالاسم: أ – يالاسم: أ – ي
+ رموز البدء/الانتهاء
+ شكرا لشرائك \"خطوط الكنتور\"
\ No newline at end of file
diff --git a/OsmAnd/res/values-cs/phrases.xml b/OsmAnd/res/values-cs/phrases.xml
index e63732cdc6..8d64234b3a 100644
--- a/OsmAnd/res/values-cs/phrases.xml
+++ b/OsmAnd/res/values-cs/phrases.xml
@@ -3844,4 +3844,5 @@
ŠipkaVibraceTlak
+ Zkapalněný zemní plyn
\ No newline at end of file
diff --git a/OsmAnd/res/values-cs/strings.xml b/OsmAnd/res/values-cs/strings.xml
index 64f1dc8949..59b0baf68d 100644
--- a/OsmAnd/res/values-cs/strings.xml
+++ b/OsmAnd/res/values-cs/strings.xml
@@ -3525,4 +3525,57 @@ Zobrazená oblast: %1$s x %2$sSmazat následující cílový bodUmožní ovládat úroveň přiblížení mapy pomocí tlačítek hlasitosti zařízení.Tlačítka hlasitosti pro přibližování
+ Mezní vzdálenost
+ Navigační profil
+ Vyberte soubor stopy, do níž se nový úsek přidá.
+ Snímky z úrovně ulice
+ Opravdu chcete zavřít plánovanou trasu a zahodit tak všechny změny\?
+ V případě opačného směru
+ Uložit jako nový soubor stopy
+ Přidat do souboru stopy
+ Stopy
+ Stopy
+ Stopy
+ "Ukládat trasu do GPX souboru"
+ Trasa ze stopy
+ Přidat soubory stop
+ Zjednodušená trasa
+ Uloží se pouze linie trasy, mezicíle budou odstraněny.
+ Název souboru
+ %s vybraných souborů stop
+ REC
+ Pozastaví záznam trasy, pokud je aplikace ukončena (přes nedávné aplikace). (Ikona režimu na pozadí zmizí z notifikační oblasti Androidu.)
+ Pozastavit záznam trasy
+ Pokračovat v záznamu trasy
+ Výchozí
+ Všechny následující úseky
+ Přecházející úsek
+ Všechny předcházející úseky
+ Pouze vybraný úsek bude přepočítán pomocí vybraného profilu.
+ Všechny následující úseky budou přepočítány pomocí vybraného profilu.
+ Všechny předcházející úseky budou přepočítány pomocí vybraného profilu.
+ Otevřít uloženou trasu
+ je uloženo
+ Přidejte prosím alespoň dva body.
+ Znovu
+ • Vylepšená funkce plánování trasy: umožňuje použít různé typy navigace pro úseky trasy a přidává stopy
+\n
+\n • Nové menu pro vzhled stop: zvolte barvu, tloušťku, směrové šipky, ikony startu a cíle
+\n
+\n • Vylepšená viditelnost bodů pro cyklistiku.
+\n
+\n • Stopy je nyní možné aktivovat, nabízejí kontextové menu se základními údaji.
+\n
+\n • Vylepšený algoritmus vyhledávání
+\n
+\n • Vylepšené možnosti následování stopy v navigaci
+\n
+\n • Opravené problémy s importem a exportem nastavení profilů
+\n
+\n
+ Naposledy změněno
+ Název: Z – A
+ Název: A – Z
+ Ikony startu/cíle
+ Děkujeme za zakoupení modulu \'Vrstevnice\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-da/phrases.xml b/OsmAnd/res/values-da/phrases.xml
index 3e1f8c0d81..9f26ab3b43 100644
--- a/OsmAnd/res/values-da/phrases.xml
+++ b/OsmAnd/res/values-da/phrases.xml
@@ -3842,4 +3842,14 @@
DonationsboksPil: nejElevator
+ Nøddebutik
+ Bikube
+ Tidsplan
+ Realtid
+ Forsinkelse
+ Ja
+ Afgangstavle: nej
+ Små elektriske apparater
+ Afgangstavle
+ Genopfyldning af drikkevand
\ No newline at end of file
diff --git a/OsmAnd/res/values-da/strings.xml b/OsmAnd/res/values-da/strings.xml
index 058b72d771..0e05949cf0 100644
--- a/OsmAnd/res/values-da/strings.xml
+++ b/OsmAnd/res/values-da/strings.xml
@@ -3243,7 +3243,7 @@
Advarsler vises nederst til venstre under navigationen.Skift profilSprog og output
- Gendan standardværdi
+ Nulstil til standardOpret, importer, rediger profilerAdministrer programprofiler…Påvirker hele programmet
@@ -3756,8 +3756,8 @@
Slet adresseTilføj adresseAngiv adresse
- Trim før
- Trim efter
+ Trimme før
+ Trimme efterSkift rutetype førSkift rutetype efterForenklet spor
@@ -3771,4 +3771,21 @@
er gemtTilføj mindst to punkter.Omgøre
+ Navigere fra position til sporet
+ Punkt på sporet for at navigere
+ %s sporfiler valgt
+ Sidst ændret
+ Navn: Z – A
+ Navn: A – Z
+ Vælg en sporfil, der skal åbnes.
+ Vælg en sporfil, som det nye segment skal føjes til.
+ Kasser alle ændringer i den planlagte rute ved at lukke den\?
+ Føj til en sporfil
+ Vælg sporfil, der skal følges eller importeres fra enheden.
+ Kun rutelinjen gemmes, rutepunkter slettes.
+ Alle efterfølgende segmenter
+ Kun det valgte segment genberegnes igen ved hjælp af den valgte profil.
+ Alle efterfølgende segmenter genberegnes ved hjælp af den valgte profil.
+ Alle tidligere segmenter genberegnes ved hjælp af den valgte profil.
+ Start-/slutikoner
\ No newline at end of file
diff --git a/OsmAnd/res/values-de/phrases.xml b/OsmAnd/res/values-de/phrases.xml
index d091dc7fd5..d40597ca63 100644
--- a/OsmAnd/res/values-de/phrases.xml
+++ b/OsmAnd/res/values-de/phrases.xml
@@ -3847,4 +3847,5 @@
BienenstockKleine ElektrogeräteNussladen
+ Flüssigerdgas
\ No newline at end of file
diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml
index 319e1c4f6f..1fcf32f445 100644
--- a/OsmAnd/res/values-de/strings.xml
+++ b/OsmAnd/res/values-de/strings.xml
@@ -3876,9 +3876,7 @@
Track Datei zum Folgen auswählen, oder vom Gerät importieren.Die GPX-Aufzeichnung wird angehalten, wenn OsmAnd beendet wird (über „zuletzt verwendete Apps“). (Die Hintergrunddienst-Anzeige verschwindet aus der Android-Benachrichtigungsleiste.)Aufzeichnungsintervall für die generelle Track-Aufzeichnung festlegen (via Schaltfläche \'GPX\' auf dem Kartenbildschirm).
- Um diese Option nutzen zu können, muss OsmAnd den Track auf die Straßen der Karte einrasten.
-\n
-\n Wählen Sie im nächsten Schritt ein Navigationsprofil um festzulegen, welche Straßentypen verwendet werden sollen, und wählen Sie einen Wert für die maximal zulässige Entfernung zwischen Track und Straße.
+ Als nächstes können Sie Ihren Track mit einem Ihrer Navigationsprofile auf die nächstgelegene erlaubte Straße einrasten lassen, um diese Option zu nutzen.Track-Wegpunkt hinzufügen%s Track Dateien ausgewähltNur die Routenlinie wird gespeichert, die Wegpunkte werden gelöscht.
@@ -3910,4 +3908,6 @@
Zuletzt geändertName: Z – AName: A – Z
+ Start-/Ziel-Symbole
+ Vielen Dank für den Kauf von \'Höhenlinien\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml
index 4019b950b2..88c35ef6f9 100644
--- a/OsmAnd/res/values-es-rAR/phrases.xml
+++ b/OsmAnd/res/values-es-rAR/phrases.xml
@@ -3851,4 +3851,5 @@
Pequeños electrodomésticosPanal de abejasFrutos secos
+ Gas natural licuado
\ No newline at end of file
diff --git a/OsmAnd/res/values-es-rAR/strings.xml b/OsmAnd/res/values-es-rAR/strings.xml
index 99f789d362..fcdccf360a 100644
--- a/OsmAnd/res/values-es-rAR/strings.xml
+++ b/OsmAnd/res/values-es-rAR/strings.xml
@@ -385,15 +385,15 @@
\nCualquiera de estos mapas puede usarse como el mapa predefinido que se mostrará, o como una superposición o subyacencia de otro mapa base (como los mapas estándar de OsmAnd en línea). Ciertos elementos de los mapas vectoriales de OsmAnd pueden ocultarse a través del menú «Configurar mapa» para hacer cualquier subyacencia mas visible.
\n
\nDescarga las teselas de los mapas directamente en línea, o prepáralo para su uso sin conexión (copiar manualmente en la carpeta de datos OsmAnd) como una base de datos SQLite que puede ser producida por una variedad de herramientas de preparación de mapas de terceros.
- Este complemento activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX.
-\n
+ Activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX.
+\n
\nLas trazas grabadas pueden ser compartidas con tus amigos o ser usadas para contribuir a OSM. Los atletas pueden usar las trazas grabadas para seguir sus entrenamientos. Algunos análisis básicos de trazas se pueden realizar directamente en OsmAnd, como tiempos por vuelta, velocidad media, etc., y por supuesto las trazas pueden analizarse posteriormente con herramientas de análisis de terceros.Complemento de curvas de nivel
- Este complemento proporciona una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje.
-\n
+ Proporciona una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje.
+\n
\nLos datos globales (entre 70° norte y 70° sur) se basan en mediciones de SRTM (Shuttle Radar Topography Mission, o en español Misión de Topografía por Radar desde Transbordador) y ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer, o en español Radiómetro Espacial Avanzado de Emisión Térmica y Reflexión), un instrumento de captura de imágenes a bordo de Terra, el satélite insignia del Sistema de Observación de la Tierra de la NASA. ASTER es un esfuerzo cooperativo entre la NASA, el Ministerio de Economía, Comercio e Industria de Japón (METI) y Sistemas Espaciales de Japón (J-spacesystems).
- Este complemento proporciona tanto una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje. (Note que las curvas de nivel y/o los datos de relieve están disponibles en descargas adicionales separadas luego de activar el complemento.)
-\n
+ Proporciona tanto una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje. (Note que las curvas de nivel y/o los datos de relieve están disponibles en descargas adicionales separadas luego de activar el complemento.)
+\n
\nLos datos globales (entre 70° norte y 70° sur) se basan en mediciones de SRTM (Shuttle Radar Topography Mission, o en español Misión de Topografía por Radar Shuffle) y ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer, o en español Radiómetro Espacial Avanzado de Emisión Térmica y Reflexión), un instrumento de captura de imágenes a bordo de Terra, el satélite insignia del Sistema de Observación de la Tierra de la NASA. ASTER es un esfuerzo cooperativo entre la NASA, el Ministerio de Economía, Comercio e Industria de Japón (METI) y Sistemas Espaciales de Japón (J-spacesystems).Activando esta vista cambia el estilo del mapa OsmAnd a la «Vista turística», que es una vista de alto detalle especial para viajeros y conductores profesionales.
\n
@@ -406,13 +406,13 @@
\nNo es necesario descargar un mapa especial, la vista es creada a partir de nuestros mapas estándar.
\n
\nEsta vista puede ser revertida desactivando de nuevo aquí, o cambiando el «Estilo del mapa» desde «Configurar mapa» cuando lo desees.
- Este complemento enriquece el mapa y la navegación de OsmAnd al producir también mapas náuticos para el canotaje, vela y otros tipos de deportes acuáticos.
-\n
-\nUn mapa especial complementado para OsmAnd proporcionará toda las marcas de navegación náutica y símbolos cartográficos para el interior, así como para la navegación cerca de la costa. La descripción de cada marca de navegación proporciona los datos necesarios para su identificación y su significado (categoría, forma, color, número, referencia, etc.).
-\n
+ Enriquece el mapa y la navegación de OsmAnd al producir también mapas náuticos para el canotaje, vela y otros tipos de deportes acuáticos.
+\n
+\nUn mapa especial complementado para OsmAnd proporcionará toda las marcas de navegación náutica y símbolos cartográficos para el interior, así como para la navegación cerca de la costa. La descripción de cada marca de navegación proporciona los datos necesarios para su identificación y su significado (categoría, forma, color, número, referencia, etc.).
+\n
\nPara volver a uno de los estilos del mapas convencionales de OsmAnd, simplemente desactiva este complemento de nuevo, o cambia el «Estilo del mapa» en «Configurar mapa» cuando lo desees.
- Este complemento para OsmAnd pone a tu alcance detalles sobre pistas de esquí de descenso, de travesía, rutas de esquí alpino, teleféricos y remontes a nivel mundial. Las rutas y pistas se muestran por código de color en función de su dificultad y representados con un estilo del mapa especial «Invierno» que lo asemeja a un paisaje invernal nevado.
-\n
+ Detalles sobre pistas de esquí de descenso, de travesía, rutas de esquí alpino, teleféricos y remontes a nivel mundial. Las rutas y pistas se muestran por código de color en función de su dificultad y representados con un estilo del mapa especial «Invierno» que lo asemeja a un paisaje invernal nevado.
+\n
\nActivando esta vista, cambia el estilo del mapa a «Invierno y esquí», mostrando las características del terreno en condiciones invernales. Esta vista se puede revertir desactivando de nuevo aquí o cambiando el «Estilo del mapa» en «Configurar mapa» cuando lo desees.Toma notas de audio, fotografía y/o video durante un viaje, usando un botón en el mapa o el menú contextual de la ubicación.Registra dónde se ha estacionado el automóvil, incluyendo cuánto tiempo queda.
@@ -3907,4 +3907,6 @@
Último modificadoNombre: Z – ANombre: A – Z
+ Iconos de inicio/fin
+ Gracias por comprar las «Curvas de nivel»
\ No newline at end of file
diff --git a/OsmAnd/res/values-es-rUS/phrases.xml b/OsmAnd/res/values-es-rUS/phrases.xml
index 25786f2488..8c0dbc2414 100644
--- a/OsmAnd/res/values-es-rUS/phrases.xml
+++ b/OsmAnd/res/values-es-rUS/phrases.xml
@@ -3851,4 +3851,5 @@
Tablero de partidasFrutos secosPanal de abejas
+ Gas natural licuado
\ No newline at end of file
diff --git a/OsmAnd/res/values-es-rUS/strings.xml b/OsmAnd/res/values-es-rUS/strings.xml
index a7750a3b93..6ba4dcc29e 100644
--- a/OsmAnd/res/values-es-rUS/strings.xml
+++ b/OsmAnd/res/values-es-rUS/strings.xml
@@ -3904,4 +3904,5 @@
Nombre: Z – ANombre: A – ZÚltimo modificado
+ Iconos de inicio/fin
\ No newline at end of file
diff --git a/OsmAnd/res/values-es/phrases.xml b/OsmAnd/res/values-es/phrases.xml
index d892b94f7e..cac7b4a36d 100644
--- a/OsmAnd/res/values-es/phrases.xml
+++ b/OsmAnd/res/values-es/phrases.xml
@@ -1588,7 +1588,7 @@
KoshintoPlaca azulJizo
- Crucero (monumento)
+ CruzCantera históricaAgregadoAntimonio
@@ -1825,7 +1825,7 @@
Capacitación: artes marcialesCapacitación: aviaciónCapacitación: peluquería
- Monumento
+ Objeto monumentalTipo: industria petroleraTipo: Área de pozosTipo: fábrica
@@ -1868,7 +1868,7 @@
100LL (con plomo, para aviones)Autogas (Etanol libre de plomo)Jet A-1 (diésel)
- AdBlue
+ Líquido de escape de dieselCombustible: maderaCombustible: carbón vegetalCombustible: carbón
@@ -3798,7 +3798,7 @@
TuboRed de recarga de agua potableRecarga de agua potable: no
- Recarga de agua potable: sí
+ SíObstrucciónNivel de agua: por debajo del nivel medio del aguaNivel de agua: por encima del nivel medio del agua
diff --git a/OsmAnd/res/values-es/strings.xml b/OsmAnd/res/values-es/strings.xml
index caf2e2ec40..6041f2a95e 100644
--- a/OsmAnd/res/values-es/strings.xml
+++ b/OsmAnd/res/values-es/strings.xml
@@ -23,7 +23,7 @@
Borrar ediciónEdición asíncrona OSM:PDI/Notas de OSM guardados en el dispositivo
- Muestra y gestiona PDI/notas de OSM guardadas en la base de datos del dispositivo.
+ Muestra y gestiona PDI/Notas de OSM en la base de datos de tu dispositivo.Indica el intervalo del seguimiento en línea.Intervalo del seguimiento en líneaIndica la dirección web con sintaxis de parámetros : lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}.
@@ -163,7 +163,7 @@
El idioma elegido es incompatible con el motor TTS (texto a voz) instalado en Android, se usará el idioma TTS predefinido. ¿Buscar otro motor TTS en la tienda de aplicaciones\?Faltan datos¿Ir a la tienda de aplicaciones para descargar el idioma elegido?
- Invertir la dirección GPX
+ Invertir la dirección de la trazaUsar destino actualPasar a lo largo de la traza completaMapa vectorial presente para esta ubicación.
@@ -265,10 +265,7 @@
Todos los datos sin conexión en la versión vieja de OsmAnd son compatibles con la nueva versión, pero los puntos de Favoritos deben exportarse desde la versión vieja y luego, importarse en la nueva.Compilación {0} instalada ({1}).Descargando compilación…
- ¿Instalar OsmAnd\?
-\nVersión: {0}
-\nFecha: {1}
-\nTamaño: {2} MB
+ ¿Instalar OsmAnd - {0} de {1} {2} MB\?Error al recuperar la lista de compilaciones de OsmAndCargando compilaciones de OsmAnd…Instalar compilación de OsmAnd
@@ -441,7 +438,7 @@
Búsqueda sin conexiónBúsqueda en líneaMáximo zoom en línea
- No buscar en las teselas de mapas en línea para niveles de zoom más allá de esto.
+ No busca en los mapas en línea niveles de zoom más allá de éste.Distancia total %1$s, tiempo de viaje %2$d h %3$d min.Servicios de navegación con o sin conexión.Servicio de navegación
@@ -606,7 +603,7 @@
Para países donde la gente conduce por el lado izquierdo del camino.Punto de partida aún no determinado.¿Cancelar la descarga\?
- El mapa base necesario para proporcionar la funcionalidad básica, está en la cola de descarga.
+ El mapa base necesario para proporcionar funcionalidad básica está en la cola de descargas.Activa el complemento «Mapas en línea», para elegir diferentes fuentes de mapasMapas en línea y teselasUsa mapas en línea (descarga y guarda teselas en la tarjeta de memoria).
@@ -729,8 +726,8 @@
PMAMLugar de aparcamiento
- Este complemento, registra dónde se ha aparcado el automóvil y cuánto tiempo queda (si hay un límite de tiempo).
-\nTanto la ubicación como el tiempo del aparcamiento se muestran en el menú principal y en un widget sobre el mapa. Puedes añadir una notificación al calendario, en el caso de que desees tener un recordatorio al respecto.
+ Te permite egistrar dónde has aparcado el automóvil y cuánto tiempo queda (si hay un límite de tiempo).
+\nTanto la ubicación como el tiempo del aparcamiento se muestran en el menú principal y en un control sobre el mapa. Puedes añadir un recordatorio al calendario, de Android.AparcamientoMarcar como aparcamientoQuitar marcador de aparcamiento
@@ -890,7 +887,7 @@
Curvas de nivelOtros mapasCurvas de nivel
- Este complemento, proporciona la funcionalidad para tomar notas de audio, fotografía y/o video durante un viaje, usando un botón en el mapa, o directamente en el menú contextual para cualquier ubicación en el mapa.
+ Tomar notas de audio/foto/video durante un viaje, usando un botón de mapa o un menú contextual de la ubicación.Notas audio/vídeoComplemento OsmAnd para curvas de nivel sin conexiónEste complemento proporciona una capa superpuesta de curvas de nivel y una capa (de relieve) sombreada, que se pueden visualizar sobre los mapas descargados de OsmAnd. Esta funcionalidad será muy apreciada por atletas, caminantes, excursionistas, y cualquiera interesado en la estructura de relieve de un paisaje.
@@ -930,7 +927,7 @@
Grabando %1$s %3$s %2$sPor favor, considera pagar por el complemento «Curvas de nivel» para apoyar desarrollos adicionales.Complemento de curvas de nivel
- El complemento de Dropbox, permite sincronizar trazas y notas multimedia con tu cuenta de Dropbox.
+ Sincroniza trazas y notas multimedia con tu cuenta de Dropbox.Complemento DropboxCambiar ordenMostrar
@@ -1003,8 +1000,8 @@
PuntoNombre del archivo GPXArchivo GPX guardado en {0}
- Este complemento proporciona un widget en el mapa, permitiendo crear caminos pulsando el mapa, usando o modificando archivos GPX existentes, para planificar un viaje y medir la distancia entre puntos. Los resultados pueden guardarse como un archivo GPX y usarse luego para la orientación.
- Distancias y planificación
+ Crea caminos pulsando en el mapa, o usando o modificando archivos GPX existentes, para planificar un viaje y medir la distancia entre puntos. El resultado puede guardarse como un archivo GPX y usarse luego para la orientación.
+ Calculadora de distancias y herramienta de planificación* Pulse para marcar un punto.
\n * Mantenga pulsado el mapa para quitar el punto anterior.
\n * Mantenga pulsado en un punto para ver e incluir la descripción.
@@ -1307,9 +1304,9 @@
DuraciónDistanciaGrabación de viaje
- Este complemento activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX.
-\n
-\nLas trazas grabadas pueden ser compartidas con sus amigos o ser usadas para contribuir a OSM. Los atletas pueden usar las trazas grabadas para seguir sus entrenamientos. Algunos análisis básicos de trazas se pueden realizar directamente en OsmAnd, como tiempos por vuelta, velocidad media, etc., y por supuesto las trazas pueden analizarse posteriormente con herramientas de análisis de terceros.
+ Este complemento activa la funcionalidad para registrar y guardar tus trazas manualmente pulsando el widget de grabación GPX en el mapa, o automáticamente registrando todas tus rutas navegadas en un archivo GPX.
+\n
+\nLas trazas grabadas pueden ser compartidas con tus amigos o ser usadas para contribuir a OSM. Los atletas pueden usar las trazas grabadas para monitorizar sus entrenamientos. Algunos análisis básicos de trazas pueden realizarse directamente en OsmAnd, como tiempos por vuelta, velocidad media, etc., y por supuesto las trazas pueden analizarse posteriormente con herramientas de análisis de terceros.Rutas de autobús, trolebús y lanzaderaIntervalo de registroRegistra la ubicación en un archivo GPX, pudiendo des/activarlo usando el widget de grabación GPX en el mapa.
@@ -1433,7 +1430,7 @@
Pista de entrenamientoSólo caminosCompartir nota
- Notas
+ Notas A/VMapa en líneaExportarAudio
@@ -1781,7 +1778,7 @@
Se ofrece la opción de controlar la aplicación principalmente a través del panel de control flexible o de un menú estático. Se puede cambiar esto luego, en los ajustes del panel.Usar panel de controlUsar menú
- El botón del menú, muestra el panel de control en lugar del menú
+ El botón del menú muestra el panel de control en lugar del menúAcceso desde el mapaIndica el tipo de PDI correcto u omítelo.Sin escaleras
@@ -1880,7 +1877,7 @@
\nSe utiliza {3} MB temporalmente, {1} MB constantemente. (De {2} MB.)Elegir marcador del mapaOtros marcadores
- Subir notas de OSM anónimas o usar el perfil de OpenStreetMap.org.
+ Sube tu nota de OSM de forma anónima o usando tu perfil de OpenStreetMap.org.Subir nota(s) de OSMSubir anónimamenteBarra superior
@@ -1910,7 +1907,7 @@
\nParte de los ingresos vuelven a la comunidad de OSM y se paga por cada contribución OSM.
\nSi amas a OsmAnd, OSM y quieres apoyarlos y ser apoyado por ellos, esta es una perfecta manera de hacerlo.Mostrar barra de transparencia en el mapa
- Se ha cambiado a la memoria interna, porque la carpeta de almacenamiento de datos elegida es de sólo lectura. Elige un directorio de almacenamiento válido.
+ Se ha cambiado a la memoria interna, porque la carpeta de almacenamiento de datos elegida está protegida de escritura. Elige un directorio de almacenamiento válido.Memoria compartidaLa aplicación ya permite escribir en el almacenamiento externo, pero se debe reiniciar la aplicación.Subir ↑
@@ -1918,7 +1915,7 @@
Finalizar navegaciónEvitar caminoInforme completo
- Nombre de usuario y contraseña de OpenStreetMap
+ Nombre de usuario y contraseña de OSMInformeAñade marcadores a través del mapaNo se encontraron puntos de referencia
@@ -2009,16 +2006,16 @@
Necesario para descargar mapas.Buscando la ubicación…Espacio libre
- Almacenamiento de datos de OsmAnd (para mapas, archivos GPX, etc.): %1$s.
+ Almacenamiento de datos de OsmAnd (para mapas, trazas, etc.): %1$s.Conceder permisoPermitir el acceso a la ubicaciónObtenga direcciones y descubra lugares nuevos, sin una conexión a InternetEncontrar mi ubicaciónOmite la búsqueda de nuevas versiones o descuentos relacionados con OsmAnd.Ocultar nuevas versiones
- Suscripción mensual. Puede cancelarlo en cualquier momento en Google Play.
- Donaciones a la comunidad de OpenStreetMap
- Parte de tu donación se envía a usuarios que realicen cambios en OpenStreetMap. El costo de la suscripción sigue siendo la misma.
+ Suscripción mensual. Puedes cancelarla en cualquier momento en Google Play.
+ Donación a la comunidad de OSM
+ Parte de tu donación se envía a los contribuidores a OSM. El coste de la suscripción sigue siendo el mismo.La suscripción permite actualizaciones cada hora, día o semana y descargas ilimitadas para los mapas de todo el mundo.ObtenerObtener por %1$s
@@ -2032,8 +2029,8 @@
Subir PDICálculo de la rutaCiudad o región
- Sin archivos GPX aún
- También puedes añadir archivos GPX a la carpeta
+ No tienes archivos de trazas aún
+ También puedes añadir archivos de trazas a la carpetaAñadir más…AspectoNotificaciones
@@ -2047,7 +2044,7 @@
Usar autopistasPermite usar autopistas.Activar la grabación rápida
- Muestra una notificación del sistema que permite la grabación del viaje.
+ Muestra una notificación del sistema que permite empezar la grabación del viaje.ViajeGrabadoGrabar
@@ -2121,7 +2118,7 @@
Un botón que añade una nota fotográfica en el centro de la pantalla.Un botón que añade una nota de OSM en el centro de la pantalla.Un botón que añade un PDI en el centro de la pantalla.
- Un botón que des/activa las indicaciones por voz durante la navegación.
+ Un botón que activa o desactiva las indicaciones por voz durante la navegación.Un botón que añade la ubicación del aparcamiento en el centro de la pantalla.Mostrar un diálogo temporal" guardado como "
@@ -2188,7 +2185,7 @@
\nProporciona un código completoOLC completo y válido.
\nÁrea representada: %1$s x %2$s
- Un botón que muestra la siguiente lista.
+ Un botón para paginar a través de la lista de abajo.Mapa superpuesto cambiado a «%s».Mapa subyacente cambiado a «%s».Pendiente
@@ -2286,15 +2283,15 @@
\n ¡Más países alrededor del globo están disponibles para descargar!
\n Obtén un navegador confiable en tu país - ya sea Francia, Alemania, México, Reino Unido, España, Países bajos, Estados Unidos, Rusia, Brasil o cualquier otro.Alternar zoom automático del mapa
- Un botón que des/activa el zoom automático del mapa de acuerdo a la velocidad.
- Activar zoom automático del mapa
- Desactivar zoom automático del mapa
+ Botón para activar o desactivar el zoom automático controlado por la velocidad.
+ Activar zoom automático
+ Desactivar zoom automáticoDefinir destinoReemplazar destinoAñadir primer destino intermedio
- Un botón que añade el destino de la ruta en el centro de la pantalla, cualquier destino previamente elegido se convierte en el último destino intermedio.
- Este botón de acción, añade un nuevo destino de ruta en el centro de la pantalla, reemplazando el anterior destino (si existe).
- Un botón que añade el primer destino intermedio en el centro de la pantalla.
+ Un botón que añade el centro de la pantalla como destino de la ruta, cualquier destino previamente elegido se convierte en el último destino intermedio.
+ Un botón que añade el centro de la pantalla como destino de la nueva ruta, reemplazando el anterior destino (si existe).
+ Un botón que añade el centro de la pantalla como el primer destino intermedio.Sin superposiciónSin subyacenciaError
@@ -2327,13 +2324,14 @@
Ubicación propia animadaActiva el desplazamiento animado del mapa para «Mi ubicación» durante la navegación.Resumen
- Navegación GPS
+ Navegación
\n • Funciona en línea (rápido) o sin conexión (sin cargos de roaming al viajar al extranjero)
\n • Guía por voz giro-a-giro (voces grabadas y sintetizadas)
-\n • (Opcional) Guía de carriles, nombres de calles y tiempo estimado al destino
-\n • Soporta puntos intermedios en el itinerario
+\n • (Opcional) Guía de carriles, nombres de calles y tiempo estimado de llegada
+\n • Soporta puntos intermedios en tu itinerario
\n • La ruta se recalcula al salirse de la misma
-\n • Busca destinos por dirección, por tipo (por ejemplo: Restaurantes, hoteles, gasolineras, museos), o por coordenada geográfica
+\n • Busca lugares por dirección, por tipo (por ejemplo: Restaurantes, hoteles, gasolineras, museos), o por coordenadas geográficas
+\nVista del mapa
\n • Muestra tu ubicación y orientación
\n • (Opcional) Ajusta el mapa a la dirección del movimiento (o la brújula)
@@ -2397,7 +2395,7 @@
Mostrar u ocultar notas de OSMMostrar notas de OSMOcultar notas de OSM
- Un botón que muestra u oculta las notas de OSM en el mapa.
+ Botón para mostrar u ocultar las notas de OSM en el mapa.Ordenados por distanciaBuscar en FavoritosOcultar desde el nivel de zoom
@@ -2419,7 +2417,7 @@
Abrir MapillaryInstalarMejorar cobertura de fotos con Mapillary
- Instala Mapillary para añadir una o más fotos a esta ubicación del mapa.
+ Instala Mapillary para añadir fotos a esta ubicación del mapa.Fotos en líneaImagen de MapillaryPermisos
@@ -2449,10 +2447,10 @@
Min/MáxRosa translúcidoPausar/reanudar navegación
- Este botón de acción, pausa o reanuda la navegación.
+ Botón para pausar o reanudar navegación.Mostrar diálogo «Navegación finalizada»Iniciar/parar navegación
- Este botón de acción, inicia o para la navegación.
+ Botón para iniciar o terminar la navegación.Tiempo del búfer para el seguimiento en líneaIndica el tiempo que el búfer mantendrá los lugares para enviar sin conexiónAñadir al menos un punto.
@@ -2468,7 +2466,7 @@
Punto de ruta 1Punto de referencia 1Sin animaciones
- Desactiva las animaciones en la aplicación.
+ Desactiva las animaciones de los mapas.Mantener en el mapa¿Salir sin guardar?Línea
@@ -2492,9 +2490,9 @@
Mover todo al historialIndicación de distanciaOrdenar por
- Elige cómo se indica la distancia y dirección a los marcadores del mapa en la pantalla del mapa:
+ Elige cómo se indica la distancia y dirección a los marcadores del mapa en el mapa:Umbral de orientación del mapa
- Velocidad a partir de la cual la orientación del mapa cambia de «Dirección del movimiento» a «Dirección de la brújula».
+ Selecciona la velocidad a partir de la que la orientación del mapa cambia de «Dirección del movimiento» a «Dirección de la brújula».Todos los marcadores del mapa movidos al historialMarcador del mapa movido al historialMarcador del mapa movido a los activos
@@ -2603,8 +2601,8 @@
Pulsa un marcador en el mapa para moverlo al primer lugar de los marcadores activos, sin abrir el menú contextual.Activar «Una pulsación»¡Toma notas!
- Añade una nota de audio, vídeo o foto para cada punto del mapa, utilizando el widget o el menú contextual.
- Notas por fecha
+ Añade una nota de audio, vídeo o foto a cualquier punto del mapa, utilizando el control o el menú contextual.
+ Notas A/V por fechaPor fechaPor tipoAquí hay:
@@ -2852,7 +2850,7 @@
GuaraníEstá utilizando el mapa «{0}» que funciona con OsmAnd. ¿Quiere ejecutar la versión completa de OsmAnd\?¿Ejecutar OsmAnd\?
- Un botón que alterna entre el modo diurno y nocturno para OsmAnd.
+ Un botón que alterna entre los modos diurno y nocturno para OsmAnd.Modo diurnoModo nocturnoAlternar modos diurno/nocturno
@@ -2956,7 +2954,7 @@
AutopistaCarretera/ruta estatalCarretera principal
- Calle residencial
+ CalleVía de servicioAceraCamino rural
@@ -3103,7 +3101,7 @@
Elegir el tipo de navegaciónAutomóvil, camión, motocicletaBicicleta de montaña, ciclomotor, caballo
- Caminata, senderismo, correr
+ Caminata, senderismo, carreraTipos de transporte públicoBarco, remo, velaAvión, ala delta
@@ -3139,7 +3137,7 @@
Dificultad preferidaPreferir rutas de esta dificultad, aunque el trazado sobre pistas más duras o más fáciles sigue siendo posible si son más cortas.Fuera de pista
- Los senderos libres y fuera de pista son rutas y pasajes no oficiales. Típicamente descuidados, no mantenidos por los oficiales y no controlados por la noche. Entrar bajo su propio riesgo.
+ Los senderos libres y fuera de pista son rutas y pasajes no oficiales. Típicamente descuidados, no mantenidos y no controlados por la noche. Entra bajo tu propio riesgo.GeocodificaciónErrorTodo terreno
@@ -3361,7 +3359,7 @@
El nombre de archivo está vacíoTraza guardadaRevertir
- Un botón para centrar en la pantalla el punto de partida y calcular la ruta hacia el destino o abre un cuadro de diálogo para elegir el destino si el marcador no está en el mapa.
+ Un botón que añade el centro de la pantalla como punto de partida. Pedirá luego que se fije el destino o iniciará el cálculo de la ruta.Mostrar nodo de la red de rutas ciclistas¿Borrar %1$s\?Diálogo de descarga del mapa
@@ -3394,7 +3392,7 @@
Elegir el colorLos perfiles predefinidos de OsmAnd no pueden borrarse , pero sí desactivarse (en la pantalla anterior), o moverse a la parte inferior.Editar perfiles
- El \'tipo de navegación\' domina como se calculan las ruta.
+ El \'Tipo de navegación\' determina cómo se calculan las rutas.Apariencia del perfilIcono, color y nombreEditar lista de perfiles
@@ -3679,7 +3677,7 @@
Buscar tipos de PDIAcción %1$s no admitidaMapa general del mundo (detallado)
- Tipo no admitido
+ Tipo no soportadoProporciona la anchura de tu vehículo, algunas restricciones de ruta pueden aplicarse para vehículos anchos.Proporciona la altura de tu vehículo, algunas restricciones de ruta pueden aplicarse para vehículos altos.Proporciona el peso de tu vehículo, algunas restricciones de ruta pueden aplicarse para vehículos pesados.
@@ -3715,7 +3713,7 @@
Elige cómo se guardarán las teselas descargadas.Puede exportar o importar acciones rápidas con perfiles de aplicación.¿Eliminar todo\?
- ¿Estás seguro de que deseas eliminar irrevocablemente% d acciones rápidas\?
+ ¿Estás seguro deseas eliminar de forma irreversible %d acciones rápidas\?Tiempo de apagado de la pantallaSi \"%1$s\" está encendido, el tiempo de actividad dependerá de ello.metros
@@ -3740,7 +3738,7 @@
UrduTayikoBávaro
- Rastreador OsmAnd
+ Trazador OsmAndLa guía para la simbología del mapa.Posiciones de estacionamientoDeshabilitado. Requiere \'Mantener la pantalla encendida\' dentro de \'Tiempo de espera después de la activación\'.
@@ -3849,7 +3847,7 @@
Distancia umbralPerfil de navegaciónSelecciona un archivo de traza al que agregar el nuevo segmento.
- Imágenes a pie de calle
+ Fotos a pie de calle¿Estás seguro de que quieres descartar todos los cambios en la ruta planeada cerrándola\?En caso de dirección contrariaGuardar como nuevo archivo de traza
@@ -3897,4 +3895,4 @@
Último modificadoNombre: Z – ANombre: A - Z
-
+
\ No newline at end of file
diff --git a/OsmAnd/res/values-et/phrases.xml b/OsmAnd/res/values-et/phrases.xml
index 6d72d50576..4cb42eee67 100644
--- a/OsmAnd/res/values-et/phrases.xml
+++ b/OsmAnd/res/values-et/phrases.xml
@@ -524,7 +524,7 @@
Oktaan 95Oktaan 98Oktaan 100
- CNG
+ Surugaas1:25 kütus1:50 kütusEtanool
@@ -3826,4 +3826,5 @@
Väikesed elektriseadmedMesitaruPähklipood
+ Veeldatud maagaas
\ No newline at end of file
diff --git a/OsmAnd/res/values-et/strings.xml b/OsmAnd/res/values-et/strings.xml
index e219936c08..8e58fdad52 100644
--- a/OsmAnd/res/values-et/strings.xml
+++ b/OsmAnd/res/values-et/strings.xml
@@ -40,7 +40,7 @@
StartStopImport
- Eksport
+ EkspordiVeel…Veel tegevusiÄra enam näita
@@ -910,7 +910,7 @@
Seadista teekonna parameetridTeekonna parameetridRakenduse profiiliks muudetud \"%s\"
- Logcat puhver
+ Logcati puhverLaienduse seadedVaikimisiSelle ala vaatlemiseks lae alla üksikasjalik %s kaart.
@@ -3364,7 +3364,7 @@
OSMIkooni kuvatakse vaid navigeerimise või liikumise ajal.Peatumisel näidatav ikoon.
- Kontrolli ja jaga rakenduse detailseid logisid
+ Vaata ja jaga rakenduse detailseid logisidGeokavatsuse väärtusest \'%s\' ei saanud aru.Selle valiku kasutamine vajab luba.See on madala kiirusega väljalülitusfilter, et mitte salvestada punkte, mis jäävad alla teatud kiiruse. See võib muuta salvestatud rajad kaardil vaadates sujuvamaks.
@@ -3497,7 +3497,7 @@
Kohandatud värvJätkamiseks vali tööpäevadTeekond punktide vahel
- Kavanda teekonda
+ Kavanda teekondLisa rajaleNäita alguse ja lõpu ikooneVali laius
@@ -3762,4 +3762,6 @@
Viimati muudetudNimi: Z – ANimi: A – Z
+ Ekraani väljalülitamine
+ Ratastool edasi
\ No newline at end of file
diff --git a/OsmAnd/res/values-fa/phrases.xml b/OsmAnd/res/values-fa/phrases.xml
index eba643a4f9..dd6c374db3 100644
--- a/OsmAnd/res/values-fa/phrases.xml
+++ b/OsmAnd/res/values-fa/phrases.xml
@@ -9,7 +9,7 @@
فروشگاه گوشتبقالیفروشگاه محصولات دامی
- سبزی فروشی
+ میوه و سبزیفروشیفروشگاه غذاهای دریاییشیرینی و آجیل فروشیبستنی فروشی
diff --git a/OsmAnd/res/values-fa/strings.xml b/OsmAnd/res/values-fa/strings.xml
index 4d8d47c875..68b4a4511d 100644
--- a/OsmAnd/res/values-fa/strings.xml
+++ b/OsmAnd/res/values-fa/strings.xml
@@ -3278,7 +3278,7 @@
تور اسکیمسیرها برای تور اسکی.کمپر
- ون کمپر
+ ون کمپر (RV)واگنکامیون پیکآپتحلیلها
@@ -3553,7 +3553,7 @@
پروفایل سفارشیزاویه: %s°زاویه
- تا مسیریابی مجدد انجام شود، از موقعیت من تا مسیر محاسبهشده پارهخط مستقیمی نمایش داده میشود
+ تا مسیریابی مجدد انجام نشده، از موقعیت من تا مسیر محاسبهشده پارهخط مستقیمی نمایش داده میشودکمترین زاویه میان موقعیت من و مسیرآمادهسازیچیزی انتخاب نشده
@@ -3873,7 +3873,7 @@
ذخیره بهعنوان رد جدیدبرعکسکردن مسیرتمام رد با استفاده از پروفایل انتخابی بازمحاسبه خواهد شد.
- با استفاده از پروفایل انتخابی فقط پارهٔ بعدی بازمحاسبه خواهد شد.
+ فقط پارهٔ بعدی با استفاده از پروفایل انتخابی بازمحاسبه خواهد شد.همهٔ پارههای بعدیپارهٔ قبلیهمهٔ پارههای قبلی
@@ -3928,4 +3928,9 @@
بازهٔ زمانی برای ضبط رد را انتخاب کنید (که از طریق ابزار ضبط سفر روی نقشه فعال میشود).نگهداشتن ضبط سفرازسرگیری ضبط سفر
+ اسکیت اینلاین
+ موتور پرشی
+ اسکوتر موتوری
+ ویلچر رو به جلو
+ فاصله آستانه
\ No newline at end of file
diff --git a/OsmAnd/res/values-fr/phrases.xml b/OsmAnd/res/values-fr/phrases.xml
index 638995c1f2..91dc180070 100644
--- a/OsmAnd/res/values-fr/phrases.xml
+++ b/OsmAnd/res/values-fr/phrases.xml
@@ -1762,7 +1762,7 @@
Fournitures de plomberieFournitures de boisAncres pour vélo
- Râtelier pour vélo
+ Arceaux pour véloTerminal d\'informationsCarte tactileTableau d\'affichage
@@ -3380,7 +3380,7 @@
Parc animalierEnceinteParc safari
- Râtelier pour vélo
+ Arceaux pour véloVélo de sportHachoirHors route
diff --git a/OsmAnd/res/values-fr/strings.xml b/OsmAnd/res/values-fr/strings.xml
index 91daebce7f..41007cc7eb 100644
--- a/OsmAnd/res/values-fr/strings.xml
+++ b/OsmAnd/res/values-fr/strings.xml
@@ -3412,7 +3412,7 @@
OSMIcône affiché pendant la navigation ou en déplacement.Icône affiché à l\'arrêt.
- Consultez et partagez les journaux de l\'application (pour debug)
+ Vérifier et partager les logs détaillés de l\'applicationVous n\'êtes pas autorisés à utiliser cette option.Intervalle de suiviAdresse web
@@ -3883,4 +3883,6 @@
Dernière modificationNom : Z – ANom : A – Z
+ Icônes de départ / arrivée
+ Merci pour votre achat de \'Courbes de niveaux\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-hu/phrases.xml b/OsmAnd/res/values-hu/phrases.xml
index 4e0cce87d3..0234cd76a2 100644
--- a/OsmAnd/res/values-hu/phrases.xml
+++ b/OsmAnd/res/values-hu/phrases.xml
@@ -294,7 +294,7 @@
Forgalomlassító útszűkületGázolajBiodízel
- Cseppfolyós gáz (LPG)
+ LPG (cseppfolyósított PB-gáz)80-as oktánszámú91-es oktánszámú92-es oktánszámú
@@ -3835,4 +3835,5 @@
Ivóvíz-utántöltésMag- és aszaltgyümölcsboltMéhkaptár
+ LNG (cseppfolyósított földgáz)
\ No newline at end of file
diff --git a/OsmAnd/res/values-hu/strings.xml b/OsmAnd/res/values-hu/strings.xml
index 97f76c0623..b903ccb8da 100644
--- a/OsmAnd/res/values-hu/strings.xml
+++ b/OsmAnd/res/values-hu/strings.xml
@@ -3277,7 +3277,7 @@
TérképnézetAlapértelmezés visszaállításaMásolás egy másik profilból
- Logcat-puffer
+ Logcat-puffer (hibanapló)Alapértelmezés szerintHóparkLovas szán
@@ -3424,8 +3424,8 @@
OSM-szerkesztésAz összes még fel nem töltött szerkesztés vagy OSM-hiba megtalálható a %1$s helyen. A már feltöltött pontok nem láthatók az OsmAndban.OSM
- Az ikon navigáció vagy haladás közben jelenik meg.
- Az ikon álló helyzetben jelenik meg.
+ Navigáció vagy haladás közben megjelenő ikon.
+ Álló helyzetben megjelenő ikon.Az alkalmazás részletes naplóinak ellenőrzése és megosztásaA beállítás használatához engedélyre van szükség.Kategóriák átrendezése
@@ -3896,4 +3896,6 @@
Utolsó módosításNév: Z–ANév: A–Z
+ Kiindulás/érkezés ikonjai
+ Köszönjük, hogy megvásárolta a szintvonalbővítményt (Contour lines)
\ No newline at end of file
diff --git a/OsmAnd/res/values-it/strings.xml b/OsmAnd/res/values-it/strings.xml
index e806eff554..919b6123a8 100644
--- a/OsmAnd/res/values-it/strings.xml
+++ b/OsmAnd/res/values-it/strings.xml
@@ -1954,7 +1954,7 @@
Navigazione OsmAnd LiveLivello della batteriaUngherese (formale)
- Tracciato attuale
+ Traccia attualeCambia posizione del marcatoreSpagnolo americanoAsturiano
@@ -3620,7 +3620,7 @@
Mappa mondiale generale (dettagliata)Mappe extraAzione non supportata %1$s
- OsmAnd tracker
+ Tracker OsmAndOsmAnd + MapillaryAzione veloceRighello radiale
@@ -3812,7 +3812,7 @@
Aggiungi ad una TracciaSeleziona l\'intervallo a cui i segnaposti con distanza o orario sulla traccia verranno mostrati.Seleziona l\'opzione desiderata per la divisione: per tempo o per distanza.
- Frecce delle direzioni
+ Frecce della direzioneSeleziona larghezzaUltima modificataImporta una traccia
@@ -3900,7 +3900,9 @@
\n
\nTraccia semplificata
- Ultimo modificato
+ CronologicoNome: Z – ANome: A – Z
+ Icona Partenza/Arrivo
+ Grazie per l\'acquisto del \'Plugin delle curve di livello\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-iw/phrases.xml b/OsmAnd/res/values-iw/phrases.xml
index c625ca9fdd..d593ffb9d0 100644
--- a/OsmAnd/res/values-iw/phrases.xml
+++ b/OsmAnd/res/values-iw/phrases.xml
@@ -1390,7 +1390,7 @@
שיתוף רכבשיתוף סירותמעגן
- בית שימוש
+ בית שימוש;אסלה;טואלטמקלחתסאונהבית בושת
@@ -2043,7 +2043,7 @@
אגרה למשאיותכןכן
- לא
+ שמע: לארק כאשר מותר לחצותתחנת הצלהאזור שירות
@@ -2119,7 +2119,7 @@
דלק 100LLגפ״מדלק מטוסים A-1
- תוסף AdBlue
+ נוזל מפלט לדיזלכספומטמקום בנסיעה שיתופיתוסליאן
@@ -2159,4 +2159,19 @@
כןמצוק טיפוסקטגוריית קושי
+ צינור
+ רשת מילוי מי שתייה
+ מילוי מי שתייה: אין
+ כן
+ תיבת תרומה
+ חץ: אין
+ כן
+ כן
+ מעלית
+ זמן אמת
+ השהיה
+ כן
+ לוח זמנים
+ כוורת דבורים
+ חנות אגוזים
\ No newline at end of file
diff --git a/OsmAnd/res/values-iw/strings.xml b/OsmAnd/res/values-iw/strings.xml
index 7f556c9926..42a840fa87 100644
--- a/OsmAnd/res/values-iw/strings.xml
+++ b/OsmAnd/res/values-iw/strings.xml
@@ -2103,7 +2103,7 @@
ביטול הבחירהלמחוק הכולשיתוף
- יצוא
+ ייצואעוד…שמירת הבחירהשגיאה בלתי צפויה
@@ -3248,7 +3248,7 @@
התצורה הנבחרת תחול בכל רחבי היישומון.הגדרה זו נבחרת כבררת מחדל על הפרופילים: %sהחלפת הגדרה
- זיכרון זמני של Logcat
+ מכלא Logcatכבררת מחדלהגדרות תוסףהורדת המפה המפורטת %s, כדי לצפות באזור זה.
@@ -3428,7 +3428,7 @@
סמל שמופיע רק בעת ניווט או תזוזה.סמל שמופיע במנוחה.דירוג
- לבדוק ולשתף תיעוד מפורט של יומני היישומון
+ בדיקה ושיתוף יומני תיעוד מפורטים של היישומיםנדרשת הרשאה כדי להשתמש באפשרות הזו.סידור הקטגוריות מחדשניתן להחליף את סדר הרשימה ולהסתיר קטגוריות בלתי נחוצות. אפשר לייבא או לייצא את כל השינויים עם פרופילים.
@@ -3909,4 +3909,6 @@
שינוי אחרוןשם: ת – אשם: א – ת
+ סמלי התחלה/סיום
+ תודה לך על רכישת ‚קווי מתאר’
\ No newline at end of file
diff --git a/OsmAnd/res/values-ja/phrases.xml b/OsmAnd/res/values-ja/phrases.xml
index 680da76205..66c5cc2a31 100644
--- a/OsmAnd/res/values-ja/phrases.xml
+++ b/OsmAnd/res/values-ja/phrases.xml
@@ -1258,7 +1258,7 @@
有り掃除機:無し掃除機
- アドブルー・尿素水還元剤
+ ディーゼル排気用液(AdBlue・尿素水)ドライブスルー有り無し
@@ -3822,4 +3822,16 @@
吸引加圧地下水
+ ナッツ専門店
+ 養蜂箱
+ リアルタイム時刻表
+ 一般的な時刻表
+ 大まかな時刻表
+ 有り
+ 時刻表:無し
+ エレベーター
+ 街区
+ 行政区
+ ギブボックス(提供品置場)
+ 簡易給水栓
\ No newline at end of file
diff --git a/OsmAnd/res/values-ja/strings.xml b/OsmAnd/res/values-ja/strings.xml
index 80f0b62286..35f9146fc7 100644
--- a/OsmAnd/res/values-ja/strings.xml
+++ b/OsmAnd/res/values-ja/strings.xml
@@ -1190,7 +1190,7 @@ POIの更新は利用できません"出発時刻: %1$tF, %1$tT ""到着時刻: %1$tF, %1$tT ""平均速度: %1$s "
- "最高速度: %1$s "
+ 最高速度: %1$s平均標高: %1$s標高差: %1$s上り/下り: %1$s
@@ -1350,7 +1350,7 @@ POIの更新は利用できません画面の電源オン設定方向転換地点に近づいたらデバイスの画面を(オフの場合指定時間)オンにしますしない
- 除外する道の指定…
+ 避ける道の指定…電車でのルート路面電車でのルートタクシーのルート共有
@@ -1625,7 +1625,7 @@ POIの更新は利用できません所属ネットワークに応じたカラー変更OSMCのハイキングシンボルカラー被災域
- 太線
+ 輪郭強調更新はありませんライブ更新ユーザーからの意見やフィードバックを大切にしています。
@@ -1816,7 +1816,7 @@ POIの更新は利用できません上に移動下に移動ナビゲーションの終了
- 使用しない道路として指定
+ 避ける道を指定選択したデータ保存フォルダーが書き込み保護されているため、内部メモリに切り替えました。書き込み可能な保存用ディレクトリを選択してください。共有記憶域より詳細なレポートは以下サイトにて
@@ -2496,7 +2496,7 @@ POIの更新は利用できません今年全て履歴に移動距離表示
- 並び順の変更
+ 並び順:マップ上に表示し続ける保存せずに終了しますか?線
@@ -3010,9 +3010,9 @@ POIの更新は利用できませんアイコン選択基本プロファイルアイコン
- 最低速度
- 最高速度
- 標準移動速度
+ 予想最低速度
+ 予想最高速度
+ 予想標準速度すべての道路の移動速度を制限し、種別や制限速度が不明な道路が多い場合の到着時間予測に役立ちます(ルート計算に影響します)オフロードプロファイルの個別設定
@@ -3590,7 +3590,7 @@ POIの更新は利用できませんPOIの作成/編集駐車位置お気に入りの追加/編集
- アイテム順序をデフォルトに戻す
+ アイテム順序を初期状態に戻します編集に戻る選択したプロファイルを切り替えるボタンです。プロファイルの追加
@@ -3667,12 +3667,12 @@ POIの更新は利用できません
コンテキストメニュー項目の並べ替えや非表示するものを指定できます。分割
- 分割線で区切られた部分より下にある項目が適用されます。
+ ここで指定された項目は、区切り線より下に配置されます。非表示これらの項目はメニューに表示されなくなりますが、オプションやプラグインはそのまま機能します。項目設定を非表示にすると、元の状態にリセットされます。
- ボタンは4つしかありません。
+ ボタンの数は4つ固定で変更できません。主要機能“%1$s”ボタンをタップすると、これらの機能にアクセスできます。アイテムはこのカテゴリ内でのみ移動できます。
@@ -3794,7 +3794,7 @@ POIの更新は利用できません
\n
\n国の法律に基づいて、使用を望むかどうかを決定する必要があります。
\n
-\n%1$sを選択すると、スピードカメラに関するアラートと警告が表示されます。
+\n%1$sを選択すると、スピードカメラに関するアラートと警告機能を使用できます。
\n
\n%2$sを選択すると、スピードカメラに関するすべてのデータ(警告、通知、POI)が、OsmAndの再インストールを行うまで削除されます。
機能を維持
diff --git a/OsmAnd/res/values-nb/strings.xml b/OsmAnd/res/values-nb/strings.xml
index 32c34dd961..9c24c2aa7c 100644
--- a/OsmAnd/res/values-nb/strings.xml
+++ b/OsmAnd/res/values-nb/strings.xml
@@ -751,7 +751,7 @@
Sikker modusProgrammet kjører i sikker modus (skru det av i \'Innstillinger\').Talemeldinger stopper midlertidig musikkavspilling.
- Avbryt musikk
+ Sett musikk på pauseOffentligOptimaliser kart forVis fra zoom-nivå (krever kotedata):
@@ -1650,7 +1650,7 @@
ValgtvalgteFJERN MERKELAPPEN
- Last ned nattlige utviklerversjoner.
+ Last ned aktuelle utviklingsversjoner.ByggversjonerSpesifiser en mellomtjener.Innlogget som %1$s
@@ -2142,7 +2142,7 @@
Vis/skjul OSM-notaterVis OSM-notaterSkjul OSM-notater
- Å trykke denne handlingsknappen viser eller skjuler OSM-notater på kartet.
+ Knapp til å vise eller skjule OSM-notater på kartet.Takk for at du kjøpte \'Havdybdekonturer\'HavdybdekonturerHavdybdekonturer
@@ -2257,17 +2257,17 @@
Rediger handlingSlett handlingNavneforvalg
- Trykk på denne handlingsknappen legger til en kartmarkør i skjermsenteret.
- Trykking på denne handlingsknappen legger til et GPX-rutepunkt i midten av skjermen.
- Trykking på denne handlingsknappen legger til et lydnotat i midten av skjermen.
- Trykking på denne handlingsknappen legger til et videonotat i midten av skjermen.
- Trykking på denne handlingsknappen legger til et bildenotat i midten av skjermen.
- Trykking på denne handlingsknappen legger til et OSM-notat i midten av skjermen.
- Trykking på denne handlingsknappen slår av eller på taleveiledning under navigering.
- Trykking på denne handlingsknappen legger til en parkeringsplass i midten av skjermen.
+ En knapp for å legge til en kartmarkør i skjermsenteret.
+ En knapp for å legge til et GPX-rutepunkt i midten av skjermen.
+ En knapp for å legge til et lydnotat i midten av skjermen.
+ En knapp for å legge til et videonotat i midten av skjermen.
+ En knapp for å legge til et bildenotat i midten av skjermen.
+ En knapp for å legge til et OSM-notat i midten av skjermen.
+ En knapp til å slå av eller på taleveiledning under navigering.
+ En knapp for å legge til en parkeringsplass i midten av skjermen.Vis en midlertidig dialog" lagret i "
- Trykking på denne handlingsknappen viser eller skjuler favorittpunktene på kartet.
+ En knapp til å vise eller skjule favorittpunktene på kartet.Opprett elementerLa stå tomt for å bruke adressen eller stedsnavnet.Denne meldingen inkluderes i kommentarfeltet.
@@ -2341,10 +2341,10 @@
Et trykk på kartet skjuler/viser kontrollknappene og miniprogrammene.Marker som passertKunne ikke endre notatet.
- Trykking på denne handlingsknappen slår av/på automatisk kartforstørrelse i henhold til hastigheten din.
- Trykking på denne handlingsknappen gjør skjermsenteret til rutemål, ethvert tidligere valgt reisemål blir det siste mellomliggende målet.
- Trykking på denne handlingsknappen gjør skjermsenteret det nye rutemålet, og erstatter det tidligere valgte reisemålet (hvis noe).
- Trykking på denne handlingsknappen gjør skjermsenteret til det første mellomliggende reisemålet.
+ Knapp til å slå av eller på hastighetsbasert auto-zoom.
+ En knapp for å gjøre skjermsenteret til rutemålet, et tidligere valgt reisemål blir det siste mellomliggende målet.
+ En knapp for å gjøre skjermsenteret til det nye rutemålet, erstatter det tidligere valgte reisemålet (hvis noe).
+ En knapp for å gjøre skjermsenteret til det første mellomliggende reisemålet.Abonner på vår e-postliste om programrabatter og få tre kartnedlastinger til!Havdybdepunkter for sørlige halvkuleHavdybdepunkter for nordlige halvkule
@@ -2382,7 +2382,7 @@
Aktiver \"sjøkartvisning\" -tilleggetNavnet inneholder for mange store bokstaver. Fortsett\?Legg til interessepunkt
- Trykking på denne handlingsknappen viser eller skjuler interessepunkter på kartet.
+ En knapp til å vise eller skjule interessepunkter på kartet.En knapp til å bla gjennom listen nedenfor.Fyll ut alle parametereVed å trykke lenge og dra knappen endres dens plassering på skjermen.
@@ -2408,7 +2408,7 @@
Alle punkter i gruppenGPX-fil med de valgte notatenes koordinater og data.GPX-fil med alle notaters koordinater og data.
- Trykking på denne handlingsknappen legger til et interessepunkt i midten av skjermen.
+ En knapp for å legge til et interessepunkt i midten av skjermen.Åpen fraÅpen tilStenger
@@ -2859,10 +2859,10 @@
Du bruker {0} kart levert av OsmAnd. Vil du starte fullversjonen av OsmAnd\?Kjør OsmAnd\?Guaraní
- Vekselvender mellom dag- og nattmodus i OsmAnd
+ En knapp til å skifte mellom dag- og nattmodus i OsmAnd.DagmodusNattmodus
- Veksle dag-/nattmodus
+ Bytt dag/natt-modusOffentlig transportSett reisemålLegg til mellomliggende
@@ -3256,7 +3256,7 @@
OsmAnd-profil: %1$sProfil-importHvit
- Brukes til å estimere ankomsttid for ukjente veityper, og for å begrense farten på alle veier (kan endre rute)
+ Estimerer ankomsttid for ukjente veityper og begrenser farten for alle veier (kan påvirke ruting)Spor-lagringsmappeSpor kan lagres i \'rec\'-mappen, månedlige eller daglige mapper.Ta opp spor til \'rec\'-mappen
@@ -3286,13 +3286,13 @@
\n
\n
Du kan bruke denne endringen på alle eller bare på den valgte profilen.
- En bryter for å vise eller skjule koter på kartet.
+ Knapp som viser eller skjuler koter på kartet.Eksporter profil\"%1$s\" finnes allerede. Overskriv\?Kunne ikke eksportere profil.Legg til en profil ved å åpne profilfilen med OsmAnd.
- %1$s feil under import: %2$s
- %1$s ble importert.
+ %1$s importfeil: %2$s
+ %1$s importert.Bytt %1$s og %2$sStartpunktIsvei
@@ -3345,20 +3345,20 @@
Åpen plasseringskode (OLC)Valgt format vil benyttes i programmet.Denne innstillingen er valgt som forvalg for profiler: %s
- Bruk kun for «%1$s»
- Bruk for alle profiler
+ Bruk kun for \"%1$s\"
+ Bruk på alle profilerAnalyseinstrumenterVis kart på låseskjermen under navigering.
- Innstillinger for ruting i valgt profil «%1$s».
+ Innstillinger for ruting i den valgte profilen \"%1$s\".Tidsavbrudd etter oppvåkningVarsler vises nede til venstre under navigering.Kart under navigasjonKart under navigasjonStemmekunngjøringer finner kun sted under navigasjon.Navigasjonsinstruks og kunngjøringer
- Stemmekunngjøringer
+ TalemeldingerSett opp ruteparameter
- Ruteparameter
+ RuteparametereLast ned detaljert %s-kart for å vise dette området.SledeAkebrett
@@ -3398,13 +3398,13 @@
PersonligLaster ned %sTykk
- For ørkener og andre tynt befolkede områder. Høyere detaljnivå.
+ For ørkener og andre tynt befolkede områder. Mer detaljert.Posisjonsikon under bevegelsePosisjonsikon i hviletilstandTrykk på \'Bruk\' sletter fjernede profiler permanent.HovedprofilVelg farge
- Du kan ikke slette forvalgsprofilene, men du kan skru dem av før dette steget, eller flytte dem til bunnen.
+ OsmAnd-Standardprofiler kan ikke slettes, men deaktiveres (på forrige skjermbilde), eller bli sortert til bunnen.Rediger profilerNavigasjonstype har innvirkning på regler for ruteberegning.Profilutseende
@@ -3418,8 +3418,8 @@
%1$s %2$sImporter profilOSM
- \"%1$s\"-filen inneholder ingen ruteplanleggingsregler, velg en annen fil.
- Ustøttet filtype. Du må velge en fil med %1$s-filendelse.
+ Ingen rutingsregler i \'%1$s\'. Velg en annen fil.
+ Velg en støttet fil med %1$s-endelse isteden.Importer fra filImporter ruteplanleggingsfilNavigasjon, loggingsnøyaktighet
@@ -3429,7 +3429,7 @@
Tillater deg å dele nåværende plassering ved bruk av turopptak.Nettbasert sporingLoggingsnøyaktighet
- Du kan finne alle dine innspilte spor i %1$s, eller i OsmAnd-mappen.
+ Dine innspilte spor er i %1$s, eller i OsmAnd-mappen.Du finner alle dine OSM-notater i %1$s.VideonotaterBildenotater
@@ -3601,7 +3601,7 @@
Sjekk og del detaljert loggføring fra programmetBruk systemets skjermtidsavbruddProgramtillegg av
- Ingen omregning
+ Ingen ny beregningAngi et navn for profilenVelg data å importere.Du kan lese mer om løyper i %1$s.
@@ -3627,7 +3627,7 @@
\nSkru av ubrukte programtillegg for å skjule alle deres styringskontroller. %1$s.
Disse elementene er skjult fra menyen, men de representerte valgene eller programtilleggene vil fortsette å virke.Velg språkene Wikipedia-artikler skal vises på i kartet. Du kan bytte mellom alle tilgjengelige språk mens du leser artikkelen.
- Veiledning til kartets merking.
+ Veiledning til kartets symbolbruk.RuteplanleggingMinsteavstand for å beregne rute på nyttOsmAnd har allerede elementer med samme navn som de i importen.
@@ -3662,7 +3662,7 @@
Fotoboks-interessepunkterAvinstallerDu kan sette fartøyhøyde for å unngå lave broer. Hvis broen endrer høyde, brukes høyden i åpen tilstand.
- Slett neste målpunkt
+ Slett nærmeste målpunktNavngi punktetVis/skjul MapillarySkjul Mapillary
@@ -3675,19 +3675,19 @@
Navigasjonsinstruks
-
+ Avinstaller og start på nyttRullestolGokartPlanlegg en ruteDu får tilgang til disse handlingene ved å trykke på knappen “%1$s”.
- Slå på for å stille inn zoomnivået på kartet med enhetens volumknapper.
+ Styr zoomnivået på kartet med enhetens volumknapper.Det tillagte punktet vil ikke være synlig på kartet, siden den valgte gruppen er skjult, du kan finne det i \"%s\".ScooterRullestol framover
- Du må definere arbeidsdagene for å fortsette
+ Still inn arbeidsdager for å fortsetteLegg til i et spor
- Vis ikoner for start-mål
+ Vis ikoner for start og målVelg breddeVelg intervallet hvor markeringer med avstand eller tid på sporet vil vises.Velg det ønskede oppdelingsalternativet: etter tid eller etter avstand.
@@ -3702,7 +3702,7 @@
Sist redigertImporter sporÅpne eksisterende spor
- Velg en sporfil for åpning.
+ Velg en sporfil for å åpne.Snu ruteOverskriv sporHele sporet blir beregnet på nytt med den valgte profilen.
@@ -3722,7 +3722,7 @@
Importer eller ta opp sporfilerFølg sporVelg sporfil å følge
- Velg sporfil å følge, eller importer en.
+ Velg sporfil å følge eller importer fra enheten din.Velg et annet sporStarten av sporetNærmeste punkt
@@ -3747,4 +3747,15 @@
Sist endretNavn: Å - ANavn: A - Å
+ Start/mål-ikoner
+ Lagre som ny sporfil
+ Legg til i en sporfil
+ Spor
+ Spor
+ Spor
+ Turopptak
+ Lagre som sporfil
+ %s sporfiler valgt
+ Sett turopptak på pause
+ Gjenoppta turopptak
\ No newline at end of file
diff --git a/OsmAnd/res/values-pl/phrases.xml b/OsmAnd/res/values-pl/phrases.xml
index 4b3efcc10c..faec609ff4 100644
--- a/OsmAnd/res/values-pl/phrases.xml
+++ b/OsmAnd/res/values-pl/phrases.xml
@@ -3812,7 +3812,7 @@
SkontrastowaneUzupełnianie wody pitnej: woda z sieciUzupełnianie wody pitnej: nie
- Uzupełnianie wody pitnej: tak
+ TakPoziom wody: utrzymujący się na powierzchniPoziom wody: poniżej średniego poziomu wodyPoziom wody: obmywający falami
@@ -3834,4 +3834,7 @@
Stan pompy: brak wiązkiStrzałka: nieWinda
+ Małogabarytowe urządzenia elektryczne
+ Tablica odjazdów/odlotów
+ Uzupełnianie wody pitnej
\ No newline at end of file
diff --git a/OsmAnd/res/values-pt-rBR/phrases.xml b/OsmAnd/res/values-pt-rBR/phrases.xml
index dd7c249f87..07c80c478d 100644
--- a/OsmAnd/res/values-pt-rBR/phrases.xml
+++ b/OsmAnd/res/values-pt-rBR/phrases.xml
@@ -3844,4 +3844,5 @@
Pequenos aparelhos elétricosColmeiaLoja de nozes
+ GNL
\ No newline at end of file
diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml
index a2c4186817..1358007216 100644
--- a/OsmAnd/res/values-pt-rBR/strings.xml
+++ b/OsmAnd/res/values-pt-rBR/strings.xml
@@ -3427,7 +3427,7 @@
OSMÍcone mostrado ao navegar ou mover.Ícone mostrado em repouso.
- Verifique e compartilhe logs detalhados do aplicativo
+ Verifique e compartilhe registros detalhados do aplicativoNão foi possível analisar a intenção geográfica \'%s\'.É necessária permissão para usar esta opção.Este é um filtro de corte de baixa velocidade para não registrar pontos abaixo de uma determinada velocidade. Isso pode fazer com que as faixas gravadas pareçam mais suaves quando visualizadas no mapa.
@@ -3899,4 +3899,6 @@
Última modificaçãoNome: Z – ANome: A – Z
+ Ícones de início/término
+ Obrigado por adquirir \'curvas de nível\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml
index 8d22b73dbe..fc6f81cb62 100644
--- a/OsmAnd/res/values-pt/phrases.xml
+++ b/OsmAnd/res/values-pt/phrases.xml
@@ -436,7 +436,7 @@
Central telefônicaReciclagemCentro de reciclagem
- Contêiner
+ ContentorVidroPapelRoupas
@@ -1775,7 +1775,7 @@
BrinquedosSorveteCartão SIM
- Seção
+ SecçãoMemorial de guerraPlaca comemorativaEstátua
@@ -2400,7 +2400,7 @@
PassageirosVeículosBicicletas
- Contêineres
+ ContentorVeículos pesadosAcademia ao ar livreHackerspace
@@ -2808,9 +2808,9 @@
Tipo de bomba: gravidadeEstilo de bomba: modernoEstilo de bomba: histórico
- Status da bomba: ok
- Status da bomba: quebrado
- Status da bomba: bloqueado
+ Estado da bomba: ok
+ Estado da bomba: quebrado
+ Estado da bomba: bloqueadoTroikaCartão Troika não aceitoTelescópio
@@ -3563,7 +3563,7 @@
3B3B*Explosão de gás;Queimador de gás
- Objeto excluído
+ Objeto apagadoCaixa de resgateSimReddit
@@ -3829,4 +3829,5 @@
Pequenos aparelhos elétricosColmeiaLoja de nozes
+ GNL
\ No newline at end of file
diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml
index fe6c5d23b1..08c39f206e 100644
--- a/OsmAnd/res/values-pt/strings.xml
+++ b/OsmAnd/res/values-pt/strings.xml
@@ -239,7 +239,7 @@
Meio de transporte:Por favor, define o destino primeiroNavegação
- A aplicação do estado do GPS não está instalada. Pesquisar na loja de aplicações\?
+ A app do estado do GPS não está instalada. Pesquisar na loja de apps\?Horas de aberturaAbrindo conjunto de alterações …Fechando conjunto de alterações…
@@ -294,7 +294,7 @@
Adicionar aos \'Favoritos\'Escolher entre os nomes nativos e inglês.Usar nomes em inglês
- Configurações da aplicação
+ Configurações da appPesquisar endereçoEscolher edifícioEscolher rua
@@ -486,7 +486,7 @@
Usar cores fluorescentes para mostrar trajetos e rotas.Edição offlineUsar sempre a edição offline.
- As alterações de POI dentro da aplicação não afetam os ficheiros de mapas descarregados; essas alterações são guardadas num ficheiro separado no seu aparelho.
+ As alterações de POI dentro da app não afetam os ficheiros de mapas descarregados; essas alterações são guardadas num ficheiro separado no seu aparelho.A enviar…{0} POI/anotações enviadosEnviar todos
@@ -523,7 +523,7 @@
Já existe um ficheiro de favoritos exportados anteriormente. Quer substitui-lo\?Configurações específicas de PerfilConfigurações Globais
- Configurações globais da aplicação
+ Configurações globais da appEspaço livre insuficiente, precisa de %1$s MB (só tem: %2$s disponíveis).Descarregar {0} ficheiro(s)\?
\n {1} MB (de {2} MB) será utilizado.
@@ -554,7 +554,7 @@
O ficheiro POI \'%1$s\' é redundante e pode ser eliminado.Não foi encontrado (e não pôde ser criado) o ficheiro local para guardar as mudanças de POI.Upgrade para OsmAnd+
- Descarregue a nova versão da aplicação para poder usar os novos ficheiros de mapas.
+ Descarregue a nova versão da app para poder usar os novos ficheiros de mapas.Mudar o nomeOnline NomeaçãoProcurando posição…
@@ -624,7 +624,7 @@
Áudio de chamada telefónica (para interromper os aparelhos de som Bluetooth do carro)Áudio de NotificaçãoÁudio de mídia/navegação
- A aplicação não conseguiu descarregar a camada do mapa %1$s, se a tornar a instalar pode resolver o problema.
+ A app não conseguiu descarregar a camada do mapa %1$s, se a tornar a instalar pode resolver o problema.Ajustar a transparência da sobreposição.Transparência da SobreposiçãoAjustar a transparência do mapa base.
@@ -646,7 +646,7 @@
Não foi possível executar a pesquisa offline.Pesquisa por localização geográficaSistema
- Idioma de exibição da aplicação (usado após OsmAnd ser reiniciado).
+ Idioma de exibição da app (usado após OsmAnd ser reiniciado).LinguagemPróximoAnterior
@@ -671,7 +671,7 @@
\nO serviço de navegação está temporariamente mudado para CloudMade on-line.Não foi possível encontrar a pasta especificada.Local de armazenamento
- Todos os dados offline na aplicação instalada antiga serão suportados pela nova aplicação, mas os pontos Favoritos devem ser exportados da aplicação antiga e depois importados na nova aplicação.
+ Todos os dados offline na app instalada antiga serão suportados pela nova, mas os pontos Favoritos devem ser exportados da app antiga e depois importados na nova.Build {0} foi instalado ({1}).Descarregando construção…Instalar OsmAnd - {0} de {1} {2} MB \?
@@ -787,27 +787,27 @@
Widgets transparentesContínuoe-mail
- OsmAnd (direções automatizadas de navegação OSM)
-\n
-\nO OsmAnd é uma aplicação de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz.
-\n
-\nAlgumas das características principais:
-\n- Funcionalidade totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável).
-\n- Mapas vetoriais compactados do mundo inteiro disponíveis.
-\n- Descarregar mapas de países ou regiões diretamente na aplicação.
-\n- Sobreposição de mapas diversos, como GPX ou trajetos de navegação, pontos de interesse (POI), favoritos, curvas de nível, paragens de transportes públicos, mapas adicionais com transparência personalizável.
-\n- Pesquisa desligado da Internet para endereços e locais (POIs).
-\n- Encaminhamento desligado da Internet para distâncias médias.
-\n- Modo de carro, bicicleta e pedestre.
-\n- Vista de dia/noite, com alteração automática (opcional).
-\n- Ampliação do mapa dependente da velocidade.
-\n- Orientação do mapa de acordo com bússola ou direção do movimento.
-\n- Orientação de faixas de rodagem, aviso de limite de velocidade, vozes gravadas e vozes para a conversão de texto para voz.
-\n
-\nLimitações desta versão gratuita do OsmAnd:
-\n- Quantidade de descarregamentos de mapas limitado.
-\n- Sem acesso aos POIs da Wikipédia no modo desligado da Internet.
-\n
+ OsmAnd (direções automatizadas de navegação OSM)
+\n
+\nO OsmAnd é uma app de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz.
+\n
+\nAlgumas das características principais:
+\n- Funcionalidade totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável).
+\n- Mapas vetoriais compactados do mundo inteiro disponíveis.
+\n- Descarregar mapas de países ou regiões diretamente na app.
+\n- Sobreposição de mapas diversos, como GPX ou trajetos de navegação, pontos de interesse (POI), favoritos, curvas de nível, paragens de transportes públicos, mapas adicionais com transparência personalizável.
+\n- Pesquisa desligado da Internet para endereços e locais (POIs).
+\n- Encaminhamento desligado da Internet para distâncias médias.
+\n- Modo de carro, bicicleta e pedestre.
+\n- Vista de dia/noite, com alteração automática (opcional).
+\n- Ampliação do mapa dependente da velocidade.
+\n- Orientação do mapa de acordo com bússola ou direção do movimento.
+\n- Orientação de faixas de rodagem, aviso de limite de velocidade, vozes gravadas e vozes para a conversão de texto para voz.
+\n
+\nLimitações desta versão gratuita do OsmAnd:
+\n- Quantidade de descarregamentos de mapas limitado.
+\n- Sem acesso aos POIs da Wikipédia no modo desligado da Internet.
+\n
\nO OsmAnd está em desenvolvimento ativo, mas o nosso projeto e o seu progresso ainda depende de contribuições financeiras para o desenvolvimento e testes de novas funcionalidades. Por favor, considere comprar o OsmAnd+, ou a ajudar a financiar novas funcionalidades específicas, ou fazer um donativo no osmand.net.Selecione um esquema de cores de estrada:Esquema de cores
@@ -846,9 +846,9 @@
Só EstradasMapa padrãoMapas só de estradas
- Executar a aplicação no modo de segurança (usando o código do Android mais lento em vez do nativo).
+ Executar a app no modo de segurança (usando o código do Android mais lento em vez do nativo).Modo seguro
- A aplicação está a ser executada no modo de segurança (desligue-a em \"Definições\").
+ A app está a ser executada no modo de segurança (desligue-a em \"Definições\").O serviço de segundo plano OsmAnd ainda está em execução. Tambẽm pará-lo\?Fechar conjunto de alteraçõesPesquisar mais povoações/códigos postais
@@ -893,7 +893,7 @@
Sob demanda\?Formato de saída de vídeo:Usar gravador do sistema para vídeo.
- Utilize aplicação do sistema para fotos.
+ Utilize a app do sistema para fotos.Usar aplicação da câmaraA tocar o áudio da gravação. \n%1$sIndisponível
@@ -927,14 +927,14 @@
desmarcadoLimite de VelocidadeNenhum edifício encontrado.
- A aplicação do leitor de código de barras ZXing não está instalada. Procurar no Google Play\?
+ A app do leitor de código de barras ZXing não está instalada. Procurar no Google Play\?Faça uma doação para ver novas funcionalidades implementadas nesta aplicação.incompletoNome da ruaNúmero de casaGravação de viagem
- Personalizar a aparência da aplicação.
- Tema da aplicação
+ Personalizar a aparência da app.
+ Tema da appOpções de acessibilidadeEspecifique um endereçoSelecione favorito
@@ -1047,14 +1047,14 @@
Mapa mundialOsmAnd+ (Direções de Navegação Automatizada do OSM)
\n
-\n OsmAnd+ é uma aplicação de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz.
+\n OsmAnd+ é uma app de navegação livre, com acesso a uma ampla variedade de dados globais do OSM. Todos os dados dos mapas (mapas vetoriais ou imagens raster) podem ser armazenados no cartão de memória do telemóvel para usar desligado da Internet. O OsmAnd também permite roteamento, tanto ligado como desligado da Internet, incluindo a funcionalidade de roteamento curva a curva com orientação por voz.
\n
-\n OsmAnd+ é a versão paga da aplicação, ao comprá-lo está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações.
+\n OsmAnd+ é a versão paga da app, ao comprá-lo está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações.
\n
\n Algumas das características principais:
\n - Funcionalidade totalmente desligado da Internet (guarda os mapas obtidos, sejam eles vetoriais ou imagens, numa pasta selecionável).
\n - Mapas vetoriais compactados do mundo inteiro disponíveis.
-\n - Descarregamento de mapas de países ou regiões diretamente na aplicação.
+\n - Descarregamento de mapas de países ou regiões diretamente na app.
\n - Recurso Wikipédia desligado da Internet (descarregamento de POIs da Wikipédia), ótimo para passeios turísticos.
\n - Possibilidade de sobreposição de várias camadas de mapas, como trilhos GPX ou navegação, pontos de Interesse, favoritos, curvas de nível, paragens de transporte público, mapas adicionais com transparência personalizável.
\n
@@ -1086,7 +1086,7 @@
Wikipédia (off-line)Marca MarítimaEscolha perfis visíveis.
- Perfis da aplicação
+ Perfis da appDestinoRosaCastanho
@@ -1341,7 +1341,7 @@
VerNorteLeste
- Memória interna da aplicação
+ Memória interna da appIrConfigurações de navegaçãoConfigurações gerais
@@ -1521,8 +1521,8 @@
O mapa %1$s está pronto para ser usado.Mapa descarregadoMostrar mapa
- Define o sinalizador que indica a primeira inicialização da aplicação, mantém todas as outras configurações inalteradas.
- Simular arranque inicial da aplicação
+ Define o sinalizador que indica a primeira inicialização da app, mantém todas as outras configurações inalteradas.
+ Simular arranque inicial da appgeo:Partilhar LocalizaçãoEnviar
@@ -1572,7 +1572,7 @@
Escolher orientação por vozEscolher ou descarregar a orientação por voz para o seu idioma.Noite
- Há uma nova opção para controlar principalmente a aplicação através do painel de controlo flexível ou um menu estático. A sua escolha pode ser alterada nas configurações do painel.
+ Há uma nova opção para controlar principalmente a app através do painel de controlo flexível ou um menu estático. A sua escolha pode ser alterada nas configurações do painel.Painel de controlo ou menu de controloAtualizarApenas descarregar com Wi-Fi
@@ -1661,7 +1661,7 @@
FinoMédiaNegrito
- Agora a aplicação está autorizada a escrever no armazenamento externo, mas primeiro é necessário reiniciar a aplicação.
+ Agora a app está autorizada a escrever no armazenamento externo, mas primeiro é necessário reiniciar a app.Mover ↑Mover ↓Terminar a navegação
@@ -1959,7 +1959,7 @@
Sem sobreposiçãoSem subposiçãoErro
- Assine a nossa lista de e-mail sobre descontos da aplicação e ganhe mais 3 descarregamentos de mapas!
+ Assine a nossa lista de e-mail sobre descontos da app e ganhe mais 3 descarregamentos de mapas!Curvas de nível de profundidade marítima e seamarks.Muito obrigado por comprar \'Contornos de profundidade náutica\'Contornos de profundidade náutica
@@ -1973,10 +1973,10 @@
Fontes do mapaCirculação pela direitaAutomático
- Não envie estatísticas anónimas de utilização da aplicação
- OsmAnd recolhe informação sobre as secções da aplicação que abriu. Não são enviadas: a sua localização; a informação que introduz na aplicação; detalhes de áreas que veja, procure ou descarregue.
+ Não envie estatísticas anónimas de utilização da app
+ OsmAnd recolhe informação sobre as secções da app que abriu. Não são enviadas: a sua localização; a informação que introduz na app; detalhes de áreas que veja, procure ou descarregue.Não mostrar mensagens ao iniciar
- Nâo mostrar descontos da aplicação e mensagens de eventos locais especiais.
+ Nâo mostrar descontos da app e mensagens de eventos locais especiais.Opções de estacionamentoMuito obrigado por comprar a versão paga de OsmAnd.Inclinado
@@ -2208,7 +2208,7 @@
Como abrir a hiperligação\?Ler a Wikipédia desligado da InternetDescarregar tudo
- Reiniciar a aplicação
+ Reiniciar a appMostrar imagensCancelou a sua assinatura do OsmAnd LiveRenovar assinatura para continuar a utilizar todas as funcionalidades:
@@ -2232,7 +2232,7 @@
Guias para os lugares mais interessantes do mundo dentro do OsmAnd, sem uma conexão com a Internet.Atualizações de mapa mensaisAtualizações de mapa a cada hora
- Compra na aplicação
+ Compra na appPagamento de uma só vezUma vez comprado, estará sempre disponível para si.Comprar - %1$s
@@ -2475,7 +2475,7 @@
\nRepresenta área: %1$s x %2$sTolerância do limite de velocidadeSelecione a margem de tolerância de limite de velocidade, acima do qual receberá um aviso de voz.
- O nome do Favorito foi modificado para %1$s para facilitar gravar corretamente a sequência de caracteres com emoticons para um ficheiro.
+ O nome do favorito foi modificado para %1$s para facilitar gravar corretamente a cadeia de caracteres com emoticons num ficheiro.Imprimir rotaNome de favorito duplicadoNome favorito especificado já está em uso, foi alterado para %1$s para evitar a duplicação.
@@ -2551,8 +2551,8 @@
Renderizar caminhos de acordo com a escala de SAC.Renderizar caminhos de acordo com traços OSMC.Hora intermediária
- OsmAnd (sigla em inglês de direções de navegação automatizada do OSM) é uma aplicação de mapas e navegação com acesso a dados livres, mundiais e de alta qualidade do OSM.
-\n
+ OsmAnd (sigla em inglês de direções de navegação automatizada do OSM) é uma app de mapas e navegação com acesso a dados livres, mundiais e de alta qualidade do OSM.
+\n
\nPoderá usar o navegador visual e por voz, ver POIs (pontos de interesse), criar e gerir trilhos GPX, usar (através de um suplemento) curvas de nível e dados de altitude, escolher entre os modos motorista, ciclista e pedestre, editar o OpenStreetMap e muito mais.Navegação GPS
\n• Escolha entre modos off-line (sem tarifa de roaming quando estiver no exterior) ou on-line (mais rápido)
@@ -2595,7 +2595,7 @@
\n • Envie trilhos GPX para o OpenStretMap diretamente da aplicação
\n • Adicione POIs e envie-os diretamente para o OpenStretMap (ou mais tarde se estiver desconectado da Internet)
\n
- OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, melhorando as traduções ou programando novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades.
+ OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a app reportando erros, melhorando as traduções ou programando novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades.
\n Cobertura de mapa e qualidade aproximada:
\n • Europa Ocidental: ****
\n • Europa Oriental: ***
@@ -2609,11 +2609,11 @@
\n • Antártida: *
\n A maioria dos países ao redor do globo está disponível para descarregar!
\n Obtenha um navegador confiável no seu país - seja em França, Alemanha, México, Reino Unido, Espanha, Holanda, EUA, Rússia, Brasil ou qualquer outro.
- OsmAnd+ (direções de navegação automatizada do OSM) é uma aplicação de mapas e navegação com acesso a dados livres do OSM, de todo o mundo e de alta qualidade.
-\nDesfrute da navegação visual ou por voz, vendo POIs (pontos de interesse), criando e gerindo trilhos GPX, usando informação de altitude e curvas de nível, escolher entre modos dirigir, andar de bicicleta e pedestre, editar o OpenStreetMap e muito mais.
-\n
-\nOsmAnd+ é a versão paga da aplicação. Ao comprá-lo, está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações.
-\n
+ OsmAnd+ (direções de navegação automatizada do OSM) é uma app de mapas e navegação com acesso a dados livres do OSM, de todo o mundo e de alta qualidade.
+\nDesfrute da navegação visual ou por voz, ver POIs (pontos de interesse), criando e gerindo trilhos GPX, usando informação de altitude e curvas de nível, escolher entre modos dirigir, andar de bicicleta e pedestre, editar o OpenStreetMap e muito mais.
+\n
+\nOsmAnd+ é a versão paga da app. Ao comprá-lo, está a apoiar o projeto, a financiar o desenvolvimento de novas funcionalidades e a receber as últimas atualizações.
+\n
\nAlgumas das características principais:Navegação
\n• Funciona on-line (rápido) ou off-line (sem custos de roaming quando estiver no estrangeiro)
@@ -2654,10 +2654,10 @@
\n• Visualização de curvas de nível e sombreamento de relevo (via suplemento adicional)Contribua diretamente para o OpenStreetMap
\n • Envie relatórios de erros.
-\n • Envie trilhos GPX para o OpenStretMap diretamente da aplicação.
+\n • Envie trilhos GPX para o OpenStretMap diretamente da app.
\n • Adicione POIs e envie-os diretamente para o OpenStretMap (ou mais tarde se estiver desconectado da Internet).
\n • Gravação de viagem opcional também em plano de fundo (enquanto o aparelho está no modo adormecido).
-\n OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, melhorando as traduções ou programando novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades.
+\n OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a app por reportar erros, a melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades.
\nCobertura de mapa e qualidade aproximada:
\n• Europa Ocidental: ****
@@ -3358,7 +3358,7 @@
Tocar em \'Aplicar\' apagará os perfis removidos permanentemente.Perfil principalSelecione a cor
- Perfis padrão do OsmAnd não podem ser apagados, mas desativados (na tela anterior) ou classificados na parte inferior.
+ Perfis predefinidos do OsmAnd não podem ser apagados, mas desativados (no ecrã anterior) ou classificados na parte inferior.Editar perfisO \'Tipo de navegação\' controla como as rotas são calculadas.Aspeto do perfil
@@ -3887,7 +3887,7 @@
Apenas a linha da rota será gravada, os pontos de passagem serão apagados.Nome do ficheiro%s ficheiros de faixa selecionados
- Vai pausar o registo de faixas quando a aplicação for morta (através de aplicações recentes). (indicação de fundo de OsmAnd desaparece da barra de notificação do Android.)
+ Vai pausar o registo de faixas quando a app for morta (através de apps recentes). (indicação de fundo de OsmAnd desaparece da barra de notificação do Android.)- Função atualizada de Planear uma rota: permite utilizar diferentes tipos de navegação por segmento e a inclusão de faixas
\n
\n - Novo menu Aparência para trilhos: selecionar cor, espessura, setas de direção de visualização, ícones de início/fim
@@ -3903,4 +3903,9 @@
\n - Problemas com as configurações de importação/exportação de perfis resolvidos
\n
\n
+ Última modificação
+ Nome: Z – A
+ Nome: A – Z
+ Ícones de início/fim
+ Obrigado por comprar \'Curvas de nível\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-ru/phrases.xml b/OsmAnd/res/values-ru/phrases.xml
index f67e31fb94..d87b1f7c05 100644
--- a/OsmAnd/res/values-ru/phrases.xml
+++ b/OsmAnd/res/values-ru/phrases.xml
@@ -3832,4 +3832,5 @@
Малые электроприборыМагазин ореховУлей
+ СПГ
\ No newline at end of file
diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml
index 701641a64d..b6dd46c4e9 100644
--- a/OsmAnd/res/values-ru/strings.xml
+++ b/OsmAnd/res/values-ru/strings.xml
@@ -52,7 +52,7 @@
ПлагинПриобретите и установите плагин «Контурные линии» для отображения градуированных вертикальных областей.Цветовая схема
- Показывать, начиная с уровня масштабирования
+ Показывать начиная с масштабаАнимация моего положенияВключить анимацию прокрутки карты с моим положением во время навигации.Масштаб: %1$s
@@ -60,7 +60,7 @@
Выбрать цветЗадать имяДля больших расстояний: добавьте промежуточные пункты, если маршрут не построен в течение 10 минут.
- Разрешить частный доступ
+ Разрешить частные зоныРазрешить доступ на частную территорию.ОбзорВыберите улицу
@@ -71,7 +71,7 @@
Ближайшие городаВыберите городПоиск почтового индекса
- Аудиозаметка
+ Запись аудиоЗаписать видеоФотозаметкаOSM-заметка
@@ -86,7 +86,7 @@
ВидимыеВосстановить покупкиШрифты карты
- Посмотреть на карте
+ Анализ на картеМорские картыКонтуры морских глубинКонтуры морских глубин
@@ -105,7 +105,7 @@
Добавление нового пункта назначения в центре экрана. Ранее выбранный пункт назначения станет последним промежуточным пунктом.Кнопка для установки центра экрана пунктом отправления. Затем нужно будет выбрать пункт назначения или запустить расчёт маршрута.Кнопка для установки центра экрана пунктом назначения с заменой предыдущего (если был задан).
- Установка центра экрана первой промежуточной точкой маршрута.
+ Установка центра экрана местом первой остановки на маршруте.Нет покрытияНет подложкиГористый
@@ -115,7 +115,7 @@
СбалансированныйПредпочитать переулкиВыберите предпочтительный рельеф.
- Склон
+ УклонДобавить новую папкуТочки удалены.Вы уверены, что хотите удалить %1$d точки\?
@@ -171,7 +171,7 @@
Добавить источник картыИсточник карты изменён на «%s».Удерживайте кнопку для перемещения её по экрану.
- Показывать контуры и точки глубины.
+ Показывать контуры и точки глубин.Контуры морских глубинЧастота горизонталейЧастота горизонталей
@@ -202,7 +202,7 @@
ПродолжитьПаузаПоездка
- Записано
+ Записано в трекЗаписьНет данныхМинимальная скорость для записи
@@ -233,7 +233,7 @@
Сохранить фильтрУдалить фильтрНовый фильтр
- Изменить позицию
+ Изменение позицииТекущий путьНавигация OsmAnd LiveУровень заряда батареи
@@ -317,18 +317,18 @@
Открыть внешний проигрывательУдалить эту запись?недоступно
- Аудиозаметка
- Видеозаметка
- Слой аудиозаписей
- Запись не может быть воспроизведена.
+ Запись аудио
+ Запись видео
+ Слой медиазаписей
+ Не удаётся воспроизвести запись.Удалить записьПроигратьЗапись %1$s %3$s %2$sЗапись
- Аудиозаметки
+ МедиазаметкиOsmAnd-плагин для линий высотИзмерение расстояний
- Нажмите «Использовать местоположение…» чтобы добавить заметку к данному местоположению.
+ Нажмите «Использовать местоположение…» для добавления заметки к месту.АудиозаметкиСоздавайте аудио-, видео- и фотозаметки в поездке, используя виджет или контекстное меню.Аудио/видеозаметки
@@ -368,9 +368,9 @@
Использовать онлайн-карты (загрузка и кеширование на SD-карте).Онлайн-картыВыберите источник онлайн или кешированных растровых карт.
- Доступ ко множеству онлайн-карт (т. н. тайловых или растровых): от встроенных OSM (как Mapnik), до спутниковых снимков и слоёв специального назначения, таких как карты погоды, климатические, геологические карты, затенения рельефа и др.
+ Доступ ко множеству онлайн-карт (т. н. тайловых или растровых): от встроенных OSM (как Mapnik) до спутниковых снимков и слоёв специального назначения, таких как карты погоды, климатические, геологические карты, затенения рельефа и др.
\n
-\n Любая из этих карт может быть использована в качестве базовой либо как наложение или подложка к другой базовой карте (например стандартной локальной карте OsmAnd). Некоторые элементы векторной карты OsmAnd можно скрыть в меню «Настройки карты».
+\n Любая из этих карт может быть использована как основная или в качестве подложки к другой карте (например стандартной локальной карте OsmAnd). Некоторые элементы векторной карты OsmAnd можно скрыть в меню «Настройки карты».
\n
\n Карты можно загрузить непосредственно из интернета или подготовить для использования в автономном режиме (и вручную скопировать в папку данных OsmAnd) в виде базы данных sqlite, которая может быть создана с помощью различных инструментов подготовки карт сторонних производителей.Показывает настройки для включения фонового отслеживания и навигации путём периодического пробуждения устройства GPS (с выключенным экраном).
@@ -505,7 +505,7 @@
Голосовые подсказки (TTS)Голосовые подсказки (записанные)Данные POI
- Голос TTS
+ TTSНовый поискРазмер текста для названий на карте:Размер текста
@@ -518,14 +518,14 @@
Обратное направление трекаИспользовать текущий пункт назначенияПройти весь путь
- Для этого региона доступны локальные векторные карты.
-\n\t
-\n\tДля их использования выберите в \"Меню\" → \"Настройка карты\" → \"Источник карты…\" → \"Векторные карты\".
- Голосовые инструкции
+ Для этого региона есть локальные векторные карты.
+\n\t
+\n\tДля использования выберите их в качестве источника (Меню → Настройка карты → Источник карты → Локальные векторные карты).
+ Аудиоканал голосовых инструкцийВыберите канал вывода голосовых подсказок.
- Канал голосовых звонков (прерывает автомобильную Bluetooth стереосистему)
- Канал уведомлений
- Канал медиа/навигации
+ Голосовые звонки (для прерывания автомобильной стереосистемы Bluetooth)
+ Уведомления
+ Мультимедиа, навигацияПриложение не может загрузить слой карты %1$s, переустановка может решить проблему.Отрегулируйте прозрачность наложения.Прозрачность наложения
@@ -605,8 +605,8 @@
Чтение кешированных растровых карт…Недостаточно памяти для локальной карты «{0}»Версия локальной карты «{0}» не поддерживается
- Локальная навигация OsmAnd является экспериментальной функцией и не работает на длинные расстояния более 20 километров.
-\n
+ Офлайн-навигация OsmAnd — это экспериментальная функция, и она не работает на дистанциях больше 20 км.
+\n
\nНавигация временно переключена на онлайн-сервис CloudMade.Невозможно найти указанную папку.Папка хранилища данных
@@ -616,7 +616,7 @@
Установить OsmAnd — {0} из {1} {2} МБ\?Не удалось получить список сборок OsmAndЗагружаются сборки OsmAnd…
- Выберите сборку OsmAnd для установки
+ Выберите сборку для установкиГолосовая навигация недоступна. Перейдите в «Настройки» → «Настройки навигации», выберите профиль → «Голосовые данные» и выберите или загрузите пакет голосовых подсказок.Выберите пакет голосовых подсказокПоказывать производительность отрисовки.
@@ -638,7 +638,7 @@
Самый быстрый маршрутРасчёт скоростного маршрута вместо кратчайшего.На масштабе {0} загрузить {1} тайлов ({2} МБ)
- Загрузить карту
+ Скачать картуНаибольший масштаб для предварительной загрузкиВыбранная карта не может быть загруженаНепрерывная отрисовка
@@ -669,9 +669,9 @@
Локальные векторные картыРедактировать POIУдалить POI
- По направлению компаса
- По направлению движения
- Не вращать (север сверху)
+ по направлению компаса
+ по направлению движения
+ не вращать (север сверху)Выравнивание карты:Ориентация картыДетали маршрута
@@ -715,10 +715,10 @@
ФильтрЗвукБез звука
- Выберите язык голосовых инструкции для навигации.
+ Выберите голосовое сопровождение для навигации.Голосовые данныеИнициализируются голосовые данные…
- Голосовые данные не поддерживаются текущей версией приложения
+ Неподдерживаемая версия голосовых данныхВыбранные голосовые данные не правильного форматаВыбранный пакет голосовых подсказок не доступенSD-карта недоступна.
@@ -726,13 +726,13 @@
Карта памяти доступна только для чтения.
\nТеперь можно только просматривать предварительно загруженную карту, а не загружать новые области.Файл распаковывается…
- Направо и прямо
- Резко направо и прямо
- Плавно направо и прямо
- Налево и прямо
- Резко налево и прямо
- Плавно налево и прямо
- Выполните разворот, затем прямо
+ Направо
+ Резко направо
+ Плавно направо
+ Налево
+ Резко налево
+ Плавно налево
+ Выполните разворотДвигайтесь прямоПродолжитьЗагрузить детальные карты регионов
@@ -844,7 +844,7 @@
Сохранить текущий трекУкажите интервал фиксирования точек для записи трека во время навигацииИнтервал записи во время навигации
- Во время навигации GPX треки будут автоматически сохранены в папку с треками.
+ Во время навигации GPX-треки будут автоматически сохранены в папку с треками.Автозапись трека во время навигацииОбновить картуОбновить часть карты
@@ -864,7 +864,7 @@
3D видПоказать последние использованные POI на карте.Показывать POI
- Выберите источник онлайн или кешированных тайлов карты
+ Выберите источник онлайн- или кешированных тайлов карт.Растровые картыИсточник картыИспользовать интернет
@@ -901,7 +901,7 @@
ДомПересечение улицОбновить карту
- Создать POI
+ Добавление POIДаОтменаНет
@@ -1160,10 +1160,10 @@
\n — подсказки полосы движения, отображение ограничения скорости, предварительно записанные и синтезированные голосовые подсказки
\nБез автомагистралей
- Привязываться к дорогам во время навигации.
+ Привязывать позицию к дороге во время навигации.Привязка к дорогеПромежуточный пункт %1$s слишком далеко от ближайшей дороги.
- Достигнут промежуточный пункт
+ Вы прибыли в промежуточный пунктПромежуточный пунктПромежуточный пунктКонец маршрута слишком далеко от ближайшей дороги.
@@ -1177,11 +1177,11 @@
РестораныДостопримечательностиПоследний промежуточный пункт
- Первый промежуточный пункт
- Добавить последним промежуточным пунктом
- Добавить первым промежуточным пунктом
+ Сделать начальной остановкой
+ Сделать последней остановкой
+ Сделать начальной остановкойЗаменить пункт назначения
- Пункт назначения уже задан:
+ Пункт назначения уже заданПункт %1$sТочки маршрутаПромежуточный пункт %1$s
@@ -1201,12 +1201,12 @@
Настройки аудио и видео Изменить порядокПросмотр
- Сделать фото
+ СнимокСделать фотоСинхронизация треков и медиазаметок с вашим аккаунтом Dropbox.Плагин Dropbox
- Плагин обеспечивает наложение контурных линии и (рельефа) затемняющего слоя, которые будут отображаться поверх стандартных карт OsmAnd. Эта функция высоко оценится спортсменами, туристами, путешественниками и всеми, кто заинтересован в рельефной структуре ландшафта.
-\n
+ Плагин обеспечивает наложение контурных линии и затемняющего слоя (рельефа), которые будут отображаться поверх стандартных карт OsmAnd. Эту функцию оценят спортсмены, туристы, путешественники и все, для кого рельеф местности имеет значение.
+\n
\nГлобальные данные (между 70° на севере и 70° на юге) основываются на измерениях SRTM (Shuttle Radar Topography Mission) и ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), инструментом визуализации Terra, флагманского спутника Земли системы наблюдения NASA. ASTER является результатом совместных усилий NASA, министерства экономики Японии, торговли и промышленности (METI), космических систем Японии (J-spacesystems).Фото %1$s %2$sМедиаданные
@@ -1227,7 +1227,7 @@
Время прибытияИнформация GPSнет
- Слой рельефа местности
+ Слой затенения рельефаНазвание улицыНомер домаНет соединения по Wi-Fi. Использовать текущее интернет-соединение для загрузки\?
@@ -1239,7 +1239,7 @@
Укажите адресВыбор избраннойМодификации OSM
- Выбирать
+ ВыбратьOsmAnd карты и навигацияOsmAnd+ карты и навигацияУменьшает «шум» компаса, но добавляет инерцию.
@@ -1258,7 +1258,7 @@
Отменить маршрутОчистить пункт назначенияИскать улицу в ближайших населённых пунктах
- В порядке следования домов
+ Расположить в оптимальном порядкеДоступно %1$d файлов для скачиванияосталось %1$d файловПодождите, пока завершится текущая операция
@@ -1272,10 +1272,10 @@
Резервное копирование как правка OSMвысотаOsmChange-файл создан за %1$s
- * Нажмите, чтобы отметить точку.
-\n* Удерживайте нажатие на карте, чтобы удалить предыдущую точку.
-\n* Удерживайте нажатие на точке, чтобы просмотреть и добавить описание.
-\n* Нажмите на виджет измерения, чтобы увидеть больше действий.
+ * Нажмите, чтобы отметить точку.
+\n* Нажмите и удерживайте карту, чтобы удалить предыдущую точку.
+\n* Удерживайте точку для просмотра и добавления описания.
+\n* Нажмите на виджет измерения для других действий.Использовать магнитный датчик вместо датчика ориентации.ДругиеКонтурные линии
@@ -1301,9 +1301,9 @@
Дорожные предупрежденияОчистить промежуточные пунктыОставить промежуточные пункты
- К:
+ Назначение:Через:
- От:
+ Отправление:Промежуточные пункты уже заданы.Названия улиц (TTS)Объявлять…
@@ -1345,9 +1345,9 @@
Симуляция использования рассчитанного маршрута Симуляция использования трека GPXБез автомасштаба
- Ближний план
- Средний план
- Дальний план
+ К ближнему плану
+ К среднему плану
+ К дальнему плануИзбегать автомагистралейБез автомагистралейПредпочитать автомагистрали
@@ -1358,7 +1358,7 @@
Избегать грунтовых дорогБез паромовИсключить паромные переправы
- Максимальная масса
+ Предельная массаУкажите допустимый предел массы автомобиля для учёта при построении маршрута.Отображение картыНавигационные знаки (водоёмы)
@@ -1386,7 +1386,7 @@
Задать пункт назначенияПредпочтения маршрутаИнформация про маршрут
- Добавить как новый пункт назначения
+ Добавить пункт назначенияИспользовать показанный путь для навигации?Рассчитать сегмент маршрута OsmAnd без интернетаРассчитать маршрут OsmAnd для первого и последнего сегмента маршрута
@@ -1445,15 +1445,15 @@
ГолосРазноеЛокализация
- Голосовые подсказки приостанавливают воспроизведение музыки.
- Приостановить музыку
+ Приостановка воспроизведения во время подсказок.
+ Прерывать музыкуПоделиться маршрутом используя файл GPXНеправильный формат: %sМаршрут предоставленный через OsmAnd
- Только вручную (нажатием «стрелочки»)
+ При нажатии на стрелку (вручную)Повторять навигационные инструкции с регулярными интервалами.Повторять навигационные инструкции
- Объявление прибытия
+ Объявление о прибытииКак скоро следует сообщать о прибытии?Места, отправленные в OsmAndРассчитать маршрут между точками
@@ -1492,7 +1492,7 @@
%1$s точекТочка %1$s%1$s \nМаршрутных точек %2$s
- Показывать кнопки изменения масштаба во время навигации.
+ Показывать кнопки масштаба во время навигации.Кнопки масштабаСортировать по расстояниюСортировать по имени
@@ -1536,14 +1536,14 @@
Сербский (кириллица)Китайский (упрощённый)Китайский (традиционный)
- Маршруты метро
+ Линии метроПродолжить навигациюПриостановить навигацию
- Визуализация пути по шкале SAC.
- Визуализация пути согласно трассам OSMC.
- Пораньше
- Как обычно
- Попозже
+ Отрисовка дорог cогласно шкале SAC.
+ Отрисовка дорог согласно трассам OSMC.
+ Раннее
+ По умолчанию
+ ПозднееНа последних метрахПеший горный туризм по шкале (SAC)Наложение туристических меток
@@ -1581,7 +1581,7 @@
Снизьте скоростьКамера скоростиДорожные предупреждения
- Выберите допустимое значение превышения скорости выше которого вы получите голосовое предупреждение.
+ Выберите значение скорости, при превышении которого вы получите голосовое предупреждение.Допустимое превышение скоростиИзбранная точка переименована на «%1$s», чтобы сохранить строку, содержащую эмотикон в файл.Печать маршрута
@@ -1739,7 +1739,7 @@
Отменить выбор всехПоделитьсяМои места
- Точки
+ ИзбранныеТрекиТекущий трекПоделиться заметкой
@@ -1780,7 +1780,7 @@
\n
\nВ случае активации этого вида, стиль карты меняется на «Зимний/лыжный», показывая все детали пейзажа так, как они выглядят зимой. Такой (зимний) вид может быть отменён либо путём деактивации здесь, либо если вы поменяете «Стиль карты» в меню «Настройки карты» на желаемый вид.Текущий маршрут
- Скачать карты
+ Загрузка картДля правильного отображения дорожных знаков и правил выберите свой регион вождения:Добро пожаловатьОтметить для удаления
@@ -1815,8 +1815,8 @@
Переместить файлы данных Osmand в новое место назначения\?Напечатайте для поискаНомера домов
- Избегать перехода границы
- Максимальная высота
+ Избегать пересечения границ
+ Предельная высотаУкажите высоту транспортного средства для учёта при построении маршрута.Умный пересчёт маршрутаДля больших маршрутов пересчитывать только начало.
@@ -1905,8 +1905,8 @@
ПропуститьOsmAndПлагины
- Локальные карты
-\nи Навигация
+ Офлайн-карты
+\nи навигацияНомер домаТип лыжной трассыАвтообновления
@@ -2030,13 +2030,13 @@
Изменить путевую точку GPXБез лестницИзбегать ступеней и лестниц
- Без пересечений границ
+ Без перехода границыОбновлятьЗагружать только по Wi-FiОбновить сейчасПриложение не имеет разрешения на использование SD-картыДоступные карты
- Начальный пункт
+ Пункт отправленияНе выбраноРазмер хранилищаЗвук
@@ -2065,7 +2065,7 @@
Разбиение на клипыИспользовать разбиение на клипыЦиклическая перезапись клипов при превышении заданного объёма хранилища.
- Поменять местами пункты отправления и назначения
+ Поменять местами пункты отправления и назначенияУдалитьПодземные объектыДанные недоступны
@@ -2110,7 +2110,7 @@
Запись удаленаэлементы удаленыАвтообновления
- Выберите или скачайте голосовые подсказки для вашего языка.
+ Выберите или скачайте голосовое сопровождение для вашего языка.Полный отчётПересчёт маршрутаИмя пользователя и пароль OSM
@@ -2235,7 +2235,7 @@
ПолучитьПолучайте неограниченное количество загрузок карт, вдобавок к еженедельным, ежедневным и даже почасовым обновлениям.Неограниченный доступ к картам, обновлениям и плагину «Википедия».
- Голосовое сопровождение
+ Голосовые инструкцииАбонентская плата взимается за выбранный период. Отменить подписку можно в Google Play в любой момент.Пожертвование для сообщества OSMЧасть вашего пожертвования будет отправлена участникам OSM. Стоимость подписки при этом остаётся прежней.
@@ -2281,16 +2281,16 @@
Изменить положение кнопкиНазвание действияСербский (латиница)
- Голос вкл/выкл
- Включить голос
- Выключить голос
+ Голосовая навигация
+ Включить подсказки
+ Выключить подсказкиНе удалось переместить файл.Благодарим вас за покупку контуров морских глубинДобавить фотоРазрешенияОнлайн-фото
- Здесь нет фотографий.
- Поделитесь вашим просмотром улиц через Mapillary.
+ Здесь нет фото.
+ Поделитесь своими уличными видами через Mapillary.Виджет MapillaryПозволяет быстро внести свой вклад в Mapillary.Фото с улиц онлайн для каждого. Открывайте места, взаимодействуйте, запечатлейте весь мир.
@@ -2337,7 +2337,7 @@
Измерить расстояниеДобавьте хотя бы одну точку.Фотография Mapillary
- Улучшить фотопокрытие через Mapillary
+ Улучшить фотопокрытие в MapillaryСкрыть, начиная с уровня масштабированияПрозрачно-розовыйБерберский
@@ -2380,7 +2380,7 @@
Избегать ледовых дорог и бродов.Моё местоположениеФиниш
- Сортировать
+ СортировкаЭкспорт маркеров в следующий файл GPX:МаркерыИзменить заметку
@@ -2392,7 +2392,7 @@
Критерий сортировки:Выберите способ указания расстояния и направления до маркеров на карте:Смена ориентации карты
- Выберите скорость, при которой переключается ориентация карты с «По направлению движения» на «По направлению компаса».
+ Выберите скорость, при которой ориентация по направлению движения переключится на ориентацию по компасу.Все маркеры перемещены в историюМаркер перемещён в историюМаркер перемещён в действующие
@@ -2440,7 +2440,7 @@
Кнопка для добавления фотозаметки в центре экрана.Добавление в центре экрана OSM-заметки.Добавление POI в центре экрана.
- Переключатель, чтобы включить или выключить голосовые подсказки во время навигации.
+ Включение/отключение голосового сопровождения при навигации.Добавление в центре экрана места парковки.Показывать промежуточный диалогФотографии Mapillary доступны только онлайн.
@@ -2470,7 +2470,7 @@
Полноэкранный режимОтметить пройденнымФайл %1$s не содержит путевых точек, импортировать его как трек?
- Выберите трек, чтобы добавить в маркеры его точки.
+ Выберите трек, чтобы добавить в маркеры его точки.Трек путевых точекНаправоНалево
@@ -2520,14 +2520,14 @@
\n• Поддержка промежуточных точек маршрута
\n• Запись собственного или отправка GPX трека и следование ему
\n
- Карта
-\n• Отображает POI (точки интереса) около вас
-\n• Адаптирует карту в направлении вашего движения (или компаса)
-\n• Показывает, где вы находитесь и куда вы смотрите
-\n• Делитесь своим расположением, чтобы друзья смогли найти вас
-\n• Сохраняет ваши самые важные места в избранных
-\n• Позволяет вам выбрать как отображать названия на карте: на английском, местным или с фонетическим написанием
-\n• Отображает специальные онлайн-тайлы, спутниковые снимки (с Bing), различные метки, как туристические/навигационные треки GPX и дополнительные слои с настраиваемой прозрачностью
+ Карта
+\n• Отображает POI (точки интереса) вокруг вас
+\n• Поворачивает карту по направлению движения (или компаса)
+\n• Показывает вашу позицию и направление взгляда
+\n• Делитесь вашим местоположением, чтобы вас могли найти друзья
+\n• Сохраняет важные для вас места в избранных
+\n• Позволяет выбрать способ отображения названий на карте: на английском, местное или фонетическое написание.
+\n• Отображает специальные онлайн-тайлы, спутниковые снимки (Bing), различные метки, как туристические/навигационные треки GPX и дополнительные слои с настраиваемой прозрачностью
\nКатание на лыжах
\n• OsmAnd-плагин лыжные карты позволяет видеть лыжные трассы с уровнем сложности и некоторой дополнительной информацией, как расположение подъёмников и других объектов.
@@ -2668,9 +2668,9 @@
Копировать местоположение/название POIМесто без названияТекущий
- Добавляет промежуточную остановку
- Добавляет начальную остановку
- Перемещает пункт назначения и создаёт промежуточную точку
+ Добавить последним промежуточным пунктом
+ Добавить первым промежуточным пунктом
+ Ранее выбранный пункт назначения станет последним промежуточным пунктомПоказать закрытые заметкиПоказать/скрыть заметки OSM на карте.GPX — подходит для экспорта в JOSM и другие OSM редакторы.
@@ -2683,7 +2683,7 @@
OSM-заметкиВпереди туннельТуннели
- Сделать отправной точкой
+ Сделать пунктом отправленияВведите имя файла.Ошибка импорта картыКарта импортирована
@@ -2764,7 +2764,7 @@
Начать редактированиеПолучить неограниченный доступДобро пожаловать на открытое бета-тестирование
- Карты горизонталей и карты с отмывкой рельефа
+ Карты горизонталей и затенение рельефаСкачать статьи Википедии для %1$s, чтобы читать их в автономном режиме.Загрузка данных ВикипедииОткрыть статью в интернете
@@ -2788,7 +2788,7 @@
Для катания на лыжах. Выделяет горнолыжные трассы, подъёмники, трассы для беговых лыж и прочее. Меньше отвлекающих второстепенных объектов на карте.Скачать всеПростой стиль для вождения. Мягкий ночной режим, контурные линии, контрастные дороги в оранжевом стиле, тусклые второстепенные объекты карты.
- Для пеших походов, трекинга и велосипедных прогулок на природе. Читабельный на открытом воздухе и при сложном освещении. Контрастные дороги и природные объекты, различные типы маршрутов, контурные линии с расширенными настройками, дополнительные детали. Функция «Качество дорожного покрытия» позволяет различать дороги с различным качеством поверхности. Нет ночного режима.
+ Для пеших походов, трекинга и велопоездок на природе. Хорошо читается при сложном освещении. Контрастные дороги и природные объекты, различные типы маршрутов, контурные линии с расширенными настройками, дополнительные детали. Параметр «Дорожное покрытие» позволяет различать поверхность и качество дорог. Ночной режим отсутствует.Старый стиль по умолчанию «Mapnik». Похожие цвета на «Mapnik».Стиль общего назначения. Густонаселённые города показаны упрощённо. Выделяет контурные линии, маршруты, качество поверхности, ограничения доступа, дорожные щиты, визуализация пешеходных маршрутов по шкале SAC, объекты спортивных сплавов.Открыть ссылку Википедии в онлайн
@@ -2796,13 +2796,13 @@
Получите подписку на OsmAnd Live, чтобы читать статьи в Википедии и Викигиде в автономном режиме.Как открыть ссылку?Читать Википедию в автономном режиме
- Туристический стиль с высоким контрастом и максимальной детализацией. Включает все функции стиля OsmAnd по умолчанию, также отображая как можно больше деталей, в частности дороги, тропы и другие пути для передвижения. Чёткое различие между типами дорог, как во многих туристических атласах. Подходит для дневного, ночного и уличного использования.
+ Туристический стиль с высоким контрастом и максимальной детализацией. Включает все функции стиля OsmAnd по умолчанию, отображая максимальное количество деталей, в частности дороги, тропы и другие пути передвижения. Чёткое различие между типами дорог (как в туристических атласах). Подходит для использования днём, ночью и при ярком освещении.СохранитьДля езды по бездорожью, основано на топографическом стиле (англ. «Topo»), можно использовать с зелёными спутниковыми снимками в качестве подложки. Уменьшенная толщина основных дорог, увеличенная толщина путей, дорожек, велосипедных и других маршрутов.Модификация стиля по умолчанию для увеличения контраста пешеходных и велосипедных дорог. Использует старые цвета Mapnik.Получите OsmAnd Live, чтобы разблокировать все функции: ежедневные обновления карт с неограниченной загрузкой, все платные и бесплатные плагины, Википедия, Викигид и многое другое.Промежуточное время прибытия
- Промежуточное время
+ Прибытие в промежуточный пунктРедактировать действиеПожалуйста, пришлите скриншот этого уведомления на support@osmand.netРедактировать точку
@@ -2855,7 +2855,7 @@
Для продолжения дайте OsmAnd разрешение на определение местоположения.ЧёрныйПоиск улицы
- Сначала выберите город/населённый пункт/местность
+ Укажите город/место/районВосстановитьМаркеры, добавленные как группа избранных или путевых точек GPX и отмеченные как пройденные, останутся на карте. Если группа не активна, маркеры исчезнут с карты.Оставить пройденные маркеры на карте
@@ -2891,7 +2891,7 @@
Точки интереса (POI)Расчёт маршрута…Общественный транспорт
- Выберите дорогу на карте или из списка ниже, которую вы хотите избежать во время навигации:
+ Выберите на карте или в списке ниже дорогу, которой хотите избежать при навигации:Моделировать навигациюВыберите файл трека для следованияГолосовые подсказки
@@ -2908,7 +2908,7 @@
пешкомПоказывать вдоль маршрутаПокрытие
- Тип дороги
+ Класс дорогиКрутизнаДобавить домДобавить работу
@@ -2923,7 +2923,7 @@
Скрыть трекиПоказать трекиВремя суток
- Поворот за поворотом
+ По шагамТипы дорогПереключатель, чтобы показать или скрыть выбранные треки на карте.На %1$s
@@ -2987,7 +2987,7 @@
СтупенькиТропаВелодорожка
- Неопределённая
+ Не определеноУзнайте больше о маршрутизации OsmAnd в нашем блоге.Навигация на общественном транспорте в настоящее время проходит бета-тестирование, возможны ошибки и неточности.Добавить промежуточную точку
@@ -3066,7 +3066,7 @@
МетроЛошадьВертолёт
- Вы можете добавить собственную модифицированную версию routing.xml в ..osmand/routing
+ Вы можете добавить свою модифицированную версию файла routing.xml в ..osmand/routingВыберите значокЛыжиТип: %s
@@ -3101,20 +3101,20 @@
Использовать WunderLINQ для контроляЗначокСобранные данные
- Нажмите ещё раз, чтобы изменить ориентацию карты
+ Нажмите ещё раз для смены ориентации картыПоследний запуск OsmAnd завершился ошибкой. Пожалуйста, помогите нам улучшить OsmAnd, отправив нам отчёт об ошибке.Режим: %sРежим пользователя, полученный из: %sПовторяющееся имяBRouter (локально)
- Альпийские/горные лыжи
+ Горнолыжные спускиСклоны для катания и спуска на горных лыжах и доступ к подъёмникам.Лыжные турыСаниСклоны для катания на санях.Более сложные маршруты с крутыми участками дороги. В целом, препятствия, которых следует избегать.Сложные маршруты, с опасными препятствиями и крутыми участками.
- Предпочитаемая сложность
+ Предпочтительная сложностьСлужба скачивания OsmAndПурпурныйОценить
@@ -3158,7 +3158,7 @@
СбойВнедорожникВыбор настроек карты для профиля
- Выбор настроек экрана для профиля
+ Настройка элементов экрана для профиляВыбор настроек навигации для профиляВыбор верхней границы измененийИспользовать бесконтактный датчик (сенсорный выключатель)
@@ -3187,8 +3187,8 @@
\nРасчёт: %.1f с, %d дорог, %d тайлов)Добавьте хотя бы один элемент в список «Быстрые действия» в настройкахМаршруты для горнолыжного туризма.
- Офпист
- Фрирайды и офписты являются неофициальными неадаптированными трассами. Обычно неухоженные, неразмеченные и неосвещённые вечером. Вход на свой страх и риск.
+ Вне трассы
+ Фрирайды и внетрассовые маршруты — это неофициальные неподготовленные трассы. Обычно неухоженные, неразмеченные и неосвещённые вечером. Вход на свой страх и риск.Открыть трекСоединить разрывы (исключить пробелы)Кемпер
@@ -3233,11 +3233,11 @@
Карта во время навигацииСкорость движения, размеры, масса транспортного средстваПараметры транспортного средства
- Голосовые оповещения происходят только во время навигации.
+ Голосовые инструкции работают только при навигации.Навигационные инструкции и объявленияГолосовые подсказкиЭкранные оповещения
- Настройка параметров маршрута
+ Настройки маршрутизацииПараметры маршрутаБуфер LogcatНастройки плагинов
@@ -3319,7 +3319,7 @@
«%1$s» уже существует. Перезаписать\?Не удалось экспортировать профиль.Импорт профиля
- Чтобы добавить профиль, откройте его с помощью OsmAnd.
+ Для добавления профиля откройте файл профиля с помощью OsmAnd.%1$s импортирован.БелыйНевозможно запустить механизм преобразования текста в речь.
@@ -3365,8 +3365,8 @@
Разрешить только маршруты для катания на конькахМаршруты, подготовленные для фристайла или катания только на коньках без классических треков.Разрешить только классические маршруты
- Маршруты, подготовленные только для классического стиля без конькобежных трасс. Сюда входят маршруты, подготовленные небольшим снегоходом с более свободной лыжнёй и трассами, подготовленные вручную лыжниками.
- Предпочитать маршруты заданной сложности, хотя прокладка маршрута по более сложным или лёгким трассам всё же возможна, если они короче.
+ Маршруты только для классического стиля, без конькобежных трасс. Сюда входят маршруты, подготовленные небольшим снегоходом с более свободной лыжнёй и трассами, подготовленные вручную лыжниками.
+ Предпочитаемый уровень сложности маршрутов. Более сложные или лёгкие трассы могут использоваться, если они короче.Включать на поворотеКласс 1Класс 2
@@ -3390,7 +3390,7 @@
ЭкспертФрирайдЭкстрим
- Неопределённо
+ НеопределённаяКанатная дорогаСоединениеСимулировать свою позицию используя записанный GPX трек.
@@ -3534,12 +3534,12 @@
Сохранение нового профиляНе удалось создать резервную копию профиля.Очистить записанные данные\?
- Побочный эффект: в записи трека будут отсутствовать все участки, где критерий минимальной скорости не был соблюдён (например, когда вы толкаете велосипед вверх по крутому склону). Также не будет информации о периодах покоя, например, во время отдыха. Это влияет на любой анализ или последующую обработку, например, при попытке определить общую продолжительность поездки, время в движении или среднюю скорость.
+ Побочный эффект: в треке будут отсутствовать все участки, где не соблюдён критерий минимальной скорости (например где вы толкаете велосипед вверх по крутому склону). Также не будет информации о периодах покоя, например во время отдыха. Это влияет на любой анализ или последующую обработку, например при попытке определить общую продолжительность поездки, время в движении или среднюю скорость.Побочный эффект: в результате фильтрации по точности, точки могут быть полностью пропущены, например, под мостами, под деревьями, между высокими зданиями или при определённых погодных условиях.Примечание: при включении GPS непосредственно перед записью точность определения первой точки может быть снижена, поэтому мы рассматриваем добавление секундной задержки перед записью точки (либо записи лучшей из трёх последовательных точек и т. д.), но пока это не реализовано.Фильтр предотвращает запись точек при отсутствии фактического перемещения и улучшает вид треков без обработки.Побочные эффекты: периоды в состоянии покоя не записываются вообще или только по одной точке каждый. Небольшие (в реальности) перемещения (например, в сторону, указывающие возможное изменение направления движения) могут быть отфильтрованы. Файл содержит меньше информации для последующей обработки и имеет худшую статистику, отфильтровывая явно избыточные точки во время записи, при этом потенциально сохраняя артефакты, вызванные плохим приёмом или эффектами модуля GPS.
- Рекомендация: настройка 5 метров может должна вас устроить, если нет необходимости учитывать более короткие перемещения, и вы точное не хотите записывать данные в состоянии покоя.
+ Рекомендация: настройка 5 метров может подойти вам, если нет необходимости учитывать более короткие перемещения, и вы точно не хотите записывать данные в состоянии покоя.Указанные %1$s уже существуют в OsmAnd.Импорт данных из %1$sИмпортирование
@@ -3553,9 +3553,9 @@
Отклонение, при котором маршрут будет пересчитан.ЛегендаНевозможно разобрать геоссылку «%s».
- Для отображения затенения рельефа на карте необходимы дополнительные карты.
+ Для отображения затенения рельефа требуются дополнительные карты.Мин.
- Отображение затенения рельефа или карты уклонов. Подробнее об этих типах карт вы можете прочитать на нашем сайте.
+ Способы отображения рельефа местности: посредством теней (затенение рельефа) или цветов (карта уклонов). Подробнее об этих типах карт вы можете прочитать на нашем сайте.ПрозрачностьУровни масштабаПересчитывать маршрут в случае отклонения
@@ -3580,14 +3580,15 @@
Примечание: проверка скорости > 0: большинство модулей GPS сообщают значение скорости только в том случае, если алгоритм определяет, что вы движетесь, и ничего, если вы не перемещаетесь. Следовательно, использование параметра > 0 в этом фильтре в некотором смысле приводит к обнаружению факта перемещения модуля GPS. Но даже если мы не производим данную фильтрацию во время записи, то всё равно эта функция используется при анализе GPX для определения скорректированного расстояния, то есть значение, отображаемое в этом поле, является расстоянием, записанным во время движения.Разделение записиУкажите веб-адрес со следующими параметрами: lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}.
- В этом случае будут записываться только точки, измеренные с минимальной точностью (в метрах/футах согласно настройкам устройства). Точность — это близость измерений к истинному местоположению и не имеет прямого отношения к точности, подразумевающейся под разбросом повторных замеров.
+ "Будут записываться только точки, отвечающие
+\n в минимальной точностью (в метрах/футах —зависит от настроек системы). Точность — это близость измерений к истинному положению, и она не связана напрямую с точностью, которая представляет собой разброс повторных измерений."Рекомендация: попробуйте сначала воспользоваться детектором движения через фильтр минимального смещения (B), что может дать лучшие результаты и вы потеряете меньше данных. Если треки остаются шумными на низких скоростях, попробуйте использовать ненулевые значения. Обратите внимание, что некоторые измерения могут вообще не указывать значения скорости (некоторые сетевые методы), и в этом случае ничего не будет записываться.
- Уклон использует цвета для визуализации крутизны рельефа.
+ Для визуализации крутизны рельефа используются цвета.Подробнее об уклонах можно прочитать в %1$s.Затенение рельефа
- Затенение рельефа использует тёмные оттенки для отображения склонов, вершин и низменностей.
- Для отображения склонов на карте необходимы дополнительные карты.
- Уклоны
+ Для отображения склонов, вершин и низменностей используются тёмные тени.
+ Для отображения уклонов требуются дополнительные карты.
+ Карта уклоновЗаменить этой точкой другую.Изменения применены к профилю «%1$s».Невозможно прочитать из «%1$s».
@@ -3789,7 +3790,7 @@
Изменение масштаба карты кнопками громкости.Масштабирование кнопками громкостиУкажите длину автомобиля, для длинных транспортных средств могут применяться ограничения на маршруте.
- Удалить следующий пункт
+ Удалить ближайший пунктЗадайте название точкиСледующая точка маршрута будет удалена. Если это конечный пункт, навигация завершится.Информация о достопримечательностях из Википедии. Ваш карманный офлайн-путеводитель — просто включите плагин Википедии и читайте об объектах вокруг вас.
@@ -3803,7 +3804,7 @@
Добавить к трекуДля продолжения задайте рабочие дниМаршрут между точками
- Составить маршрут
+ Составление маршрутаВыберите способ разбиения: по времени или по расстоянию.Интервал между метками расстояния или времени на треке.Своё
@@ -3883,17 +3884,17 @@
сохраненДобавьте хотя бы две точки.ПОВТОРИТЬ
- • Обновлённый режим планирования маршрута позволяет использовать разные типы навигации для каждого сегмента и прикрепляет любой трек к дорогам
+ • Обновлённая функция планирования маршрута позволяет применять к сегментам разные режимы навигации и настраивать привязку к дорогам
\n
-\n • Новые параметры внешнего вида для треков: можно выбрать цвет, толщину, включите стрелки направления и отметки начала/окончания
+\n • Новые настройки вида треков: выбор цвета и толщины линии, указатели направления, метки начала и конца маршрута
\n
-\n • Улучшена видимость велосипедных узлов
+\n • Повышенная видимость велосипедных узлов
\n
-\n • Контекстное меню для треков с основной информацией
+\n • Контекстное меню с основной информацией для треков
\n
\n • Улучшенные алгоритмы поиска
\n
-\n • Улучшены параметры следования по треку в навигации
+\n • Улучшенные настройки следования по треку в Навигации
\n
\n • Исправлены проблемы с импортом/экспортом настроек профиля
\n
@@ -3901,4 +3902,6 @@
Последнее изменениеИмя: Я - АИмя: А - Я
+ Значки старта и финиша
+ Спасибо за покупку \'Контурных линий\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-sc/phrases.xml b/OsmAnd/res/values-sc/phrases.xml
index df52122c1e..62f4c1ece8 100644
--- a/OsmAnd/res/values-sc/phrases.xml
+++ b/OsmAnd/res/values-sc/phrases.xml
@@ -3840,4 +3840,5 @@
Eletrodomèsticos minoresTabellone de sas tzucadasRicàrriga de abba potàbile
+ GNL (LNG)
\ No newline at end of file
diff --git a/OsmAnd/res/values-sc/strings.xml b/OsmAnd/res/values-sc/strings.xml
index accd1282b4..57221ff561 100644
--- a/OsmAnd/res/values-sc/strings.xml
+++ b/OsmAnd/res/values-sc/strings.xml
@@ -3903,4 +3903,6 @@
Ùrtima modìficaNùmene: Z – ANùmene: A – Z
+ Iconas de incumintzu/fine
+ Gràtzias pro àere comporadu \'Curvas de livellu\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-sk/phrases.xml b/OsmAnd/res/values-sk/phrases.xml
index 14f1a5e6be..28ac2c4297 100644
--- a/OsmAnd/res/values-sk/phrases.xml
+++ b/OsmAnd/res/values-sk/phrases.xml
@@ -3274,9 +3274,9 @@
MahájanaZamrznutíTuristická/trasová značka
-
-
-
+ Ko-Shintō
+ Jizō
+ PrasatApoštolská cirkevRadiačná onkológiaNebezpečenstvo
@@ -3567,4 +3567,89 @@
ŠípkaVibrácieTlak
+ Skvapalnený zemný plyn
+ Obchod s orechmi
+ Včelí úľ
+ Cestovný poriadok
+ Odjazdy v reálnom čase
+ Intervaly
+ Áno
+ Tabuľa odjazdov: nie
+ Výťah
+ Mestský blok
+ Mestský obvod
+ Šípka: nie
+ Nie
+ Áno
+ Vibrovanie: nie
+ Výber hotovosti: cudzie karty
+ Výber hotovosti: minimálny nákup
+ Poplatok za výber hotovosti: nie
+ Poplatok za výber hotovosti: áno
+ Výber hotovosti: nie je vyžadovaný nákup
+ Výber hotovosti: vyžadovaný nákup
+ Mena výberu hotovosti
+ Limit výberu hotovosti
+ Typ výberu hotovosti: samoobslužný výber
+ Typ výberu hotovosti: pri pokladni
+ Operátor výberu hotovosti
+ Výber hotovosti
+ Výber hotovosti: áno
+ Zmiešané: nie
+ Zmiešané: áno
+ Ľad: nie
+ Ľad: áno
+ Trvanlivosť vodného zdroja: núdzový
+ Trvanlivosť vodného zdroja: trvalý
+ Ordinácia pôrodnej asistentky
+ Opatrovateľská služba
+ Ordinácia psychológa
+ Ordinácia liečiteľa
+ Ordinácia terapeuta
+ Ordinácia lekára
+ Zdravotná služba: testovanie: nie
+ Zdravotná služba: testovanie: áno
+ Zdravotná služba: podpora: nie
+ Zdravotná služba: podpora: áno
+ Zdravotná služba: očkovanie: nie
+ Zdravotná služba: očkovanie: áno
+ Zdravotná služba: prevencia: nie
+ Zdravotná služba: prevencia: áno
+ Zdravotná služba: starostlivosť o deti: nie
+ Zdravotná služba: starostlivosť o deti: áno
+ Zdravotná služba: vyšetrenie: nie
+ Zdravotná služba: vyšetrenie: áno
+ Zdravotná služba: poradenstvo: nie
+ Zdravotná služba: poradenstvo: áno
+ Zdravotná služba: opatrovateľstvo: áno
+ Zdravotná služba: opatrovateľstvo: nie
+ Správanie
+ Športová medicína
+ Malé elektrické spotrebiče
+ Tabuľa odjazdov/cestovný poriadok
+ Doplnenie pitnej vody
+ Nasávanie
+ Pod tlakom
+ Spodná voda
+ Potrubie
+ Sieť doplnenia pitnej vody
+ Doplnenie pitnej vody: nie
+ Áno
+ Prekážka
+ Výška vody: pod strednou hladinou
+ Výška vody: nad strednou hladinou
+ Výška vody: prekrýva
+ Výška vody: suché
+ Výška vody: ponorené
+ Výška vody: čiastočne ponorené
+ Nesprávne
+ Primitívne
+ Kontrastné
+ Len keď je povolená chôdza
+ Nie
+ Áno
+ Typ búdky
+ Búdka
+ Nie
+ Áno
\ No newline at end of file
diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml
index a9fbf672f4..6ce4134d80 100644
--- a/OsmAnd/res/values-sk/strings.xml
+++ b/OsmAnd/res/values-sk/strings.xml
@@ -224,7 +224,7 @@
Aktualizovať OsmAnd+Stiahnite novú verziu aplikácie, aby ste mohli použiť nové mapové súbory.Online filter Nominatim
- Hľadám umiestnenie…
+ Hľadám polohu…Moja poloha (nájdená)Adresa…Obľúbené miesta…
@@ -325,11 +325,11 @@
ftmphmi
- Zdieľať umiestnenie cez
+ Zdieľať polohu cezPozícia: %1$s\n%2$sNa zobrazenie umiestnenia nasledujte webový odkaz %1$s alebo odkaz androidovského obsahu (intent link) %2$s
- Poslať umiestnenie
- Zdieľať umiestnenie
+ Poslať polohu
+ Zdieľať polohuGPX bod (waypoint) \"{0}\" pridanýPridať waypoint do nahranej GPX stopyAdministratíva
@@ -419,7 +419,7 @@
sem zadajte čo chcete nájsťMapa s vysokým rozlíšenímNerozťahovať (a nerozmazať) mapové dlaždice na obrazovkách s vysokou hustotou bodov.
- Umiestnenie zatiaľ nenájdené.
+ Poloha zatiaľ nezistená.Hľadať hromadnú dopravuHľadá sa preprava (bez cieľa):Hľadá sa preprava ({0} ako cieľ):
@@ -743,7 +743,7 @@
Pridať pripomienku do aplikácie KalendárČasovo obmedzené parkovanieČasovo neobmedzené parkovanie
- Umiestnenie vášho zaparkovaného vozidla. %1$s
+ Poloha vášho zaparkovaného vozidla. %1$sVyzdvihnúť vozidlo o:popoludnídopoludnia
@@ -754,7 +754,7 @@
Označiť ako parkovacie miestoOdstrániť parkovaciu značkuVýchodzí bod je príliš ďaleko od najbližšej cesty.
- Zdieľané umiestnenie
+ Zdieľaná polohaPridelená pamäť %1$s MB (Android limit %2$s MB, Dalvik %3$s MB).Pridelená pamäťCelková natívna pamäť pridelená aplikácii %1$s MB (Dalvik %2$s MB, iné %3$s MB).
@@ -886,7 +886,7 @@
\n
\nGlobálne údaje (medzi 70° severne a 70° južne) sú založené na meraní SRTM (Shuttle Radar Topography Mission) a ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), zobrazovacieho nástroja na palube Terra, vlajkového satelitu NASA Earth Observing System. ASTER je kooperatívne úsilie medzi NASA, Japonským ministerstvom hospodárstva, obchodu a priemyslu (METI) a Japonských vesmírnych systémov (J-spacesystems).Meranie vzdialenosti
- Stlačte \"Použiť umiestnenie…\" pre pridanie poznámky k polohe.
+ Stlačte \"Použiť polohu…\" pre pridanie poznámky k polohe.Zvukové poznámkyVytvárajte obrazové/zvukové/video poznámky počas výletu, buď tlačidlom na mape alebo v kontextovom menu polohy na mape.Audio/video poznámky
@@ -1393,7 +1393,9 @@
\n
\nTento pohľad môže byť vypnutý jeho deaktivovaním tu alebo zmenou hodnoty v \"Štýl vykresľovania\" v \"Nastaviť mapu\".Cestovný mapový pohľad
- Umiestnenie:\n Šírka %1$s\n Dĺžka %2$s
+ Poloha:
+\n Šírka %1$s
+\n Dĺžka %2$sZobraziťdní pozaduPremenovanie zlyhalo.
@@ -2632,7 +2634,7 @@
Cestovný pohľadNámornýKopírovať názov bodu/umiestnenia
- Nepomenované umiestnenie
+ Nepomenované miestoZobraziť uzavreté poznámkyZobraziť/skryť OSM poznámky na mape.GPX - vhodné na export do JOSM a iných editorov OSM.
@@ -3890,16 +3892,18 @@
\n
\n • Zlepšená viditeľnosť bodov pre bicykle.
\n
-\n • Stopy je teraz možné aktivovať, pre kontextové menu sú základnými údajmi.
+\n • Stopy je teraz možné aktivovať a majú kontextové menu so základnými údajmi.
\n
\n • Zlepšený algoritmus vyhľadávania
\n
\n • Zlepšené možnosti nasledovania stopy v navigácii
\n
-\n • Opravené problému s importom a exportom nastavení profilov
+\n • Opravené problémy s importom a exportom nastavení profilov
\n
\nNaposledy zmenenéNázov: Z – ANázov: A – Z
+ Ikony štartu/cieľa
+ Ďakujeme za zakúpenie modulu \'Vrstevnice\'
\ No newline at end of file
diff --git a/OsmAnd/res/values-tr/phrases.xml b/OsmAnd/res/values-tr/phrases.xml
index ed3292f096..16005d8a23 100644
--- a/OsmAnd/res/values-tr/phrases.xml
+++ b/OsmAnd/res/values-tr/phrases.xml
@@ -45,7 +45,7 @@
Kullanıcı tanımlıPaleontolojik alanFırın
- Likör dükkanı
+ İçki dükkanıPeynir dükkanıÇikolata dükkanıKahve dükkanı
@@ -326,7 +326,7 @@
Düşük enerji AmpüllerFloresan tüplerMetal
- Elektrik öğeleri
+ Elektrik ögeleriBeyaz eşyaYemeklik yağMotor yağı
@@ -362,7 +362,7 @@
Çocuk beziAraba aküsüArabalar
- Bisiklet
+ BisikletlerDepolamaÇöp bertarafÇöp tenekesi
@@ -766,7 +766,7 @@
Tarımsal motorlarDemirciBira fabrikası
- Boat builder
+ Gemi yapımcısıCiltçiMarangozHalı satıcısı
@@ -866,7 +866,7 @@
Gün işaretiMesafe işaretiHavuz
- Lezbiyen
+ Su setiSimgesel YapıDeniz işareti, ışıkDeniz işareti, büyük ışık
@@ -1740,8 +1740,8 @@
Açıklık genişliği0.5$ madeni paraSatıcı
- Onarım
- Onarım yok
+ Tamir
+ Tamir yokElektrikli araçların tamiriMotosiklet tamiriEvet
@@ -2043,8 +2043,8 @@
Bisiklet kiralama: hayırPompaBisiklet pompası: hayır
- Kendin-Yap onarım için araçlar
- Kendin-Yap onarım için bisiklet araçları: hayır
+ Kendin-Yap tamir için araçlar
+ Kendin-Yap tamir için bisiklet araçları: hayırTemizlemeBisiklet temizleme: hayırZincir aleti
@@ -2148,7 +2148,7 @@
KostümGelenekselTakım elbise
- Hamile
+ HamilelikNostaljiBüyük bedenOkul
@@ -2201,8 +2201,8 @@
Karavan: hayırHazırlıksız: evetHazırlıksız: hayır
- Tuvalet atığı istasyonu: evet
- Tuvalet atığı istasyonu: hayır
+ Sıhhi atık boşaltma istasyonu: evet
+ Sıhhi atık boşaltma istasyonu: hayırEvetGüç kaynağı: hayırGüç kaynağı (soket): CEE 17 mavi
@@ -2443,4 +2443,476 @@
Tarihi tren istasyonuTarihi çiftlikPa (müstahkem maori yerleşimi)
+ Futsal (salon futbolu)
+ ATM: hayır
+ ATM: evet
+ Boks
+ Lakros
+ Disk iteleme oyunu
+ Squash (duvar tenisi)
+ Uzaktan kumandalı araba yarışı
+ Disk golf
+ Judo
+ Badminton
+ Karting
+ Netbol
+ Koşma
+ Gal oyunları
+ Dojo
+ Resmi adı
+ Araç park çatısı
+ Garaj bölmeleri
+ Tür: yüzey
+ Su ısıtıcısı: hayır
+ Su ısıtıcısı: evet
+ Mikrodalga fırın: hayır
+ Mikrodalga fırın: evet
+ Bilardo
+ Yüzme: hayır
+ Yüzme: evet
+ Tekne depolama
+ Referans numarası
+ Tünel numarası
+ Köprü numarası
+ Taşıma: evet
+ Uzunluk
+ Havai fişek mağazası
+ Elektronik tamiri
+ Hackerspace
+ Fitness istasyonu
+ Bina türü: piramit
+ Palyatif tıp
+ Davranış
+ Derinlik psikolojisi
+ Naturopati
+ Kayropraktik
+ Bitkisel tıp
+ Reiki
+ Geleneksel Çin tıbbı
+ Homeopati
+ Akupunktur
+ Yetişkin psikiyatrisi
+ Podoloji
+ Spor hekimliği
+ Manuel terapi
+ Konuşma terapisi
+ Klinik patoloji
+ Optometri
+ Bağımlılık tedavisi
+ Sağlık uzmanlığı: obstetrik (sezaryen): hayır
+ Obstetrik (sezaryen)
+ Sağlık uzmanlığı: sosyal pediatri: hayır
+ Sosyal pediatri
+ Sağlık uzmanlığı: obstetrik (doğum öncesi): hayır
+ Obstetrik (doğum öncesi)
+ Sağlık uzmanlığı: obstetrik (doğum sonrası): hayır
+ Obstetrik (doğum sonrası)
+ Sağlık uzmanlığı: tropikal tıp: hayır
+ Tropikal tıp
+ Onkoloji
+ Patolojik anatomi
+ Nükleer tıp
+ Endokrinoloji
+ Nöropsikiyatri
+ Beyin ve sinir cerrahisi
+ Nefroloji (böbrek hastalıkları)
+ Diş hekimliği
+ Gastroenteroloji
+ Tıbbi görüntüleme
+ Çene-yüz cerrahisi
+ Fiziksel tıp ve rehabilitasyon
+ Çocuk psikiyatrisi
+ İş terapisi
+ Biyokimya
+ Fizyoterapi
+ Ortodonti
+ Plastik cerrahi
+ Sağlık uzmanlığı: kaza ve acil tıp: hayır
+ Kaza ve acil tıp
+ Hamilelik
+ Diş, ağız ve çene-yüz cerrahisi
+ Göğüs hastalıkları
+ Anesteziyoloji
+ Osteopati
+ Klinik biyoloji
+ Travmatoloji
+ Kardiyoloji
+ Dermato-venereoloji
+ Nöroloji
+ Psikiyatri
+ Radyoterapi
+ Radyoloji
+ Genel cerrahi
+ Üroloji
+ Dermatoloji
+ Sağlık uzmanlığı: pediatri: hayır
+ Pediatri
+ Kulak burun boğaz
+ Ortopedi
+ İç hastalıkları
+ Jinekoloji
+ Oftalmoloji
+ Pratisyen doktor
+ Ağır yük araçları
+ Bisikletler
+ Konteynerler
+ Araçlar
+ Yolcular
+ Apartman
+ Ok: hayır
+ Evet
+ Evet
+ Titreşim: hayır
+ Evet
+ Evet
+ Hayır
+ Evet
+ Göl
+ Irmak
+ Hamam
+ Konum
+ Feribot
+ Tramvay
+ Otobüs
+ Tren
+ Evet
+ Çocuk
+ Akademik
+ Din
+ Kan bağışı
+ Toptancı
+ Yalnızca
+ Evet
+ Ayakkabı tamiri
+ Kaya
+ Tekerlek
+ Araba tamiri
+ Tür: tarım
+ Posta kodu
+ Ev numarası
+ Posta kutusu
+ Park ücreti
+ Park ücreti: hayır
+ Park ücreti: evet
+ Tır: hayır
+ Tır: evet
+ Bisiklet: hayır
+ Bisiklet: evet
+ Araba: hayır
+ Araba: evet
+ Kısıtlı
+ Kapalı
+ Açık
+ Görünürlük: sokak
+ Görünürlük: ev
+ Konum: duvar
+ Konum: yerüstü
+ Konum: sualtı
+ Konum: yeraltı
+ Metal ızgara
+ Yapay çimen
+ Kil
+ Hazine
+ Kamu hizmeti
+ Bakanlık
+ Arşiv
+ Ağ
+ Yatak
+ Yön: her
+ Yön: çıkış
+ Yön: giriş
+ Yön: aşağı
+ Yön: yukarı
+ Yön: saat yönünün tersi
+ Yön: saat yönü
+ Yön: geri
+ Yön: ileri
+ Yön: kuzey-kuzeybatı
+ Yön: kuzeybatı
+ Yön: batı-kuzeybatı
+ Yön: batı
+ Yön: batı-güneybatı
+ Yön: güneybatı
+ Yön: güney-güneybatı
+ Gün: güney
+ Yön güney-güneydoğu
+ Yön: güneydoğu
+ Yön: doğu-güneydoğu
+ Yön: doğu
+ Yön: doğu-kuzeydoğu
+ Yön: kuzeydoğu
+ Yön: kuzey-kuzeydoğu
+ Yön: kuzey
+ Sürüngen
+ Kuşhane
+ Kuşlar
+ Elektronik tamir: televizyon
+ Elektronik tamir: telefon
+ Elektronik tamir: bilgisayar
+ Şarap: perakende
+ Şarap: evet
+ Evet
+ Çilingir
+ Elektrik mağazası
+ Tibet
+ Bulgar
+ Yahudi
+ Senegal
+ Mısır
+ Avustralya
+ Suriye
+ Hollanda
+ Tayvan
+ Pakistan
+ İngiliz
+ Hawaii
+ Ermeni
+ Jamaika
+ İsveç
+ Kanton
+ İsviçre
+ Bask
+ Belçika
+ Afgan
+ Ukrayna
+ Orta Doğu
+ Moğol
+ Nepal
+ Latin Amerika
+ Britanya
+ Küba
+ Çek
+ Özbek
+ Avrupa
+ Lao
+ Macar
+ Etiyopya
+ İrlanda
+ Malezya
+ Avusturya
+ Fas
+ Madagaskar
+ Fars
+ Bolivya
+ Hırvat
+ Peru
+ Balkan
+ Arjantin
+ Karayip
+ Afrika
+ Endonez
+ Dan
+ Arap
+ Brezilya
+ Leh
+ Gürcü
+ Portekiz
+ Filipin
+ Rus
+ Lübnan
+ Bavyera
+ Akdeniz
+ Kore
+ Vietnam
+ İspanyol
+ Türk
+ Uluslararası
+ Tay
+ Yunan
+ Fransız
+ Asya
+ Amerikan
+ Hint
+ Alman
+ Japon
+ Meksika
+ Çin
+ İtalyan
+ Bölgesel
+ Patates
+ Şarap
+ Çikolata
+ Kanat
+ Et
+ Yoğurt
+ Tatlı
+ Salata
+ Çorba
+ Çay
+ Makarna
+ Biftek
+ Kahvaltı
+ Mangal
+ Suşi
+ Dondurma
+ Tavuk
+ Döner
+ Kebap
+ Sandviç
+ Pizza
+ Hamburger
+ Kahve
+ Noel ağacı
+ Noel
+ Vikipedi
+ Uzunluk
+ Su deposu
+ Delegasyon
+ Başkonsolosluk
+ Konsolosluk
+ Yalnızca
+ Evet
+ Su kültürü: midye
+ Su kültürü: balık
+ Su kültürü: karides
+ Su kültürü
+ Termometre: hayır
+ Termometre
+ Barometre: hayır
+ Barometre
+ Teleskop
+ Özel
+ Askeri/kamusal
+ Askeri
+ Kamusal
+ Bölgesel
+ Uluslararası
+ Yaz kampı
+ Tuz: hayır
+ Tuz
+ Derinlik
+ Otizm: hayır
+ Otizm: evet
+ Ebola: hayır
+ Ebola: evet
+ Sıtma: hayır
+ Sıtma: evet
+ Evet
+ Acil: hayır
+ Acil: evet
+ Danışma: hayır
+ Danışma: evet
+ Kapasite (yatak)
+ Danışma (evsiz): hayır
+ Danışma (evsiz): evet
+ Danışma (aile): hayır
+ Danışma (aile): evet
+ Danışma (uyuşturucu): hayır
+ Danışma (uyuşturucu): evet
+ Danışma (bağımlılık): evet
+ Danışma (bağımlılık): evet
+ Sağlık çalışanının işi: psikolog
+ İlk yardım çantası
+ Duvar
+ Yeraltı
+ Sağlık merkezi türü: sahra hastanesi
+ Sağlık merkezi türü: laboratuvar
+ Sağlık hizmeti: aşılama: hayır
+ Sağlık hizmeti: aşılama: evet
+ Sağlık hizmeti: danışma: hayır
+ Sağlık hizmeti: danışma: evet
+ Sağlık hizmeti: hasta bakıcılık: hayır
+ Sağlık hizmeti: hasta bakıcılık: evet
+ Geleneksel Tibet
+ Geleneksel Moğol
+ Geleneksel Çin
+ Batılı
+ Solaryum
+ Çeşme
+ Elektrikli süpürtge: hayır
+ Elektrikli süpürge
+ Evet
+ Evet
+ Evet
+ Ev adı
+ Patlama zamanı (UTC)
+ Patlama tarihi (UTC)
+ Patlama türü: yeraltı
+ Patlama türü: yeraltı, tünel
+ Patlama: ülke
+ Korunan nesne: su
+ Korunan nesne: yaşam alanı
+ Korunan nesne: doğa
+ Korunan nesne: tarihi
+ Koruma alanı
+ Yapım aşamasında
+ Fast food
+ Pankek
+ Pasta
+ Izgara
+ Sosis
+ Sosisli sandviç
+ Köri
+ Krep
+ Erişte (ramen)
+ Çörek (donut)
+ Erişte (noodle)
+ Deniz ürünleri
+ Balık ve patates kızartması
+ Noel: web sitesi
+ Noel: konum
+ Noel: çalışma saatleri
+ Noel: not
+ Noel: etkinlik dönemi
+ Ağaç dükkanı
+ Noel dükkanı
+ Noel piramidi
+ Noel marketi
+ Noel etkinliği
+ Yolcu bilgilendirme ekranı: hayır
+ Yolcu bilgilendirme ekranı: evet
+ Higrometre: hayır
+ Higrometre
+ Kullanım: casusluk
+ Kullanım: araştırma
+ Kullanım: casusluk
+ Kullanım: eğitim
+ Ev ziyareti: hayır
+ Yunani
+ Sidda
+ Kampo
+ Ayurveda
+ Sıhhi atık boşaltma istasyonu
+ Yalnızca adil ticaret ürünleri
+ Adil ticaret: hayır
+ Adil ticaret: evet
+ Basınçlı hava: hayır
+ Bitki fidanlığı
+ Motorlu tekneler: hayır
+ Motorlu tekneler: evet
+ Tekne kiralama
+ Konum: giriş
+ Yön tabelası: orman tahsisi
+ Yön tabelası: orman bölmesi
+ Motosiklet giysileri: hayır
+ Motosiklet giysileri
+ Lastik: hayır
+ Lastik
+ Yedek parça: hayır
+ Yedek parça
+ Tamir: hayır
+ Tamir
+ Kiralama: hayır
+ Kiralama
+ Müzik okulu
+ Şarap: servis
+ Parti malzemeleri
+ Cajun
+ Tex-mex
+ Baget
+ Waffle
+ Krep
+ Falafel
+ Taco
+ Kantin
+ Tuzlu krep
+ Kızarmış tavuk
+ Kuskus
+ Fırın
+ Bistro
+ Kızarmış yiyecekler
+ Dondurulmuş yoğurt
+ Şarküteri
+ Turta
+ Çay dükkanı
\ No newline at end of file
diff --git a/OsmAnd/res/values-tr/strings.xml b/OsmAnd/res/values-tr/strings.xml
index 9713a69b10..50a41c8562 100644
--- a/OsmAnd/res/values-tr/strings.xml
+++ b/OsmAnd/res/values-tr/strings.xml
@@ -892,7 +892,7 @@
Güzergahı gösterYönlendirmeyi başlatınLütfen önce hedefi ayarlayın
- Açılış saatleri
+ Çalışma saatleriYetkilendirme başarısızSokaklar/binalar yükleniyor…Sokaklar yükleniyor…
@@ -1374,7 +1374,7 @@
AçılışKapanışİletişim Bilgileri
- Açılış saatleri ekle
+ Çalışma saatleri eklePOI TürüDash %1$s satır sayısıPOI türü belirtiniz.
@@ -3859,4 +3859,6 @@
Son değiştirmeİsim: Z – Aİsim: A – Z
+ Başlangıç/bitiş simgeleri
+ \'Eş yükselti eğrileri\'ni satın aldığınız için teşekkürler
\ No newline at end of file
diff --git a/OsmAnd/res/values-uk/phrases.xml b/OsmAnd/res/values-uk/phrases.xml
index 4375b43720..493e631c02 100644
--- a/OsmAnd/res/values-uk/phrases.xml
+++ b/OsmAnd/res/values-uk/phrases.xml
@@ -3832,4 +3832,5 @@
Невеликі електроприладиВуликНасіннєвий магазин
+ СПГ
\ No newline at end of file
diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml
index 905af29b00..fda15847c5 100644
--- a/OsmAnd/res/values-uk/strings.xml
+++ b/OsmAnd/res/values-uk/strings.xml
@@ -42,7 +42,7 @@
\nБудь яка з цих мап може використовуватись як основна мапа в OsmAnd, або як покриття чи підкладка до іншої основної мапи (наприклад усталена безмережева мапа OsmAnd). Для того, щоб зробити більш помітною будь-яку мапу-підкладку, певні елементи векторної мапи OsmAnd можна легко сховати через меню „Налаштування мапи“ за бажанням, щоб зробити будь-яку підкладку мапи помітнішою..
\n
\nКвадрати мап можна отримувати безпосередньо з мережевих джерел або підготувати їх для безмережевого використання (та вручну скопіювати в теку даних OsmAnd) у вигляді бази даних SQLite, яку можна створити за допомогою різноманітних сторонніх знаряддь підготовки мап.
- Показує налаштування для увімкнення фонового трекінгу та навігації шляхом періодичного пробудження GPS-передавача (з вимкненим екраном).
+ Показує налаштування для увімкнення відстеження у тлі та навігації шляхом періодичного пробудження GPS-передавача (з вимкненим екраном).Робить спеціальні можливості пристрою доступними безпосередньо в OsmAnd. Це полегшує, наприклад, налаштування частоти мовлення для голосу синтезу мовлення, настроювання D-Pad навігації, за допомогою трекбола для контролю масштабу або зворотного зв\'язку синтезу мовлення, наприклад, щоб автоматично оголосити свою позицію.Налаштування функцій розробки та налагодження, як-от навігаційне моделювання, дієвість відмальовування чи голосові підказки. Призначений для розробників, не потрібний для звичайного використання застосунку.Втулки
@@ -158,7 +158,7 @@
Файл з раніше імпортованими Закладками вже існує. Замінити його?Налаштування профілюУсталений профіль
- Вид мапи й налаштування навігації зберігаються для кожного окремого профілю. Встановіть ваш типовий профіль.
+ Вид мапи й налаштування навігації зберігаються для кожного окремого профілю. Встановіть ваш усталений профіль.НавігаціяВизначити налаштування навігації.Загальні налаштування
@@ -169,8 +169,8 @@
Ім\'я користувача OSMПотрібно для входу на openstreetmap.org.Пароль
- Фоновий режим
- OsmAnd працює у фоновому режимі з вимкненим екраном.
+ Режим тла
+ OsmAnd працює у режимі тла з вимкненим екраном.Не вистачає місця на диску для завантаження %1$s MB (вільно: %2$s).Завантажити {0} файл(ів)\?
\n{1} МБ (із {2} МБ) буде використано.
@@ -281,8 +281,8 @@
ПрозорістьЗмінити прозорість основної мапи.Прозорість мапи
- Фонова мапа…
- Фонова мапа
+ Мапа підкладки…
+ Мапа підкладкиВиберіть тлову мапуВерхній шар…Нема
@@ -428,7 +428,7 @@
Відсутні точки Закладок для збереженняІмпортуватиНе вдалося завантажити GPX.
- Відправити звіт
+ Надіслати звітНе знайдено завантаженої мапи на карті пам\'яті.Почніть вводити текст для пошуку POIВсі
@@ -456,7 +456,7 @@
Виберіть метод позиціонування, що використовується тловою службою:Джерело позиціюванняВідслідковує Ваше місцерозташування, поки екран вимкнено.
- Запустити OsmAnd у фоні
+ Запустити OsmAnd у тліСлужбі навігації у тлі необхідно, аби постачальник позиціювання був активним.Сховати фільтрПоказати фільтр
@@ -759,8 +759,9 @@
Торкніться значка блокування, щоб розблокуватиРозблокуватиЗапустити
-\n застосунок у фоновому режимі
- Вимкнути\nфоновий режим
+\n застосунок у режимі тла
+ Вимкнути
+\nрежим тлаЧас прибуттяне відміченоМапа
@@ -846,7 +847,7 @@
Безпечний режимПрограму запущено в безпечному режимі (вимкніть його в \'Налаштуваннях\').Оберіть \"Використати місцезнаходження...\" для прив\'язки нотатки до даного місцезнаходження.
- Фоновий режим OsmAnd досі запущений. Зупинити його роботу також?
+ Службу OsmAnd у тлі досі запущено. Зупинити її роботу також\?Закрити набір змінПрограма \'ZXing Barcode Scanner\' не встановлена. Шукати в Google Play?Виберіть кольорову схему доріг:
@@ -1078,9 +1079,9 @@
КолірПродовжуватиЗупинити
- Ввімкнути фоновий режим GPS
+ Увімкнути режим GPS у тліІнтервал вмикання GPS
- Зупинити фоновий режим GPS?
+ Зупинити режим GPS у тлі\?Бажана мова для підписів на мапі (якщо вона недоступна, будуть використані англійська чи місцева мови).Бажана мова мапиНазви місцевою мовою
@@ -1686,7 +1687,7 @@
м/сЗапис подорожіНавігація
- Працює у фоновому режимі
+ Працює у тліВідомості про закладкиЗупинити симуляцію Вашої позиції.Пошук адреси
@@ -2390,12 +2391,12 @@
\n• Опціональний запис подорожі в локальний GPX-файл чи онлайн-сервіс
\n• Опціональне відображення швидкості та висотного розташування
\n• Відображення горизонталей та рельєфу (через додатковий втулок)
- Безпосередній вклад у OSM
-\n• Звітуйте про помилки в даних
-\n• Вивантажуйте GPX-треки в OSM безпосередньо з програми
-\n• Додавайте POI (цікаві точки) та безпосередньо вивантажуйте їх в OSM (чи пізніше, якщо зараз Ви в офлайні)
-\n• Опція запису подорожі також і у фоновому режимі (в той час як пристрій знаходиться в сплячому режимі)
-\nOsmAnd — вільне й відкрите програмне забезпечення, що активно розвивається. Кожен може внести свій вклад, звітуючи про помилки, поліпшуючи переклад чи розробляючи нові функції. Також проєкт покладається на фінансові внески для оплати розробки та тестування нових функціональних можливостей.
+ Безпосередній вклад у OSM
+\n• Звітуйте про помилки в даних
+\n• Вивантажуйте GPX-треки в OSM безпосередньо з програми
+\n• Додавайте POI (цікаві точки) та безпосередньо вивантажуйте їх в OSM (чи пізніше, якщо зараз Ви в офлайні)
+\n• Опція запису подорожі також і в режимі тла (в той час як пристрій знаходиться в сплячому режимі)
+\nOsmAnd — вільне й відкрите програмне забезпечення, що активно розвивається. Кожен може внести свій вклад, звітуючи про помилки, поліпшуючи переклад чи розробляючи нові функції. Також проєкт покладається на фінансові внески для оплати розробки та тестування нових функціональних можливостей.
\nПриблизне охоплення мап та якість:
\n • Західна Європа: ****
@@ -3043,7 +3044,7 @@
ЛижіПоказати компас-лінійкуПриховати компас-лінійку
- Оберіть піктограму
+ Оберіть значокРежим: %sКористувацький режим, похідний від: %sЛижі
@@ -3068,31 +3069,31 @@
Натисніть ще раз для зміни орієнтації мапиМінімальна швидкістьМаксимальна швидкість
- Типова швидкість
+ Усталена швидкістьЗмінити налаштування усталеної швидкостіВстановити мінімальну/максимальну швидкістьНовий профільЗбійПід час останнього запуску OsmAnd сталася помилка. Допоможіть нам покращити OsmAnd - надішліть повідомлення про помилку.НЛО
- • Профілі застосунку: створюйте власний профіль з довільною піктограмою та кольором для ваших особистих потреб
-\n
-\n• Налаштуйте типову мінімальну/максимальну швидкості профілю
-\n
-\n• Додано віджет з поточними координатами
-\n
+ • Профілі застосунку: створюйте власний профіль з довільним значком та кольором для ваших особистих потреб
+\n
+\n• Налаштуйте усталену найменшу/найбільшу швидкості профілю
+\n
+\n• Додано віджет з поточними координатами
+\n
\n• Додано можливість показувати на мапі компас і радіус-лінійку
-\n
+\n
\n• Виправлено записування шляху у тлі
-\n
-\n• Покращено завантаження мап у тлі
+\n
+\n• Покращено завантаження мап у тлі
\n
\n• Повернено параметр \'Увімкнути екран\'
\n
-\n• Виправлено вибір мови Wikipedia
-\n
+\n• Виправлено вибір мови Wikipedia
+\n
\n• Виправлено поведінку кнопки компаса під час навігації
-\n
+\n
\n• Інші виправлення помилок
\n
\n
@@ -3430,7 +3431,7 @@
OSMЗначок відображається під час навігації чи переміщення.Значок показано в спокої.
- Перевіряти та обмінюватися докладними журналами застосунку
+ Переглянути та надіслати докладний журнал застосункуНе вдалося розібрати метод \'%s\'.Для використання цього параметра потрібен дозвіл.Це низькошвидкісний відсічний фільтр, щоб не записувати точки нижче певної швидкості. Це може призвести до плавнішого вигляду записаних треків при перегляді на мапі.
@@ -3670,7 +3671,7 @@
Профілі навігації• Нові автономні мапи схилів
\n
-\n• Налаштування вибраних та GPX шляхових точок - спеціальні кольори, піктограми, форми
+\n• Налаштування вибраних та GPX шляхових точок - спеціальні кольори, значки, форми
\n
\n• Налаштування порядку елементів меню \"Контекстне меню\", \"Налаштувати мапу\" та \"Скринька\"
\n
@@ -3694,7 +3695,7 @@
Кнопка показу або приховування громадського транспорту на мапі.Створити / змінити POIДодати / правити вибране
- Відновити типовий порядок елементів
+ Відновити усталене впорядкуванняПовернутися до редагуванняВи можете отримати доступ до цих дій, торкнувшись кнопки “%1$s”.Продовжити
@@ -3741,7 +3742,7 @@
\nОдин тиждень - 10 080 хвилин.
\nОдин місяць - 43 829 хвилин.Виберіть спосіб зберігання завантажених плиток.
- Типовий час до вимкнення екрану
+ Усталений час до вимкнення екрануВи можете експортувати або імпортувати швидкі дії з профілями застосунку.Видалити все\?Ви дійсно бажаєте безповоротно видалити %d швидких дій\?
@@ -3805,7 +3806,7 @@
Маршрут між точкамиПланування маршрутуДодати до треку
- Показувати піктограми старт та фініш
+ Показувати значки початку та завершенняВстановити ширинуВиберіть інтервал показу міток часу або відстані для показу поверх треку.Виберіть власний варіант поділу: за часом чи відстанню.
@@ -3863,7 +3864,7 @@
Змінити вид маршруту ранішеЗмінити вид маршруту післяВкажіть інтервал для загального запису поїздки (включається через віджет запису подорожі на мапі).
- Типова системи
+ Усталена системнаВсі наступні сегментиПопередній сегментУсі попередні сегменти
@@ -3876,14 +3877,14 @@
Відкрити збережений трекзбереженоДодайте принаймні дві точки.
- Запис треку буде зупинено після припинення роботи застосунку через меню з переліком нещодавно запущених застосунків. (Індикатор, який інформує про роботу OsmAnd у фоні зникне з панелі сповіщень Android)
+ Запис треку буде зупинено після припинення роботи застосунку через меню з переліком нещодавно запущених застосунків. (Індикатор, що повідомляє про роботу OsmAnd у тлі зникне з панелі сповіщень Android)Спрощений трекБуде збережено лише лінію маршруту, а проміжні точки буде видалено.Назва файлуПовторити• Оновлено функції планування маршруту: дозволено застосувати різні типи переходів для кожного сегмента і прив\'язати будь-який трек до доріг
\n
-\n• Нове меню вигляду треків: вибір кольору, товщина, вигляд стрілки напрямку, піктограми початку/завершення
+\n• Нове меню вигляду треків: вибір кольору, товщина, вигляд стрілки напрямку, значки початку/завершення
\n
\n• Покращено оглядовість велосипедних вузлів
\n
@@ -3899,4 +3900,6 @@
Востаннє зміненоЗа назвою: Я — АЗа назвою: А — Я
+ Значки початку/завершення
+ Дякуємо за придбання «Горизонталей»
\ No newline at end of file
diff --git a/OsmAnd/res/values-zh-rTW/phrases.xml b/OsmAnd/res/values-zh-rTW/phrases.xml
index 25766f928e..e36aa4414d 100644
--- a/OsmAnd/res/values-zh-rTW/phrases.xml
+++ b/OsmAnd/res/values-zh-rTW/phrases.xml
@@ -3843,4 +3843,5 @@
小電器蜂箱堅果店
+ LNG
\ No newline at end of file
diff --git a/OsmAnd/res/values-zh-rTW/strings.xml b/OsmAnd/res/values-zh-rTW/strings.xml
index b12cc12a51..cf0cc3fd99 100644
--- a/OsmAnd/res/values-zh-rTW/strings.xml
+++ b/OsmAnd/res/values-zh-rTW/strings.xml
@@ -3425,7 +3425,7 @@
OSM在導航或移動時顯示的圖示。靜止時顯示的圖示。
- 檢視及分享應用程式的紀錄檔
+ 檢查及分享應用程式的詳細紀錄無法解析地理含義「%s」。您已紀錄的軌跡位於 %1$s,或是 OsmAnd 資料夾。您的 OSM 註記位於 %1$s。
@@ -3899,4 +3899,6 @@
最後修改時間名稱:Z – A名稱:A – Z
+ 開始/結束圖示
+ 感謝您購買 \'Contour lines\'
\ No newline at end of file
diff --git a/OsmAnd/res/values/phrases.xml b/OsmAnd/res/values/phrases.xml
index 3f4394578a..b58d25a781 100644
--- a/OsmAnd/res/values/phrases.xml
+++ b/OsmAnd/res/values/phrases.xml
@@ -4257,5 +4257,7 @@
Nut store
+ LNG
+
diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml
index a8b3d34fbe..6093d106d4 100644
--- a/OsmAnd/res/values/strings.xml
+++ b/OsmAnd/res/values/strings.xml
@@ -15,6 +15,12 @@
Clear OpenStreetMap OAuth tokenLog in via OAuthPerform an OAuth Login to use osmedit features
+ Avoid footways
+ Avoid footways
+ Payment will be charged to your AppGallery account at the confirmation of purchase.\n\nSubscription automatically renews unless it is canceled before the renewal date. Your account will be charged for renewal period(month/three month/year) only on the renewal date.\n\nYou can manage and cancel your subscriptions by going to your AppGallery settings.
+ Subscription charged per selected period. Cancel it on AppGallery at any time.
+ Thank you for purchasing \'Contour lines\'
+ Start/finish iconsName: A – ZName: Z – ALast modified
diff --git a/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java
new file mode 100644
index 0000000000..c6925a5e63
--- /dev/null
+++ b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java
@@ -0,0 +1,583 @@
+package net.osmand.plus.inapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.billingclient.api.BillingClient;
+import com.android.billingclient.api.BillingResult;
+import com.android.billingclient.api.Purchase;
+import com.android.billingclient.api.SkuDetails;
+import com.android.billingclient.api.SkuDetailsResponseListener;
+
+import net.osmand.AndroidUtils;
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.OsmandPlugin;
+import net.osmand.plus.R;
+import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
+import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
+import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription;
+import net.osmand.plus.inapp.util.BillingManager;
+import net.osmand.plus.settings.backend.OsmandSettings;
+import net.osmand.plus.srtmplugin.SRTMPlugin;
+import net.osmand.util.Algorithms;
+
+import java.lang.ref.WeakReference;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
+
+ // The helper object
+ private BillingManager billingManager;
+ private List skuDetailsList;
+
+ /* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY
+ * (that you got from the Google Play developer console). This is not your
+ * developer public key, it's the *app-specific* public key.
+ *
+ * Instead of just storing the entire literal string here embedded in the
+ * program, construct the key at runtime from pieces or
+ * use bit manipulation (for example, XOR with some other string) to hide
+ * the actual key. The key itself is not secret information, but we don't
+ * want to make it easy for an attacker to replace the public key with one
+ * of their own and then fake messages from the server.
+ */
+ private static final String BASE64_ENCODED_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgk8cEx" +
+ "UO4mfEwWFLkQnX1Tkzehr4SnXLXcm2Osxs5FTJPEgyTckTh0POKVMrxeGLn0KoTY2NTgp1U/inp" +
+ "wccWisPhVPEmw9bAVvWsOkzlyg1kv03fJdnAXRBSqDDPV6X8Z3MtkPVqZkupBsxyIllEILKHK06" +
+ "OCw49JLTsMR3oTRifGzma79I71X0spw0fM+cIRlkS2tsXN8GPbdkJwHofZKPOXS51pgC1zU8uWX" +
+ "I+ftJO46a1XkNh1dO2anUiQ8P/H4yOTqnMsXF7biyYuiwjXPOcy0OMhEHi54Dq6Mr3u5ZALOAkc" +
+ "YTjh1H/ZgqIHy5ZluahINuDE76qdLYMXrDMQIDAQAB";
+
+ public InAppPurchaseHelperImpl(OsmandApplication ctx) {
+ super(ctx);
+ purchases = new InAppPurchasesImpl(ctx);
+ }
+
+ @Override
+ public void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback) {
+ if (callback != null) {
+ callback.onSuccess();
+ }
+ }
+
+ private BillingManager getBillingManager() {
+ return billingManager;
+ }
+
+ protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand runnable) {
+ billingManager = new BillingManager(ctx, BASE64_ENCODED_PUBLIC_KEY, new BillingManager.BillingUpdatesListener() {
+
+ @Override
+ public void onBillingClientSetupFinished() {
+ logDebug("Setup finished.");
+
+ BillingManager billingManager = getBillingManager();
+ // Have we been disposed of in the meantime? If so, quit.
+ if (billingManager == null) {
+ stop(true);
+ return;
+ }
+
+ if (!billingManager.isServiceConnected()) {
+ // Oh noes, there was a problem.
+ //complain("Problem setting up in-app billing: " + result);
+ notifyError(taskType, billingManager.getBillingClientResponseMessage());
+ stop(true);
+ return;
+ }
+
+ runnable.run(InAppPurchaseHelperImpl.this);
+ }
+
+ @Override
+ public void onConsumeFinished(String token, BillingResult billingResult) {
+ }
+
+ @Override
+ public void onPurchasesUpdated(final List purchases) {
+
+ BillingManager billingManager = getBillingManager();
+ // Have we been disposed of in the meantime? If so, quit.
+ if (billingManager == null) {
+ stop(true);
+ return;
+ }
+
+ if (activeTask == InAppPurchaseTaskType.REQUEST_INVENTORY) {
+ List skuInApps = new ArrayList<>();
+ for (InAppPurchase purchase : getInAppPurchases().getAllInAppPurchases(false)) {
+ skuInApps.add(purchase.getSku());
+ }
+ for (Purchase p : purchases) {
+ skuInApps.add(p.getSku());
+ }
+ billingManager.querySkuDetailsAsync(BillingClient.SkuType.INAPP, skuInApps, new SkuDetailsResponseListener() {
+ @Override
+ public void onSkuDetailsResponse(BillingResult billingResult, final List skuDetailsListInApps) {
+ // Is it a failure?
+ if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
+ logError("Failed to query inapps sku details: " + billingResult.getResponseCode());
+ notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage());
+ stop(true);
+ return;
+ }
+
+ List skuSubscriptions = new ArrayList<>();
+ for (InAppSubscription subscription : getInAppPurchases().getAllInAppSubscriptions()) {
+ skuSubscriptions.add(subscription.getSku());
+ }
+ for (Purchase p : purchases) {
+ skuSubscriptions.add(p.getSku());
+ }
+
+ BillingManager billingManager = getBillingManager();
+ // Have we been disposed of in the meantime? If so, quit.
+ if (billingManager == null) {
+ stop(true);
+ return;
+ }
+
+ billingManager.querySkuDetailsAsync(BillingClient.SkuType.SUBS, skuSubscriptions, new SkuDetailsResponseListener() {
+ @Override
+ public void onSkuDetailsResponse(BillingResult billingResult, final List skuDetailsListSubscriptions) {
+ // Is it a failure?
+ if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
+ logError("Failed to query subscriptipons sku details: " + billingResult.getResponseCode());
+ notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage());
+ stop(true);
+ return;
+ }
+
+ List skuDetailsList = new ArrayList<>(skuDetailsListInApps);
+ skuDetailsList.addAll(skuDetailsListSubscriptions);
+ InAppPurchaseHelperImpl.this.skuDetailsList = skuDetailsList;
+
+ mSkuDetailsResponseListener.onSkuDetailsResponse(billingResult, skuDetailsList);
+ }
+ });
+ }
+ });
+ }
+ for (Purchase purchase : purchases) {
+ if (!purchase.isAcknowledged()) {
+ onPurchaseFinished(purchase);
+ }
+ }
+ }
+
+ @Override
+ public void onPurchaseCanceled() {
+ stop(true);
+ }
+ });
+ }
+
+ @Override
+ public void purchaseFullVersion(@NonNull final Activity activity) {
+ notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION);
+ exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, new InAppCommand() {
+ @Override
+ public void run(InAppPurchaseHelper helper) {
+ try {
+ SkuDetails skuDetails = getSkuDetails(getFullVersion().getSku());
+ if (skuDetails == null) {
+ throw new IllegalArgumentException("Cannot find sku details");
+ }
+ BillingManager billingManager = getBillingManager();
+ if (billingManager != null) {
+ billingManager.initiatePurchaseFlow(activity, skuDetails);
+ } else {
+ throw new IllegalStateException("BillingManager disposed");
+ }
+ commandDone();
+ } catch (Exception e) {
+ complain("Cannot launch full version purchase!");
+ logError("purchaseFullVersion Error", e);
+ stop(true);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void purchaseDepthContours(@NonNull final Activity activity) {
+ notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS);
+ exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, new InAppCommand() {
+ @Override
+ public void run(InAppPurchaseHelper helper) {
+ try {
+ SkuDetails skuDetails = getSkuDetails(getDepthContours().getSku());
+ if (skuDetails == null) {
+ throw new IllegalArgumentException("Cannot find sku details");
+ }
+ BillingManager billingManager = getBillingManager();
+ if (billingManager != null) {
+ billingManager.initiatePurchaseFlow(activity, skuDetails);
+ } else {
+ throw new IllegalStateException("BillingManager disposed");
+ }
+ commandDone();
+ } catch (Exception e) {
+ complain("Cannot launch depth contours purchase!");
+ logError("purchaseDepthContours Error", e);
+ stop(true);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void purchaseContourLines(@NonNull Activity activity) throws UnsupportedOperationException {
+ OsmandPlugin plugin = OsmandPlugin.getPlugin(SRTMPlugin.class);
+ if(plugin == null || plugin.getInstallURL() == null) {
+ Toast.makeText(activity.getApplicationContext(),
+ activity.getString(R.string.activate_srtm_plugin), Toast.LENGTH_LONG).show();
+ } else {
+ activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(plugin.getInstallURL())));
+ }
+ }
+
+ @Override
+ public void manageSubscription(@NonNull Context ctx, @Nullable String sku) {
+ String url = "https://play.google.com/store/account/subscriptions?package=" + ctx.getPackageName();
+ if (!Algorithms.isEmpty(sku)) {
+ url += "&sku=" + sku;
+ }
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ ctx.startActivity(intent);
+ }
+
+ @Nullable
+ private SkuDetails getSkuDetails(@NonNull String sku) {
+ List skuDetailsList = this.skuDetailsList;
+ if (skuDetailsList != null) {
+ for (SkuDetails details : skuDetailsList) {
+ if (details.getSku().equals(sku)) {
+ return details;
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean hasDetails(@NonNull String sku) {
+ return getSkuDetails(sku) != null;
+ }
+
+ @Nullable
+ private Purchase getPurchase(@NonNull String sku) {
+ BillingManager billingManager = getBillingManager();
+ if (billingManager != null) {
+ List purchases = billingManager.getPurchases();
+ if (purchases != null) {
+ for (Purchase p : purchases) {
+ if (p.getSku().equals(sku)) {
+ return p;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ // Listener that's called when we finish querying the items and subscriptions we own
+ private SkuDetailsResponseListener mSkuDetailsResponseListener = new SkuDetailsResponseListener() {
+
+ @NonNull
+ private List getAllOwnedSubscriptionSkus() {
+ List result = new ArrayList<>();
+ BillingManager billingManager = getBillingManager();
+ if (billingManager != null) {
+ for (Purchase p : billingManager.getPurchases()) {
+ if (getInAppPurchases().getInAppSubscriptionBySku(p.getSku()) != null) {
+ result.add(p.getSku());
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void onSkuDetailsResponse(BillingResult billingResult, List skuDetailsList) {
+
+ logDebug("Query sku details finished.");
+
+ // Have we been disposed of in the meantime? If so, quit.
+ if (getBillingManager() == null) {
+ stop(true);
+ return;
+ }
+
+ // Is it a failure?
+ if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
+ logError("Failed to query inventory: " + billingResult.getResponseCode());
+ notifyError(InAppPurchaseTaskType.REQUEST_INVENTORY, billingResult.getDebugMessage());
+ stop(true);
+ return;
+ }
+
+ logDebug("Query sku details was successful.");
+
+ /*
+ * Check for items we own. Notice that for each purchase, we check
+ * the developer payload to see if it's correct! See
+ * verifyDeveloperPayload().
+ */
+
+ List allOwnedSubscriptionSkus = getAllOwnedSubscriptionSkus();
+ for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) {
+ if (hasDetails(s.getSku())) {
+ Purchase purchase = getPurchase(s.getSku());
+ SkuDetails liveUpdatesDetails = getSkuDetails(s.getSku());
+ if (liveUpdatesDetails != null) {
+ fetchInAppPurchase(s, liveUpdatesDetails, purchase);
+ }
+ allOwnedSubscriptionSkus.remove(s.getSku());
+ }
+ }
+ for (String sku : allOwnedSubscriptionSkus) {
+ Purchase purchase = getPurchase(sku);
+ SkuDetails liveUpdatesDetails = getSkuDetails(sku);
+ if (liveUpdatesDetails != null) {
+ InAppSubscription s = getLiveUpdates().upgradeSubscription(sku);
+ if (s == null) {
+ s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesDetails);
+ }
+ fetchInAppPurchase(s, liveUpdatesDetails, purchase);
+ }
+ }
+
+ InAppPurchase fullVersion = getFullVersion();
+ if (hasDetails(fullVersion.getSku())) {
+ Purchase purchase = getPurchase(fullVersion.getSku());
+ SkuDetails fullPriceDetails = getSkuDetails(fullVersion.getSku());
+ if (fullPriceDetails != null) {
+ fetchInAppPurchase(fullVersion, fullPriceDetails, purchase);
+ }
+ }
+
+ InAppPurchase depthContours = getDepthContours();
+ if (hasDetails(depthContours.getSku())) {
+ Purchase purchase = getPurchase(depthContours.getSku());
+ SkuDetails depthContoursDetails = getSkuDetails(depthContours.getSku());
+ if (depthContoursDetails != null) {
+ fetchInAppPurchase(depthContours, depthContoursDetails, purchase);
+ }
+ }
+
+ InAppPurchase contourLines = getContourLines();
+ if (hasDetails(contourLines.getSku())) {
+ Purchase purchase = getPurchase(contourLines.getSku());
+ SkuDetails contourLinesDetails = getSkuDetails(contourLines.getSku());
+ if (contourLinesDetails != null) {
+ fetchInAppPurchase(contourLines, contourLinesDetails, purchase);
+ }
+ }
+
+ Purchase fullVersionPurchase = getPurchase(fullVersion.getSku());
+ boolean fullVersionPurchased = fullVersionPurchase != null;
+ if (fullVersionPurchased) {
+ ctx.getSettings().FULL_VERSION_PURCHASED.set(true);
+ }
+
+ Purchase depthContoursPurchase = getPurchase(depthContours.getSku());
+ boolean depthContoursPurchased = depthContoursPurchase != null;
+ if (depthContoursPurchased) {
+ ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true);
+ }
+
+ // Do we have the live updates?
+ boolean subscribedToLiveUpdates = false;
+ List liveUpdatesPurchases = new ArrayList<>();
+ for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) {
+ Purchase purchase = getPurchase(p.getSku());
+ if (purchase != null) {
+ liveUpdatesPurchases.add(purchase);
+ if (!subscribedToLiveUpdates) {
+ subscribedToLiveUpdates = true;
+ }
+ }
+ }
+ OsmandSettings.OsmandPreference subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME;
+ if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) {
+ if (subscriptionCancelledTime.get() == 0) {
+ subscriptionCancelledTime.set(System.currentTimeMillis());
+ ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.set(false);
+ ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false);
+ } else if (System.currentTimeMillis() - subscriptionCancelledTime.get() > SUBSCRIPTION_HOLDING_TIME_MSEC) {
+ ctx.getSettings().LIVE_UPDATES_PURCHASED.set(false);
+ if (!isDepthContoursPurchased(ctx)) {
+ ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(false);
+ }
+ }
+ } else if (subscribedToLiveUpdates) {
+ subscriptionCancelledTime.set(0L);
+ ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
+ }
+
+ lastValidationCheckTime = System.currentTimeMillis();
+ logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE")
+ + " live updates purchased.");
+
+ OsmandSettings settings = ctx.getSettings();
+ settings.INAPPS_READ.set(true);
+
+ List tokensToSend = new ArrayList<>();
+ if (liveUpdatesPurchases.size() > 0) {
+ List tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";"));
+ for (Purchase purchase : liveUpdatesPurchases) {
+ if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get()))
+ && !Algorithms.isEmpty(purchase.getDeveloperPayload())) {
+ String payload = purchase.getDeveloperPayload();
+ if (!Algorithms.isEmpty(payload)) {
+ String[] arr = payload.split(" ");
+ if (arr.length > 0) {
+ settings.BILLING_USER_ID.set(arr[0]);
+ }
+ if (arr.length > 1) {
+ token = arr[1];
+ settings.BILLING_USER_TOKEN.set(token);
+ }
+ }
+ }
+ if (!tokensSent.contains(purchase.getSku())) {
+ tokensToSend.add(purchase);
+ }
+ }
+ }
+ List purchaseInfoList = new ArrayList<>();
+ for (Purchase purchase : tokensToSend) {
+ purchaseInfoList.add(getPurchaseInfo(purchase));
+ }
+ onSkuDetailsResponseDone(purchaseInfoList);
+ }
+ };
+
+ private PurchaseInfo getPurchaseInfo(Purchase purchase) {
+ return new PurchaseInfo(purchase.getSku(), purchase.getOrderId(), purchase.getPurchaseToken());
+ }
+
+ private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull SkuDetails skuDetails, @Nullable Purchase purchase) {
+ if (purchase != null) {
+ inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.PURCHASED);
+ inAppPurchase.setPurchaseTime(purchase.getPurchaseTime());
+ } else {
+ inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.NOT_PURCHASED);
+ }
+ inAppPurchase.setPrice(skuDetails.getPrice());
+ inAppPurchase.setPriceCurrencyCode(skuDetails.getPriceCurrencyCode());
+ if (skuDetails.getPriceAmountMicros() > 0) {
+ inAppPurchase.setPriceValue(skuDetails.getPriceAmountMicros() / 1000000d);
+ }
+ String subscriptionPeriod = skuDetails.getSubscriptionPeriod();
+ if (!Algorithms.isEmpty(subscriptionPeriod)) {
+ if (inAppPurchase instanceof InAppSubscription) {
+ try {
+ ((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod);
+ } catch (ParseException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ if (inAppPurchase instanceof InAppSubscription) {
+ String introductoryPrice = skuDetails.getIntroductoryPrice();
+ String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod();
+ String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
+ long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros();
+ if (!Algorithms.isEmpty(introductoryPrice)) {
+ InAppSubscription s = (InAppSubscription) inAppPurchase;
+ try {
+ s.setIntroductoryInfo(new InAppPurchases.InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
+ introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles));
+ } catch (ParseException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ }
+
+ protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference activity, final String sku, final String payload) {
+ return new InAppCommand() {
+ @Override
+ public void run(InAppPurchaseHelper helper) {
+ try {
+ Activity a = activity.get();
+ SkuDetails skuDetails = getSkuDetails(sku);
+ if (AndroidUtils.isActivityNotDestroyed(a) && skuDetails != null) {
+ BillingManager billingManager = getBillingManager();
+ if (billingManager != null) {
+ billingManager.setPayload(payload);
+ billingManager.initiatePurchaseFlow(a, skuDetails);
+ } else {
+ throw new IllegalStateException("BillingManager disposed");
+ }
+ commandDone();
+ } else {
+ stop(true);
+ }
+ } catch (Exception e) {
+ logError("launchPurchaseFlow Error", e);
+ stop(true);
+ }
+ }
+ };
+ }
+
+ protected InAppCommand getRequestInventoryCommand() {
+ return new InAppCommand() {
+ @Override
+ public void run(InAppPurchaseHelper helper) {
+ logDebug("Setup successful. Querying inventory.");
+ try {
+ BillingManager billingManager = getBillingManager();
+ if (billingManager != null) {
+ billingManager.queryPurchases();
+ } else {
+ throw new IllegalStateException("BillingManager disposed");
+ }
+ commandDone();
+ } catch (Exception e) {
+ logError("queryInventoryAsync Error", e);
+ notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
+ stop(true);
+ }
+ }
+ };
+ }
+
+ // Call when a purchase is finished
+ private void onPurchaseFinished(Purchase purchase) {
+ logDebug("Purchase finished: " + purchase);
+
+ // if we were disposed of in the meantime, quit.
+ if (getBillingManager() == null) {
+ stop(true);
+ return;
+ }
+
+ onPurchaseDone(getPurchaseInfo(purchase));
+ }
+
+ @Override
+ protected boolean isBillingManagerExists() {
+ return getBillingManager() != null;
+ }
+
+ @Override
+ protected void destroyBillingManager() {
+ BillingManager billingManager = getBillingManager();
+ if (billingManager != null) {
+ billingManager.destroy();
+ this.billingManager = null;
+ }
+ }
+}
diff --git a/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java
new file mode 100644
index 0000000000..a2a0f8d680
--- /dev/null
+++ b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java
@@ -0,0 +1,323 @@
+package net.osmand.plus.inapp;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.billingclient.api.SkuDetails;
+
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.R;
+import net.osmand.plus.Version;
+
+public class InAppPurchasesImpl extends InAppPurchases {
+
+ private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion();
+ private static final InAppPurchaseDepthContoursFull DEPTH_CONTOURS_FULL = new InAppPurchaseDepthContoursFull();
+ private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree();
+ private static final InAppPurchaseContourLinesFull CONTOUR_LINES_FULL = new InAppPurchaseContourLinesFull();
+ private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree();
+
+ private static final InAppSubscription[] LIVE_UPDATES_FULL = new InAppSubscription[]{
+ new InAppPurchaseLiveUpdatesOldMonthlyFull(),
+ new InAppPurchaseLiveUpdatesMonthlyFull(),
+ new InAppPurchaseLiveUpdates3MonthsFull(),
+ new InAppPurchaseLiveUpdatesAnnualFull()
+ };
+
+ private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{
+ new InAppPurchaseLiveUpdatesOldMonthlyFree(),
+ new InAppPurchaseLiveUpdatesMonthlyFree(),
+ new InAppPurchaseLiveUpdates3MonthsFree(),
+ new InAppPurchaseLiveUpdatesAnnualFree()
+ };
+
+ public InAppPurchasesImpl(OsmandApplication ctx) {
+ super(ctx);
+ fullVersion = FULL_VERSION;
+ if (Version.isFreeVersion(ctx)) {
+ liveUpdates = new LiveUpdatesInAppPurchasesFree();
+ } else {
+ liveUpdates = new LiveUpdatesInAppPurchasesFull();
+ }
+ for (InAppSubscription s : liveUpdates.getAllSubscriptions()) {
+ if (s instanceof InAppPurchaseLiveUpdatesMonthly) {
+ if (s.isDiscounted()) {
+ discountedMonthlyLiveUpdates = s;
+ } else {
+ monthlyLiveUpdates = s;
+ }
+ }
+ }
+ if (Version.isFreeVersion(ctx)) {
+ depthContours = DEPTH_CONTOURS_FREE;
+ } else {
+ depthContours = DEPTH_CONTOURS_FULL;
+ }
+ if (Version.isFreeVersion(ctx)) {
+ contourLines = CONTOUR_LINES_FREE;
+ } else {
+ contourLines = CONTOUR_LINES_FULL;
+ }
+
+ inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines };
+ }
+
+ @Override
+ public boolean isFullVersion(String sku) {
+ return FULL_VERSION.getSku().equals(sku);
+ }
+
+ @Override
+ public boolean isDepthContours(String sku) {
+ return DEPTH_CONTOURS_FULL.getSku().equals(sku) || DEPTH_CONTOURS_FREE.getSku().equals(sku);
+ }
+
+ @Override
+ public boolean isContourLines(String sku) {
+ return CONTOUR_LINES_FULL.getSku().equals(sku) || CONTOUR_LINES_FREE.getSku().equals(sku);
+ }
+
+ @Override
+ public boolean isLiveUpdates(String sku) {
+ for (InAppPurchase p : LIVE_UPDATES_FULL) {
+ if (p.getSku().equals(sku)) {
+ return true;
+ }
+ }
+ for (InAppPurchase p : LIVE_UPDATES_FREE) {
+ if (p.getSku().equals(sku)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class InAppPurchaseFullVersion extends InAppPurchase {
+
+ private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
+
+ InAppPurchaseFullVersion() {
+ super(SKU_FULL_VERSION_PRICE);
+ }
+
+ @Override
+ public String getDefaultPrice(Context ctx) {
+ return ctx.getString(R.string.full_version_price);
+ }
+ }
+
+ private static class InAppPurchaseDepthContoursFull extends InAppPurchaseDepthContours {
+
+ private static final String SKU_DEPTH_CONTOURS_FULL = "net.osmand.seadepth_plus";
+
+ InAppPurchaseDepthContoursFull() {
+ super(SKU_DEPTH_CONTOURS_FULL);
+ }
+ }
+
+ private static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours {
+
+ private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth";
+
+ InAppPurchaseDepthContoursFree() {
+ super(SKU_DEPTH_CONTOURS_FREE);
+ }
+ }
+
+ private static class InAppPurchaseContourLinesFull extends InAppPurchaseContourLines {
+
+ private static final String SKU_CONTOUR_LINES_FULL = "net.osmand.contourlines_plus";
+
+ InAppPurchaseContourLinesFull() {
+ super(SKU_CONTOUR_LINES_FULL);
+ }
+ }
+
+ private static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines {
+
+ private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines";
+
+ InAppPurchaseContourLinesFree() {
+ super(SKU_CONTOUR_LINES_FREE);
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesMonthlyFull extends InAppPurchaseLiveUpdatesMonthly {
+
+ private static final String SKU_LIVE_UPDATES_MONTHLY_FULL = "osm_live_subscription_monthly_full";
+
+ InAppPurchaseLiveUpdatesMonthlyFull() {
+ super(SKU_LIVE_UPDATES_MONTHLY_FULL, 1);
+ }
+
+ private InAppPurchaseLiveUpdatesMonthlyFull(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFull(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly {
+
+ private static final String SKU_LIVE_UPDATES_MONTHLY_FREE = "osm_live_subscription_monthly_free";
+
+ InAppPurchaseLiveUpdatesMonthlyFree() {
+ super(SKU_LIVE_UPDATES_MONTHLY_FREE, 1);
+ }
+
+ private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdates3MonthsFull extends InAppPurchaseLiveUpdates3Months {
+
+ private static final String SKU_LIVE_UPDATES_3_MONTHS_FULL = "osm_live_subscription_3_months_full";
+
+ InAppPurchaseLiveUpdates3MonthsFull() {
+ super(SKU_LIVE_UPDATES_3_MONTHS_FULL, 1);
+ }
+
+ private InAppPurchaseLiveUpdates3MonthsFull(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFull(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months {
+
+ private static final String SKU_LIVE_UPDATES_3_MONTHS_FREE = "osm_live_subscription_3_months_free";
+
+ InAppPurchaseLiveUpdates3MonthsFree() {
+ super(SKU_LIVE_UPDATES_3_MONTHS_FREE, 1);
+ }
+
+ private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesAnnualFull extends InAppPurchaseLiveUpdatesAnnual {
+
+ private static final String SKU_LIVE_UPDATES_ANNUAL_FULL = "osm_live_subscription_annual_full";
+
+ InAppPurchaseLiveUpdatesAnnualFull() {
+ super(SKU_LIVE_UPDATES_ANNUAL_FULL, 1);
+ }
+
+ private InAppPurchaseLiveUpdatesAnnualFull(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFull(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual {
+
+ private static final String SKU_LIVE_UPDATES_ANNUAL_FREE = "osm_live_subscription_annual_free";
+
+ InAppPurchaseLiveUpdatesAnnualFree() {
+ super(SKU_LIVE_UPDATES_ANNUAL_FREE, 1);
+ }
+
+ private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesOldMonthlyFull extends InAppPurchaseLiveUpdatesOldMonthly {
+
+ private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FULL = "osm_live_subscription_2";
+
+ InAppPurchaseLiveUpdatesOldMonthlyFull() {
+ super(SKU_LIVE_UPDATES_OLD_MONTHLY_FULL);
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesOldMonthlyFree extends InAppPurchaseLiveUpdatesOldMonthly {
+
+ private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FREE = "osm_free_live_subscription_2";
+
+ InAppPurchaseLiveUpdatesOldMonthlyFree() {
+ super(SKU_LIVE_UPDATES_OLD_MONTHLY_FREE);
+ }
+ }
+
+ public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription {
+
+ private SkuDetails details;
+
+ InAppPurchaseLiveUpdatesOldSubscription(@NonNull SkuDetails details) {
+ super(details.getSku(), true);
+ this.details = details;
+ }
+
+ @Override
+ public String getDefaultPrice(Context ctx) {
+ return "";
+ }
+
+ @Override
+ public CharSequence getTitle(Context ctx) {
+ return details.getTitle();
+ }
+
+ @Override
+ public CharSequence getDescription(@NonNull Context ctx) {
+ return details.getDescription();
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return null;
+ }
+ }
+
+ private static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList {
+
+ public LiveUpdatesInAppPurchasesFree() {
+ super(LIVE_UPDATES_FREE);
+ }
+ }
+
+ private static class LiveUpdatesInAppPurchasesFull extends InAppSubscriptionList {
+
+ public LiveUpdatesInAppPurchasesFull() {
+ super(LIVE_UPDATES_FULL);
+ }
+ }
+}
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java
new file mode 100755
index 0000000000..c7e6cd11be
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/CipherUtil.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.osmand.plus.inapp;
+
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * Signature related tools.
+ *
+ * @since 2019/12/9
+ */
+public class CipherUtil {
+ private static final String TAG = "CipherUtil";
+ private static final String SIGN_ALGORITHMS = "SHA256WithRSA";
+ private static final String PUBLIC_KEY = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAsB+oH8rYQncwpTqGa0kS/5E725HJrq2sW1ThAZtmorYVi52Yt9PmZvNDz7284ol9C2skrKQR34eIer8Tr7Qqq3mlNo+/LVUpq9sa++kB2glaG6jj5NNjM3w4nVYHFIYkd5AQhodJgmqFvnp2s7r7YmyQVXZSehei5bA1G70Bs+El9cSv9shNNGTCaU3ARUu2hy3Ltkc/ov7/ZYYpiwjbyD3cmoMh9jO1++zztXb2phjv1h9zeJOp1i6HsotZll+c9J4jjV3GhrF+ZJm5WrSzGLDLtwSldRpMZFxrSvAJJstjzhDz3LpUM+nPV3HZ5VQ/xosmwWYmiibo89E1gw8p73NTBXHzuQMJcTJ6vTjD8LeMskpXHZUAGhifmFLGN1LbNP9662ulCV12kIbXuzWCwwi/h0DWqmnjKmLvzc88e4BrGrp2zZUnHz7m15voPG+4cQ3z9+cwS4gEI3SUTiFyQGE539SO/11VkkQAJ8P7du1JFNqQw5ZEW3AoE1iUsp5XAgMBAAE=";
+
+ /**
+ * the method to check the signature for the data returned from the interface
+ * @param content Unsigned data
+ * @param sign the signature for content
+ * @param publicKey the public of the application
+ * @return boolean
+ */
+ public static boolean doCheck(String content, String sign, String publicKey) {
+ if (TextUtils.isEmpty(publicKey)) {
+ Log.e(TAG, "publicKey is null");
+ return false;
+ }
+
+ if (TextUtils.isEmpty(content) || TextUtils.isEmpty(sign)) {
+ Log.e(TAG, "data is error");
+ return false;
+ }
+
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ byte[] encodedKey = Base64.decode(publicKey, Base64.DEFAULT);
+ PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
+
+ java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
+
+ signature.initVerify(pubKey);
+ signature.update(content.getBytes("UTF-8"));
+
+ boolean bverify = signature.verify(Base64.decode(sign, Base64.DEFAULT));
+ return bverify;
+
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(TAG, "doCheck NoSuchAlgorithmException" + e);
+ } catch (InvalidKeySpecException e) {
+ Log.e(TAG, "doCheck InvalidKeySpecException" + e);
+ } catch (InvalidKeyException e) {
+ Log.e(TAG, "doCheck InvalidKeyException" + e);
+ } catch (SignatureException e) {
+ Log.e(TAG, "doCheck SignatureException" + e);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "doCheck UnsupportedEncodingException" + e);
+ }
+ return false;
+ }
+
+ /**
+ * get the publicKey of the application
+ * During the encoding process, avoid storing the public key in clear text.
+ * @return publickey
+ */
+ public static String getPublicKey(){
+ return PUBLIC_KEY;
+ }
+
+}
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java
new file mode 100755
index 0000000000..fba4db210a
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/Constants.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.osmand.plus.inapp;
+
+/**
+ * Constants Class.
+ *
+ * @since 2019/12/9
+ */
+public class Constants {
+
+ /** requestCode for pull up the pmsPay page */
+ public static final int REQ_CODE_BUY_SUB = 4002;
+ public static final int REQ_CODE_BUY_INAPP = 4003;
+
+ /** requestCode for pull up the login page for isEnvReady interface */
+ public static final int REQ_CODE_LOGIN = 2001;
+
+}
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java
new file mode 100755
index 0000000000..585c6b8404
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/ExceptionHandle.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.osmand.plus.inapp;
+
+import android.app.Activity;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.huawei.hms.iap.IapApiException;
+import com.huawei.hms.iap.entity.OrderStatusCode;
+
+import net.osmand.AndroidUtils;
+import net.osmand.PlatformUtil;
+
+/**
+ * Handles the exception returned from the iap api.
+ *
+ * @since 2019/12/9
+ */
+public class ExceptionHandle {
+
+ protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(ExceptionHandle.class);
+
+ /**
+ * The exception is solved.
+ */
+ public static final int SOLVED = 0;
+
+ /**
+ * Handles the exception returned from the iap api.
+ * @param activity The Activity to call the iap api.
+ * @param e The exception returned from the iap api.
+ * @return int
+ */
+ public static int handle(@Nullable Activity activity, Exception e) {
+
+ if (e instanceof IapApiException) {
+ IapApiException iapApiException = (IapApiException) e;
+ LOG.info("returnCode: " + iapApiException.getStatusCode());
+ switch (iapApiException.getStatusCode()) {
+ case OrderStatusCode.ORDER_STATE_CANCEL:
+ showToast(activity, "Order has been canceled!");
+ return SOLVED;
+ case OrderStatusCode.ORDER_STATE_PARAM_ERROR:
+ showToast(activity, "Order state param error!");
+ return SOLVED;
+ case OrderStatusCode.ORDER_STATE_NET_ERROR:
+ showToast(activity, "Order state net error!");
+ return SOLVED;
+ case OrderStatusCode.ORDER_VR_UNINSTALL_ERROR:
+ showToast(activity, "Order vr uninstall error!");
+ return SOLVED;
+ case OrderStatusCode.ORDER_HWID_NOT_LOGIN:
+ IapRequestHelper.startResolutionForResult(activity, iapApiException.getStatus(), Constants.REQ_CODE_LOGIN);
+ return SOLVED;
+ case OrderStatusCode.ORDER_PRODUCT_OWNED:
+ showToast(activity, "Product already owned error!");
+ return OrderStatusCode.ORDER_PRODUCT_OWNED;
+ case OrderStatusCode.ORDER_PRODUCT_NOT_OWNED:
+ showToast(activity, "Product not owned error!");
+ return SOLVED;
+ case OrderStatusCode.ORDER_PRODUCT_CONSUMED:
+ showToast(activity, "Product consumed error!");
+ return SOLVED;
+ case OrderStatusCode.ORDER_ACCOUNT_AREA_NOT_SUPPORTED:
+ showToast(activity, "Order account area not supported error!");
+ return SOLVED;
+ case OrderStatusCode.ORDER_NOT_ACCEPT_AGREEMENT:
+ showToast(activity, "User does not agree the agreement");
+ return SOLVED;
+ default:
+ // handle other error scenarios
+ showToast(activity, "Order unknown error (" + iapApiException.getStatusCode() + ")");
+ return SOLVED;
+ }
+ } else {
+ showToast(activity, "External error");
+ LOG.error(e.getMessage(), e);
+ return SOLVED;
+ }
+ }
+
+ private static void showToast(@Nullable Activity activity, String s) {
+ if (AndroidUtils.isActivityNotDestroyed(activity)) {
+ Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java
new file mode 100755
index 0000000000..d8fb908093
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapApiCallback.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.osmand.plus.inapp;
+
+/**
+ * Used to callback the result from iap api.
+ *
+ * @since 2019/12/9
+ */
+public interface IapApiCallback {
+
+ /**
+ * The request is successful.
+ * @param result The result of a successful response.
+ */
+ void onSuccess(T result);
+
+ /**
+ * Callback fail.
+ * @param e An Exception from IAPSDK.
+ */
+ void onFail(Exception e);
+}
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java
new file mode 100755
index 0000000000..5c1b7838a5
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/IapRequestHelper.java
@@ -0,0 +1,351 @@
+/**
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.osmand.plus.inapp;
+
+import android.app.Activity;
+import android.content.IntentSender;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.huawei.hmf.tasks.OnFailureListener;
+import com.huawei.hmf.tasks.OnSuccessListener;
+import com.huawei.hmf.tasks.Task;
+import com.huawei.hms.iap.Iap;
+import com.huawei.hms.iap.IapApiException;
+import com.huawei.hms.iap.IapClient;
+import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseReq;
+import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseResult;
+import com.huawei.hms.iap.entity.IsEnvReadyResult;
+import com.huawei.hms.iap.entity.OwnedPurchasesReq;
+import com.huawei.hms.iap.entity.OwnedPurchasesResult;
+import com.huawei.hms.iap.entity.ProductInfoReq;
+import com.huawei.hms.iap.entity.ProductInfoResult;
+import com.huawei.hms.iap.entity.PurchaseIntentReq;
+import com.huawei.hms.iap.entity.PurchaseIntentResult;
+import com.huawei.hms.iap.entity.StartIapActivityReq;
+import com.huawei.hms.iap.entity.StartIapActivityResult;
+import com.huawei.hms.support.api.client.Status;
+
+import java.util.List;
+
+/**
+ * The tool class of Iap interface.
+ *
+ * @since 2019/12/9
+ */
+public class IapRequestHelper {
+ private final static String TAG = "IapRequestHelper";
+
+ /**
+ * Create a PurchaseIntentReq object.
+ * @param type In-app product type.
+ * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
+ * @param productId ID of the in-app product to be paid.
+ * The in-app product ID is the product ID you set during in-app product configuration in AppGallery Connect.
+ * @return PurchaseIntentReq
+ */
+ private static PurchaseIntentReq createPurchaseIntentReq(int type, String productId) {
+ PurchaseIntentReq req = new PurchaseIntentReq();
+ req.setPriceType(type);
+ req.setProductId(productId);
+ req.setDeveloperPayload("testPurchase");
+ return req;
+ }
+
+ /**
+ * Create a ConsumeOwnedPurchaseReq object.
+ * @param purchaseToken which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData.
+ * The app transfers this parameter for the Huawei payment server to update the order status and then deliver the in-app product.
+ * @return ConsumeOwnedPurchaseReq
+ */
+ private static ConsumeOwnedPurchaseReq createConsumeOwnedPurchaseReq(String purchaseToken) {
+ ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
+ req.setPurchaseToken(purchaseToken);
+ req.setDeveloperChallenge("testConsume");
+ return req;
+ }
+
+ /**
+ * Create a OwnedPurchasesReq object.
+ * @param type type In-app product type.
+ * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
+ * @param continuationToken A data location flag which returns from obtainOwnedPurchases api or obtainOwnedPurchaseRecord api.
+ * @return OwnedPurchasesReq
+ */
+ private static OwnedPurchasesReq createOwnedPurchasesReq(int type, String continuationToken) {
+ OwnedPurchasesReq req = new OwnedPurchasesReq();
+ req.setPriceType(type);
+ req.setContinuationToken(continuationToken);
+ return req;
+ }
+
+ /**
+ * Create a ProductInfoReq object.
+ * @param type In-app product type.
+ * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
+ * @param productIds ID list of products to be queried. Each product ID must exist and be unique in the current app.
+ * @return ProductInfoReq
+ */
+ private static ProductInfoReq createProductInfoReq(int type, List productIds) {
+ ProductInfoReq req = new ProductInfoReq();
+ req.setPriceType(type);
+ req.setProductIds(productIds);
+ return req;
+ }
+
+ /**
+ * To check whether the country or region of the logged in HUAWEI ID is included in the countries or regions supported by HUAWEI IAP.
+ * @param mClient IapClient instance to call the isEnvReady API.
+ * @param callback IapApiCallback.
+ */
+ public static void isEnvReady(IapClient mClient, final IapApiCallback callback) {
+ Log.i(TAG, "call isEnvReady");
+ Task task = mClient.isEnvReady();
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(IsEnvReadyResult result) {
+ Log.i(TAG, "isEnvReady, success");
+ callback.onSuccess(result);
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.e(TAG, "isEnvReady, fail");
+ callback.onFail(e);
+ }
+ });
+ }
+
+ /**
+ * Obtain in-app product details configured in AppGallery Connect.
+ * @param iapClient IapClient instance to call the obtainProductInfo API.
+ * @param productIds ID list of products to be queried. Each product ID must exist and be unique in the current app.
+ * @param type In-app product type.
+ * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
+ * @param callback IapApiCallback
+ */
+ public static void obtainProductInfo(IapClient iapClient, final List productIds, int type, final IapApiCallback callback) {
+ Log.i(TAG, "call obtainProductInfo");
+
+ Task task = iapClient.obtainProductInfo(createProductInfoReq(type, productIds));
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(ProductInfoResult result) {
+ Log.i(TAG, "obtainProductInfo, success");
+ callback.onSuccess(result);
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.e(TAG, "obtainProductInfo, fail");
+ callback.onFail(e);
+ }
+ });
+ }
+
+ /**
+ * create orders for in-app products in the PMS
+ * @param iapClient IapClient instance to call the createPurchaseIntent API.
+ * @param productId ID of the in-app product to be paid.
+ * The in-app product ID is the product ID you set during in-app product configuration in AppGallery Connect.
+ * @param type In-app product type.
+ * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
+ * @param callback IapApiCallback
+ */
+ public static void createPurchaseIntent(final IapClient iapClient, String productId, int type, final IapApiCallback callback) {
+ Log.i(TAG, "call createPurchaseIntent");
+ Task task = iapClient.createPurchaseIntent(createPurchaseIntentReq(type, productId));
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(PurchaseIntentResult result) {
+ Log.i(TAG, "createPurchaseIntent, success");
+ callback.onSuccess(result);
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.e(TAG, "createPurchaseIntent, fail");
+ callback.onFail(e);
+
+ }
+ });
+ }
+
+ public static void createPurchaseIntent(final IapClient iapClient, String productId, int type, String payload, final IapApiCallback callback) {
+ Log.i(TAG, "call createPurchaseIntent");
+ PurchaseIntentReq req = createPurchaseIntentReq(type, productId);
+ req.setDeveloperPayload(payload);
+ Task task = iapClient.createPurchaseIntent(req);
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(PurchaseIntentResult result) {
+ Log.i(TAG, "createPurchaseIntent, success");
+ callback.onSuccess(result);
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.e(TAG, "createPurchaseIntent, fail");
+ callback.onFail(e);
+
+ }
+ });
+ }
+
+ /**
+ * to start an activity.
+ * @param activity the activity to launch a new page.
+ * @param status This parameter contains the pendingIntent object of the payment page.
+ * @param reqCode Result code.
+ */
+ public static void startResolutionForResult(Activity activity, Status status, int reqCode) {
+ if (status == null) {
+ Log.e(TAG, "status is null");
+ return;
+ }
+ if (status.hasResolution()) {
+ try {
+ status.startResolutionForResult(activity, reqCode);
+ } catch (IntentSender.SendIntentException exp) {
+ Log.e(TAG, exp.getMessage());
+ }
+ } else {
+ Log.e(TAG, "intent is null");
+ }
+ }
+
+ /**
+ * query information about all subscribed in-app products, including consumables, non-consumables, and auto-renewable subscriptions.
+ * If consumables are returned, the system needs to deliver them and calls the consumeOwnedPurchase API to consume the products.
+ * If non-consumables are returned, the in-app products do not need to be consumed.
+ * If subscriptions are returned, all existing subscription relationships of the user under the app are returned.
+ * @param mClient IapClient instance to call the obtainOwnedPurchases API.
+ * @param type In-app product type.
+ * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription
+ * @param callback IapApiCallback
+ */
+ public static void obtainOwnedPurchases(IapClient mClient, final int type, String continuationToken, final IapApiCallback callback) {
+ Log.i(TAG, "call obtainOwnedPurchases");
+ Task task = mClient.obtainOwnedPurchases(IapRequestHelper.createOwnedPurchasesReq(type, continuationToken));
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(OwnedPurchasesResult result) {
+ Log.i(TAG, "obtainOwnedPurchases, success");
+ callback.onSuccess(result);
+
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.e(TAG, "obtainOwnedPurchases, fail");
+ callback.onFail(e);
+ }
+ });
+
+ }
+
+ /**
+ * obtain the historical consumption information about a consumable in-app product or all subscription receipts of a subscription.
+ * @param iapClient IapClient instance to call the obtainOwnedPurchaseRecord API.
+ * @param priceType In-app product type.
+ * The value contains: 0: consumable 1: non-consumable 2 auto-renewable subscription.
+ * @param continuationToken Data locating flag for supporting query in pagination mode.
+ * @param callback IapApiCallback
+ */
+ public static void obtainOwnedPurchaseRecord(IapClient iapClient, int priceType, String continuationToken, final IapApiCallback callback) {
+ Log.i(TAG, "call obtainOwnedPurchaseRecord");
+ Task task = iapClient.obtainOwnedPurchaseRecord(createOwnedPurchasesReq(priceType, continuationToken));
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(OwnedPurchasesResult result) {
+ Log.i(TAG, "obtainOwnedPurchaseRecord, success");
+ callback.onSuccess(result);
+
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ Log.e(TAG, "obtainOwnedPurchaseRecord, fail");
+ callback.onFail(e);
+ }
+ });
+ }
+
+ /**
+ * Consume all the unconsumed purchases with priceType 0.
+ * @param iapClient IapClient instance to call the consumeOwnedPurchase API.
+ * @param purchaseToken which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData.
+ */
+ public static void consumeOwnedPurchase(IapClient iapClient, String purchaseToken) {
+ Log.i(TAG, "call consumeOwnedPurchase");
+ Task task = iapClient.consumeOwnedPurchase(createConsumeOwnedPurchaseReq(purchaseToken));
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(ConsumeOwnedPurchaseResult result) {
+ // Consume success.
+ Log.i(TAG, "consumeOwnedPurchase success");
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ if (e instanceof IapApiException) {
+ IapApiException apiException = (IapApiException)e;
+ int returnCode = apiException.getStatusCode();
+ Log.e(TAG, "consumeOwnedPurchase fail, IapApiException returnCode: " + returnCode);
+ } else {
+ // Other external errors
+ Log.e(TAG, e.getMessage());
+ }
+
+ }
+ });
+
+ }
+
+ /**
+ * link to subscription manager page
+ * @param activity activity
+ * @param productId the productId of the subscription product
+ */
+ public static void showSubscription(final Activity activity, String productId) {
+ StartIapActivityReq req = new StartIapActivityReq();
+ if (TextUtils.isEmpty(productId)) {
+ req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_MANAGER_ACTIVITY);
+ } else {
+ req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_EDIT_ACTIVITY);
+ req.setSubscribeProductId(productId);
+ }
+
+ IapClient iapClient = Iap.getIapClient(activity);
+ Task task = iapClient.startIapActivity(req);
+
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(StartIapActivityResult result) {
+ if(result != null) {
+ result.startActivity(activity);
+ }
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ ExceptionHandle.handle(activity, e);
+ }
+ });
+ }
+
+}
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java
new file mode 100644
index 0000000000..c3d6fc193c
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java
@@ -0,0 +1,703 @@
+package net.osmand.plus.inapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.huawei.hmf.tasks.OnFailureListener;
+import com.huawei.hmf.tasks.OnSuccessListener;
+import com.huawei.hmf.tasks.Task;
+import com.huawei.hms.iap.Iap;
+import com.huawei.hms.iap.IapClient;
+import com.huawei.hms.iap.entity.InAppPurchaseData;
+import com.huawei.hms.iap.entity.IsEnvReadyResult;
+import com.huawei.hms.iap.entity.OrderStatusCode;
+import com.huawei.hms.iap.entity.OwnedPurchasesResult;
+import com.huawei.hms.iap.entity.ProductInfo;
+import com.huawei.hms.iap.entity.ProductInfoResult;
+import com.huawei.hms.iap.entity.PurchaseIntentResult;
+import com.huawei.hms.iap.entity.PurchaseResultInfo;
+import com.huawei.hms.iap.entity.StartIapActivityReq;
+import com.huawei.hms.iap.entity.StartIapActivityResult;
+
+import net.osmand.AndroidUtils;
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
+import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
+import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionIntroductoryInfo;
+import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription;
+import net.osmand.plus.settings.backend.OsmandSettings;
+import net.osmand.plus.settings.backend.OsmandSettings.OsmandPreference;
+import net.osmand.util.Algorithms;
+
+import java.lang.ref.WeakReference;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
+
+ private boolean envReady = false;
+ private boolean purchaseSupported = false;
+
+ private List productInfos;
+ private OwnedPurchasesResult ownedSubscriptions;
+ private List ownedInApps = new ArrayList<>();
+
+ public InAppPurchaseHelperImpl(OsmandApplication ctx) {
+ super(ctx);
+ purchases = new InAppPurchasesImpl(ctx);
+ }
+
+ @Override
+ public void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback) {
+ if (envReady) {
+ if (callback != null) {
+ if (purchaseSupported) {
+ callback.onSuccess();
+ } else {
+ callback.onFail();
+ }
+ }
+ } else {
+ // Initiating an isEnvReady request when entering the app.
+ // Check if the account service country supports IAP.
+ IapClient mClient = Iap.getIapClient(activity);
+ final WeakReference activityRef = new WeakReference<>(activity);
+ IapRequestHelper.isEnvReady(mClient, new IapApiCallback() {
+
+ private void onReady(boolean succeed) {
+ logDebug("Setup finished.");
+ envReady = true;
+ purchaseSupported = succeed;
+ if (callback != null) {
+ if (succeed) {
+ callback.onSuccess();
+ } else {
+ callback.onFail();
+ }
+ }
+ }
+
+ @Override
+ public void onSuccess(IsEnvReadyResult result) {
+ onReady(true);
+ }
+
+ @Override
+ public void onFail(Exception e) {
+ onReady(false);
+ LOG.error("isEnvReady fail, " + e.getMessage(), e);
+ ExceptionHandle.handle(activityRef.get(), e);
+ }
+ });
+ }
+ }
+
+ protected void execImpl(@NonNull final InAppPurchaseTaskType taskType, @NonNull final InAppCommand command) {
+ if (envReady) {
+ command.run(this);
+ } else {
+ command.commandDone();
+ }
+ }
+
+ private InAppCommand getPurchaseInAppCommand(@NonNull final Activity activity, @NonNull final String productId) throws UnsupportedOperationException {
+ return new InAppCommand() {
+ @Override
+ public void run(InAppPurchaseHelper helper) {
+ try {
+ ProductInfo productInfo = getProductInfo(productId);
+ if (productInfo != null) {
+ IapRequestHelper.createPurchaseIntent(getIapClient(), productInfo.getProductId(),
+ IapClient.PriceType.IN_APP_NONCONSUMABLE, new IapApiCallback() {
+ @Override
+ public void onSuccess(PurchaseIntentResult result) {
+ if (result == null) {
+ logError("result is null");
+ } else {
+ // you should pull up the page to complete the payment process
+ IapRequestHelper.startResolutionForResult(activity, result.getStatus(), Constants.REQ_CODE_BUY_INAPP);
+ }
+ commandDone();
+ }
+
+ @Override
+ public void onFail(Exception e) {
+ int errorCode = ExceptionHandle.handle(activity, e);
+ if (errorCode != ExceptionHandle.SOLVED) {
+ logDebug("createPurchaseIntent, returnCode: " + errorCode);
+ if (OrderStatusCode.ORDER_PRODUCT_OWNED == errorCode) {
+ logError("already own this product");
+ } else {
+ logError("unknown error");
+ }
+ }
+ commandDone();
+ }
+ });
+ } else {
+ commandDone();
+ }
+ } catch (Exception e) {
+ complain("Cannot launch full version purchase!");
+ logError("purchaseFullVersion Error", e);
+ stop(true);
+ }
+ }
+ };
+ }
+
+ @Override
+ public void purchaseFullVersion(@NonNull final Activity activity) throws UnsupportedOperationException {
+ notifyShowProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION);
+ exec(InAppPurchaseTaskType.PURCHASE_FULL_VERSION, getPurchaseInAppCommand(activity, purchases.getFullVersion().getSku()));
+ }
+
+ @Override
+ public void purchaseDepthContours(@NonNull final Activity activity) throws UnsupportedOperationException {
+ notifyShowProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS);
+ exec(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS, getPurchaseInAppCommand(activity, purchases.getDepthContours().getSku()));
+ }
+
+ @Override
+ public void purchaseContourLines(@NonNull Activity activity) throws UnsupportedOperationException {
+ notifyShowProgress(InAppPurchaseTaskType.PURCHASE_CONTOUR_LINES);
+ exec(InAppPurchaseTaskType.PURCHASE_CONTOUR_LINES, getPurchaseInAppCommand(activity, purchases.getContourLines().getSku()));
+ }
+
+ @Override
+ public void manageSubscription(@NonNull Context ctx, @Nullable String sku) {
+ if (uiActivity != null) {
+ StartIapActivityReq req = new StartIapActivityReq();
+ if (!Algorithms.isEmpty(sku)) {
+ req.setSubscribeProductId(sku);
+ req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_EDIT_ACTIVITY);
+ } else {
+ req.setType(StartIapActivityReq.TYPE_SUBSCRIBE_MANAGER_ACTIVITY);
+ }
+ Task task = getIapClient().startIapActivity(req);
+ task.addOnSuccessListener(new OnSuccessListener() {
+ @Override
+ public void onSuccess(StartIapActivityResult result) {
+ logDebug("startIapActivity: onSuccess");
+ Activity activity = (Activity) uiActivity;
+ if (result != null && AndroidUtils.isActivityNotDestroyed(activity)) {
+ result.startActivity(activity);
+ }
+ }
+ }).addOnFailureListener(new OnFailureListener() {
+ @Override
+ public void onFailure(Exception e) {
+ logDebug("startIapActivity: onFailure");
+ }
+ });
+ }
+ }
+
+ @Nullable
+ private ProductInfo getProductInfo(@NonNull String productId) {
+ List productInfos = this.productInfos;
+ if (productInfos != null) {
+ for (ProductInfo info : productInfos) {
+ if (info.getProductId().equals(productId)) {
+ return info;
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean hasDetails(@NonNull String productId) {
+ return getProductInfo(productId) != null;
+ }
+
+ @Nullable
+ private InAppPurchaseData getPurchaseData(@NonNull String productId) {
+ InAppPurchaseData data = SubscriptionUtils.getPurchaseData(ownedSubscriptions, productId);
+ if (data == null) {
+ for (OwnedPurchasesResult result : ownedInApps) {
+ data = InAppUtils.getPurchaseData(result, productId);
+ if (data != null) {
+ break;
+ }
+ }
+ }
+ return data;
+ }
+
+ private PurchaseInfo getPurchaseInfo(InAppPurchaseData purchase) {
+ return new PurchaseInfo(purchase.getProductId(), purchase.getOrderID(), purchase.getPurchaseToken());
+ }
+
+ private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull ProductInfo productInfo, @Nullable InAppPurchaseData purchaseData) {
+ if (purchaseData != null) {
+ inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.PURCHASED);
+ inAppPurchase.setPurchaseTime(purchaseData.getPurchaseTime());
+ } else {
+ inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.NOT_PURCHASED);
+ }
+ inAppPurchase.setPrice(productInfo.getPrice());
+ inAppPurchase.setPriceCurrencyCode(productInfo.getCurrency());
+ if (productInfo.getMicrosPrice() > 0) {
+ inAppPurchase.setPriceValue(productInfo.getMicrosPrice() / 1000000d);
+ }
+ String subscriptionPeriod = productInfo.getSubPeriod();
+ if (!Algorithms.isEmpty(subscriptionPeriod)) {
+ if (inAppPurchase instanceof InAppSubscription) {
+ try {
+ ((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod);
+ } catch (ParseException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ if (inAppPurchase instanceof InAppSubscription) {
+ String introductoryPrice = productInfo.getSubSpecialPrice();
+ String introductoryPricePeriod = productInfo.getSubPeriod();
+ int introductoryPriceCycles = productInfo.getSubSpecialPeriodCycles();
+ long introductoryPriceAmountMicros = productInfo.getSubSpecialPriceMicros();
+ if (!Algorithms.isEmpty(introductoryPrice)) {
+ InAppSubscription s = (InAppSubscription) inAppPurchase;
+ try {
+ s.setIntroductoryInfo(new InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
+ introductoryPriceAmountMicros, introductoryPricePeriod, String.valueOf(introductoryPriceCycles)));
+ } catch (ParseException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ }
+
+ protected InAppCommand getPurchaseLiveUpdatesCommand(final WeakReference activity, final String sku, final String payload) {
+ return new InAppCommand() {
+ @Override
+ public void run(InAppPurchaseHelper helper) {
+ try {
+ Activity a = activity.get();
+ ProductInfo productInfo = getProductInfo(sku);
+ if (AndroidUtils.isActivityNotDestroyed(a) && productInfo != null) {
+ IapRequestHelper.createPurchaseIntent(getIapClient(), sku,
+ IapClient.PriceType.IN_APP_SUBSCRIPTION, payload, new IapApiCallback() {
+ @Override
+ public void onSuccess(PurchaseIntentResult result) {
+ if (result == null) {
+ logError("GetBuyIntentResult is null");
+ } else {
+ Activity a = activity.get();
+ if (AndroidUtils.isActivityNotDestroyed(a)) {
+ IapRequestHelper.startResolutionForResult(a, result.getStatus(), Constants.REQ_CODE_BUY_SUB);
+ } else {
+ logError("startResolutionForResult on destroyed activity");
+ }
+ }
+ commandDone();
+ }
+
+ @Override
+ public void onFail(Exception e) {
+ int errorCode = ExceptionHandle.handle(activity.get(), e);
+ if (ExceptionHandle.SOLVED != errorCode) {
+ logError("createPurchaseIntent, returnCode: " + errorCode);
+ if (OrderStatusCode.ORDER_PRODUCT_OWNED == errorCode) {
+ logError("already own this product");
+ } else {
+ logError("unknown error");
+ }
+ }
+ commandDone();
+ }
+ });
+ } else {
+ stop(true);
+ }
+ } catch (Exception e) {
+ logError("launchPurchaseFlow Error", e);
+ stop(true);
+ }
+ }
+ };
+ }
+
+ @Override
+ protected InAppCommand getRequestInventoryCommand() {
+ return new InAppCommand() {
+
+ @Override
+ protected void commandDone() {
+ super.commandDone();
+ inventoryRequested = false;
+ }
+
+ @Override
+ public void run(InAppPurchaseHelper helper) {
+ logDebug("Setup successful. Querying inventory.");
+ try {
+ productInfos = new ArrayList<>();
+ obtainOwnedSubscriptions();
+ } catch (Exception e) {
+ logError("queryInventoryAsync Error", e);
+ notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
+ stop(true);
+ commandDone();
+ }
+ }
+
+ private void obtainOwnedSubscriptions() {
+ if (uiActivity != null) {
+ IapRequestHelper.obtainOwnedPurchases(getIapClient(), IapClient.PriceType.IN_APP_SUBSCRIPTION,
+ null, new IapApiCallback() {
+ @Override
+ public void onSuccess(OwnedPurchasesResult result) {
+ ownedSubscriptions = result;
+ obtainOwnedInApps(null);
+ }
+
+ @Override
+ public void onFail(Exception e) {
+ logError("obtainOwnedSubscriptions exception", e);
+ ExceptionHandle.handle((Activity) uiActivity, e);
+ commandDone();
+ }
+ });
+ } else {
+ commandDone();
+ }
+ }
+
+ private void obtainOwnedInApps(final String continuationToken) {
+ if (uiActivity != null) {
+ // Query users' purchased non-consumable products.
+ IapRequestHelper.obtainOwnedPurchases(getIapClient(), IapClient.PriceType.IN_APP_NONCONSUMABLE,
+ continuationToken, new IapApiCallback() {
+ @Override
+ public void onSuccess(OwnedPurchasesResult result) {
+ ownedInApps.add(result);
+ if (result != null && !TextUtils.isEmpty(result.getContinuationToken())) {
+ obtainOwnedInApps(result.getContinuationToken());
+ } else {
+ obtainSubscriptionsInfo();
+ }
+ }
+
+ @Override
+ public void onFail(Exception e) {
+ logError("obtainOwnedInApps exception", e);
+ ExceptionHandle.handle((Activity) uiActivity, e);
+ commandDone();
+ }
+ });
+ } else {
+ commandDone();
+ }
+ }
+
+ private void obtainSubscriptionsInfo() {
+ if (uiActivity != null) {
+ Set productIds = new HashSet<>();
+ List subscriptions = purchases.getLiveUpdates().getAllSubscriptions();
+ for (InAppSubscription s : subscriptions) {
+ productIds.add(s.getSku());
+ }
+ productIds.addAll(ownedSubscriptions.getItemList());
+ IapRequestHelper.obtainProductInfo(getIapClient(), new ArrayList<>(productIds),
+ IapClient.PriceType.IN_APP_SUBSCRIPTION, new IapApiCallback() {
+ @Override
+ public void onSuccess(final ProductInfoResult result) {
+ if (result != null && result.getProductInfoList() != null) {
+ productInfos.addAll(result.getProductInfoList());
+ }
+ obtainInAppsInfo();
+ }
+
+ @Override
+ public void onFail(Exception e) {
+ int errorCode = ExceptionHandle.handle((Activity) uiActivity, e);
+ if (ExceptionHandle.SOLVED != errorCode) {
+ LOG.error("Unknown error");
+ }
+ commandDone();
+ }
+ });
+ } else {
+ commandDone();
+ }
+ }
+
+ private void obtainInAppsInfo() {
+ if (uiActivity != null) {
+ Set productIds = new HashSet<>();
+ for (InAppPurchase purchase : getInAppPurchases().getAllInAppPurchases(false)) {
+ productIds.add(purchase.getSku());
+ }
+ for (OwnedPurchasesResult result : ownedInApps) {
+ productIds.addAll(result.getItemList());
+ }
+ IapRequestHelper.obtainProductInfo(getIapClient(), new ArrayList<>(productIds),
+ IapClient.PriceType.IN_APP_NONCONSUMABLE, new IapApiCallback() {
+ @Override
+ public void onSuccess(ProductInfoResult result) {
+ if (result != null && result.getProductInfoList() != null) {
+ productInfos.addAll(result.getProductInfoList());
+ }
+ processInventory();
+ }
+
+ @Override
+ public void onFail(Exception e) {
+ int errorCode = ExceptionHandle.handle((Activity) uiActivity, e);
+ if (ExceptionHandle.SOLVED != errorCode) {
+ LOG.error("Unknown error");
+ }
+ commandDone();
+ }
+ });
+ } else {
+ commandDone();
+ }
+ }
+
+ private void processInventory() {
+ logDebug("Query sku details was successful.");
+
+ /*
+ * Check for items we own. Notice that for each purchase, we check
+ * the developer payload to see if it's correct!
+ */
+
+ List allOwnedSubscriptionSkus = ownedSubscriptions.getItemList();
+ for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) {
+ if (hasDetails(s.getSku())) {
+ InAppPurchaseData purchaseData = getPurchaseData(s.getSku());
+ ProductInfo liveUpdatesInfo = getProductInfo(s.getSku());
+ if (liveUpdatesInfo != null) {
+ fetchInAppPurchase(s, liveUpdatesInfo, purchaseData);
+ }
+ allOwnedSubscriptionSkus.remove(s.getSku());
+ }
+ }
+ for (String sku : allOwnedSubscriptionSkus) {
+ InAppPurchaseData purchaseData = getPurchaseData(sku);
+ ProductInfo liveUpdatesInfo = getProductInfo(sku);
+ if (liveUpdatesInfo != null) {
+ InAppSubscription s = getLiveUpdates().upgradeSubscription(sku);
+ if (s == null) {
+ s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesInfo);
+ }
+ fetchInAppPurchase(s, liveUpdatesInfo, purchaseData);
+ }
+ }
+
+ InAppPurchase fullVersion = getFullVersion();
+ if (hasDetails(fullVersion.getSku())) {
+ InAppPurchaseData purchaseData = getPurchaseData(fullVersion.getSku());
+ ProductInfo fullPriceDetails = getProductInfo(fullVersion.getSku());
+ if (fullPriceDetails != null) {
+ fetchInAppPurchase(fullVersion, fullPriceDetails, purchaseData);
+ }
+ }
+ InAppPurchase depthContours = getDepthContours();
+ if (hasDetails(depthContours.getSku())) {
+ InAppPurchaseData purchaseData = getPurchaseData(depthContours.getSku());
+ ProductInfo depthContoursDetails = getProductInfo(depthContours.getSku());
+ if (depthContoursDetails != null) {
+ fetchInAppPurchase(depthContours, depthContoursDetails, purchaseData);
+ }
+ }
+ InAppPurchase contourLines = getContourLines();
+ if (hasDetails(contourLines.getSku())) {
+ InAppPurchaseData purchaseData = getPurchaseData(contourLines.getSku());
+ ProductInfo contourLinesDetails = getProductInfo(contourLines.getSku());
+ if (contourLinesDetails != null) {
+ fetchInAppPurchase(contourLines, contourLinesDetails, purchaseData);
+ }
+ }
+
+ if (getPurchaseData(fullVersion.getSku()) != null) {
+ ctx.getSettings().FULL_VERSION_PURCHASED.set(true);
+ }
+ if (getPurchaseData(depthContours.getSku()) != null) {
+ ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true);
+ }
+ if (getPurchaseData(contourLines.getSku()) != null) {
+ ctx.getSettings().CONTOUR_LINES_PURCHASED.set(true);
+ }
+
+ // Do we have the live updates?
+ boolean subscribedToLiveUpdates = false;
+ List liveUpdatesPurchases = new ArrayList<>();
+ for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) {
+ InAppPurchaseData purchaseData = getPurchaseData(p.getSku());
+ if (purchaseData != null) {
+ liveUpdatesPurchases.add(purchaseData);
+ if (!subscribedToLiveUpdates) {
+ subscribedToLiveUpdates = true;
+ }
+ }
+ }
+ OsmandPreference subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME;
+ if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) {
+ if (subscriptionCancelledTime.get() == 0) {
+ subscriptionCancelledTime.set(System.currentTimeMillis());
+ ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.set(false);
+ ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false);
+ } else if (System.currentTimeMillis() - subscriptionCancelledTime.get() > SUBSCRIPTION_HOLDING_TIME_MSEC) {
+ ctx.getSettings().LIVE_UPDATES_PURCHASED.set(false);
+ if (!isDepthContoursPurchased(ctx)) {
+ ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(false);
+ }
+ }
+ } else if (subscribedToLiveUpdates) {
+ subscriptionCancelledTime.set(0L);
+ ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
+ }
+
+ lastValidationCheckTime = System.currentTimeMillis();
+ logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE")
+ + " live updates purchased.");
+
+ OsmandSettings settings = ctx.getSettings();
+ settings.INAPPS_READ.set(true);
+
+ List tokensToSend = new ArrayList<>();
+ if (liveUpdatesPurchases.size() > 0) {
+ List tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";"));
+ for (InAppPurchaseData purchase : liveUpdatesPurchases) {
+ if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get()))
+ && !Algorithms.isEmpty(purchase.getDeveloperPayload())) {
+ String payload = purchase.getDeveloperPayload();
+ if (!Algorithms.isEmpty(payload)) {
+ String[] arr = payload.split(" ");
+ if (arr.length > 0) {
+ settings.BILLING_USER_ID.set(arr[0]);
+ }
+ if (arr.length > 1) {
+ token = arr[1];
+ settings.BILLING_USER_TOKEN.set(token);
+ }
+ }
+ }
+ if (!tokensSent.contains(purchase.getProductId())) {
+ tokensToSend.add(purchase);
+ }
+ }
+ }
+ List purchaseInfoList = new ArrayList<>();
+ for (InAppPurchaseData purchase : tokensToSend) {
+ purchaseInfoList.add(getPurchaseInfo(purchase));
+ }
+ onSkuDetailsResponseDone(purchaseInfoList);
+ }
+ };
+ }
+
+ private IapClient getIapClient() {
+ return Iap.getIapClient((Activity) uiActivity);
+ }
+
+ // Call when a purchase is finished
+ private void onPurchaseFinished(InAppPurchaseData purchase) {
+ logDebug("Purchase finished: " + purchase.getProductId());
+ onPurchaseDone(getPurchaseInfo(purchase));
+ }
+
+ @Override
+ protected boolean isBillingManagerExists() {
+ return false;
+ }
+
+ @Override
+ protected void destroyBillingManager() {
+ // non implemented
+ }
+
+ @Override
+ public boolean onActivityResult(@NonNull Activity activity, int requestCode, int resultCode, Intent data) {
+ if (requestCode == Constants.REQ_CODE_BUY_SUB) {
+ boolean succeed = false;
+ if (resultCode == Activity.RESULT_OK) {
+ PurchaseResultInfo result = SubscriptionUtils.getPurchaseResult(activity, data);
+ if (result != null) {
+ switch (result.getReturnCode()) {
+ case OrderStatusCode.ORDER_STATE_CANCEL:
+ logDebug("Purchase cancelled");
+ break;
+ case OrderStatusCode.ORDER_STATE_FAILED:
+ inventoryRequestPending = true;
+ logDebug("Purchase failed");
+ break;
+ case OrderStatusCode.ORDER_PRODUCT_OWNED:
+ inventoryRequestPending = true;
+ logDebug("Product already owned");
+ break;
+ case OrderStatusCode.ORDER_STATE_SUCCESS:
+ inventoryRequestPending = true;
+ InAppPurchaseData purchaseData = SubscriptionUtils.getInAppPurchaseData(null,
+ result.getInAppPurchaseData(), result.getInAppDataSignature());
+ if (purchaseData != null) {
+ onPurchaseFinished(purchaseData);
+ succeed = true;
+ } else {
+ logDebug("Purchase failed");
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ logDebug("Purchase failed");
+ }
+ } else {
+ logDebug("Purchase cancelled");
+ }
+ if (!succeed) {
+ stop(true);
+ }
+ return true;
+ } else if (requestCode == Constants.REQ_CODE_BUY_INAPP) {
+ boolean succeed = false;
+ if (data == null) {
+ logDebug("data is null");
+ } else {
+ PurchaseResultInfo buyResultInfo = Iap.getIapClient(activity).parsePurchaseResultInfoFromIntent(data);
+ switch (buyResultInfo.getReturnCode()) {
+ case OrderStatusCode.ORDER_STATE_CANCEL:
+ logDebug("Order has been canceled");
+ break;
+ case OrderStatusCode.ORDER_STATE_FAILED:
+ inventoryRequestPending = true;
+ logDebug("Order has been failed");
+ break;
+ case OrderStatusCode.ORDER_PRODUCT_OWNED:
+ inventoryRequestPending = true;
+ logDebug("Product already owned");
+ break;
+ case OrderStatusCode.ORDER_STATE_SUCCESS:
+ InAppPurchaseData purchaseData = InAppUtils.getInAppPurchaseData(null,
+ buyResultInfo.getInAppPurchaseData(), buyResultInfo.getInAppDataSignature());
+ if (purchaseData != null) {
+ onPurchaseFinished(purchaseData);
+ succeed = true;
+ } else {
+ logDebug("Purchase failed");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (!succeed) {
+ stop(true);
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java
new file mode 100644
index 0000000000..4ed0021b6f
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java
@@ -0,0 +1,196 @@
+package net.osmand.plus.inapp;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.huawei.hms.iap.entity.ProductInfo;
+
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.R;
+
+public class InAppPurchasesImpl extends InAppPurchases {
+
+ private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion();
+ private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree();
+ private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree();
+
+
+ private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{
+ new InAppPurchaseLiveUpdatesMonthlyFree(),
+ new InAppPurchaseLiveUpdates3MonthsFree(),
+ new InAppPurchaseLiveUpdatesAnnualFree()
+ };
+
+ public InAppPurchasesImpl(OsmandApplication ctx) {
+ super(ctx);
+ fullVersion = FULL_VERSION;
+ depthContours = DEPTH_CONTOURS_FREE;
+ contourLines = CONTOUR_LINES_FREE;
+ inAppPurchases = new InAppPurchase[] { fullVersion, depthContours, contourLines };
+
+ liveUpdates = new LiveUpdatesInAppPurchasesFree();
+ for (InAppSubscription s : liveUpdates.getAllSubscriptions()) {
+ if (s instanceof InAppPurchaseLiveUpdatesMonthly) {
+ if (s.isDiscounted()) {
+ discountedMonthlyLiveUpdates = s;
+ } else {
+ monthlyLiveUpdates = s;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isFullVersion(String sku) {
+ return FULL_VERSION.getSku().equals(sku);
+ }
+
+ @Override
+ public boolean isDepthContours(String sku) {
+ return DEPTH_CONTOURS_FREE.getSku().equals(sku);
+ }
+
+ @Override
+ public boolean isContourLines(String sku) {
+ return CONTOUR_LINES_FREE.getSku().equals(sku);
+ }
+
+ @Override
+ public boolean isLiveUpdates(String sku) {
+ for (InAppPurchase p : LIVE_UPDATES_FREE) {
+ if (p.getSku().equals(sku)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class InAppPurchaseFullVersion extends InAppPurchase {
+
+ private static final String SKU_FULL_VERSION_PRICE = "net.osmand.huawei.full";
+
+ InAppPurchaseFullVersion() {
+ super(SKU_FULL_VERSION_PRICE);
+ }
+
+ @Override
+ public String getDefaultPrice(Context ctx) {
+ return ctx.getString(R.string.full_version_price);
+ }
+ }
+
+ private static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours {
+
+ private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.huawei.seadepth";
+
+ InAppPurchaseDepthContoursFree() {
+ super(SKU_DEPTH_CONTOURS_FREE);
+ }
+ }
+
+ private static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines {
+
+ private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.huawei.contourlines";
+
+ InAppPurchaseContourLinesFree() {
+ super(SKU_CONTOUR_LINES_FREE);
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly {
+
+ private static final String SKU_LIVE_UPDATES_MONTHLY_HW_FREE = "net.osmand.huawei.monthly";
+
+ InAppPurchaseLiveUpdatesMonthlyFree() {
+ super(SKU_LIVE_UPDATES_MONTHLY_HW_FREE, 1);
+ }
+
+ private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months {
+
+ private static final String SKU_LIVE_UPDATES_3_MONTHS_HW_FREE = "net.osmand.huawei.3months";
+
+ InAppPurchaseLiveUpdates3MonthsFree() {
+ super(SKU_LIVE_UPDATES_3_MONTHS_HW_FREE, 1);
+ }
+
+ private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null;
+ }
+ }
+
+ private static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual {
+
+ private static final String SKU_LIVE_UPDATES_ANNUAL_HW_FREE = "net.osmand.huawei.annual";
+
+ InAppPurchaseLiveUpdatesAnnualFree() {
+ super(SKU_LIVE_UPDATES_ANNUAL_HW_FREE, 1);
+ }
+
+ private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) {
+ super(sku);
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null;
+ }
+ }
+
+ public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription {
+
+ private ProductInfo info;
+
+ InAppPurchaseLiveUpdatesOldSubscription(@NonNull ProductInfo info) {
+ super(info.getProductId(), true);
+ this.info = info;
+ }
+
+ @Override
+ public String getDefaultPrice(Context ctx) {
+ return "";
+ }
+
+ @Override
+ public CharSequence getTitle(Context ctx) {
+ return info.getProductName();
+ }
+
+ @Override
+ public CharSequence getDescription(@NonNull Context ctx) {
+ return info.getProductDesc();
+ }
+
+ @Nullable
+ @Override
+ protected InAppSubscription newInstance(@NonNull String sku) {
+ return null;
+ }
+ }
+
+ private static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList {
+
+ public LiveUpdatesInAppPurchasesFree() {
+ super(LIVE_UPDATES_FREE);
+ }
+ }
+}
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java
new file mode 100644
index 0000000000..445727de96
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppUtils.java
@@ -0,0 +1,49 @@
+package net.osmand.plus.inapp;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.huawei.hms.iap.entity.InAppPurchaseData;
+import com.huawei.hms.iap.entity.OwnedPurchasesResult;
+
+import org.json.JSONException;
+
+public class InAppUtils {
+ private static final String TAG = "InAppUtils";
+
+ @Nullable
+ public static InAppPurchaseData getPurchaseData(OwnedPurchasesResult result, String productId) {
+ if (result == null || result.getInAppPurchaseDataList() == null) {
+ Log.i(TAG, "result is null");
+ return null;
+ }
+ int index = result.getItemList().indexOf(productId);
+ if (index != -1) {
+ String data = result.getInAppPurchaseDataList().get(index);
+ String signature = result.getInAppSignature().get(index);
+ return getInAppPurchaseData(productId, data, signature);
+ }
+ return null;
+ }
+
+ @Nullable
+ public static InAppPurchaseData getInAppPurchaseData(@Nullable String productId, @NonNull String data, @NonNull String signature) {
+ if (CipherUtil.doCheck(data, signature, CipherUtil.getPublicKey())) {
+ try {
+ InAppPurchaseData purchaseData = new InAppPurchaseData(data);
+ if (purchaseData.getPurchaseState() == InAppPurchaseData.PurchaseState.PURCHASED) {
+ if (productId == null || productId.equals(purchaseData.getProductId())) {
+ return purchaseData;
+ }
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "delivery: " + e.getMessage());
+ }
+ } else {
+ Log.e(TAG, "delivery: verify signature error");
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java
new file mode 100755
index 0000000000..3b249cb300
--- /dev/null
+++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/SubscriptionUtils.java
@@ -0,0 +1,138 @@
+/**
+ * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *