diff --git a/OsmAnd/AndroidManifest.xml b/OsmAnd/AndroidManifest.xml index 5cc90e170d..7ca98ddfc8 100644 --- a/OsmAnd/AndroidManifest.xml +++ b/OsmAnd/AndroidManifest.xml @@ -1026,6 +1026,7 @@ + diff --git a/OsmAnd/res/layout/test_backup_layout.xml b/OsmAnd/res/layout/test_backup_layout.xml new file mode 100644 index 0000000000..216820a9bf --- /dev/null +++ b/OsmAnd/res/layout/test_backup_layout.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml index 4cb0bab704..307f2513cc 100644 --- a/OsmAnd/res/values-ar/strings.xml +++ b/OsmAnd/res/values-ar/strings.xml @@ -4154,5 +4154,4 @@ الإعلان عند التجاوز نقاط المستخدم الإخراج - %1$s ← … \ No newline at end of file diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml index 094ba47620..dae511a7f4 100644 --- a/OsmAnd/res/values-de/strings.xml +++ b/OsmAnd/res/values-de/strings.xml @@ -4089,5 +4089,4 @@ Meldung bei Überschreitung Anwenderpunkte Leistung - %1$s → … \ No newline at end of file diff --git a/OsmAnd/res/values-eo/strings.xml b/OsmAnd/res/values-eo/strings.xml index 5b9d9345b5..19328a93e7 100644 --- a/OsmAnd/res/values-eo/strings.xml +++ b/OsmAnd/res/values-eo/strings.xml @@ -4085,5 +4085,4 @@ Numero de elirejo Poentoj de uzanto Eligo - %1$s → … \ 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 bcf6918377..c7a7007a59 100644 --- a/OsmAnd/res/values-es-rAR/strings.xml +++ b/OsmAnd/res/values-es-rAR/strings.xml @@ -4087,5 +4087,4 @@ Anunciar al excederse Puntos de usuario Salida - %1$s → … \ No newline at end of file diff --git a/OsmAnd/res/values-fr/strings.xml b/OsmAnd/res/values-fr/strings.xml index b45c597a1a..8fa7588019 100644 --- a/OsmAnd/res/values-fr/strings.xml +++ b/OsmAnd/res/values-fr/strings.xml @@ -4075,5 +4075,4 @@ Numéro de sortie Annoncer en cas de dépassement Sortie - %1$s → … \ No newline at end of file diff --git a/OsmAnd/res/values-hu/strings.xml b/OsmAnd/res/values-hu/strings.xml index 9709e8bce8..ced108288e 100644 --- a/OsmAnd/res/values-hu/strings.xml +++ b/OsmAnd/res/values-hu/strings.xml @@ -4074,7 +4074,6 @@ Várakoztatva Lejárt Frissüljön minden térkép, amely hozzá lett adva ehhez: %1$s\? - %1$s → … Kijárat száma Értesítés túllépéskor Felhasználói pontok diff --git a/OsmAnd/res/values-iw/strings.xml b/OsmAnd/res/values-iw/strings.xml index e7684a1670..8fee045082 100644 --- a/OsmAnd/res/values-iw/strings.xml +++ b/OsmAnd/res/values-iw/strings.xml @@ -4087,5 +4087,4 @@ להכריז בחריגה נקודות משתמש פלט - %1$s ← … \ No newline at end of file diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index d805e31c37..8d3a99bb75 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -4089,5 +4089,4 @@ Anunciar quando ultrapassado Pontos do utilizador Saída - %1$s → … \ No newline at end of file diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml index 3ad683a6fb..4fe37da2e3 100644 --- a/OsmAnd/res/values-uk/strings.xml +++ b/OsmAnd/res/values-uk/strings.xml @@ -4084,5 +4084,4 @@ Повідомляти про перевищення Користувацькі точки Вивід - %1$s → … \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 97cfcf3a42..9959488bd2 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -12,7 +12,6 @@ --> - %1$s → … Output User points Announce when exceeded diff --git a/OsmAnd/res/xml/development_settings.xml b/OsmAnd/res/xml/development_settings.xml index 231bd469bc..75079dd7ed 100644 --- a/OsmAnd/res/xml/development_settings.xml +++ b/OsmAnd/res/xml/development_settings.xml @@ -70,6 +70,13 @@ android:summary="@string/play_commands_of_currently_selected_voice" android:title="@string/test_voice_prompts" /> + + getAdditionalParams(@NonNull File file); + void onFileUploadProgress(@NonNull File file, int percent); + void onFilesUploadDone(@NonNull Map errors); + } + public static class RequestResponse { private Request request; private String response; @@ -156,7 +166,7 @@ public class AndroidNetworkUtils { String params = null; if (parameters != null && parameters.size() > 0) { StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : parameters.entrySet()) { + for (Entry entry : parameters.entrySet()) { if (sb.length() > 0) { sb.append("&"); } @@ -296,16 +306,18 @@ public class AndroidNetworkUtils { private static final String BOUNDARY = "CowMooCowMooCowCowCow"; - public static String uploadFile(String urlText, File file, boolean gzip, Map additionalParams) throws IOException { - return uploadFile(urlText, new FileInputStream(file), file.getName(), gzip, additionalParams); + public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip, + @NonNull Map additionalParams, @Nullable Map headers) throws IOException { + return uploadFile(urlText, new FileInputStream(file), file.getName(), gzip, additionalParams, headers); } - public static String uploadFile(String urlText, InputStream inputStream, String fileName, boolean gzip, Map additionalParams) { + public static String uploadFile(@NonNull String urlText, @NonNull InputStream inputStream, @NonNull String fileName, boolean gzip, + Map additionalParams, @Nullable Map headers) { URL url; try { boolean firstPrm = !urlText.contains("?"); StringBuilder sb = new StringBuilder(urlText); - for (Map.Entry entry : additionalParams.entrySet()) { + for (Entry entry : additionalParams.entrySet()) { sb.append(firstPrm ? "?" : "&").append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "UTF-8")); firstPrm = false; } @@ -320,6 +332,11 @@ public class AndroidNetworkUtils { conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); conn.setRequestProperty("User-Agent", "OsmAnd"); + if (headers != null) { + for (Entry header : headers.entrySet()) { + conn.setRequestProperty(header.getKey(), header.getValue()); + } + } OutputStream ous = conn.getOutputStream(); ous.write(("--" + BOUNDARY + "\r\n").getBytes()); @@ -376,6 +393,58 @@ public class AndroidNetworkUtils { } } + public static void uploadFilesAsync(final @NonNull String url, + final @NonNull List files, + final boolean gzip, + final @NonNull Map parameters, + final @Nullable Map headers, + final OnFilesUploadCallback callback) { + + new AsyncTask>() { + + @Override + @NonNull + protected Map doInBackground(Void... v) { + Map errors = new HashMap<>(); + for (File file : files) { + publishProgress(file, 0); + try { + Map params = new HashMap<>(parameters); + if (callback != null) { + Map additionalParams = callback.getAdditionalParams(file); + if (additionalParams != null) { + params.putAll(additionalParams); + } + } + String res = uploadFile(url, file, gzip, params, headers); + if (res != null) { + errors.put(file, res); + } + } catch (Exception e) { + errors.put(file, e.getMessage()); + } + publishProgress(file, 100); + } + return errors; + } + + @Override + protected void onProgressUpdate(Object... objects) { + if (callback != null) { + callback.onFileUploadProgress((File) objects[0], (Integer) objects[1]); + } + } + + @Override + protected void onPostExecute(@NonNull Map errors) { + if (callback != null) { + callback.onFilesUploadDone(errors); + } + } + + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + } + private static void showToast(OsmandApplication ctx, String message) { ctx.showToastMessage(message); } diff --git a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java index 2fd12a11ac..e8d04254c3 100644 --- a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java +++ b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java @@ -184,7 +184,7 @@ public class AnalyticsHelper extends SQLiteOpenHelper { String jsonStr = json.toString(); InputStream inputStream = new ByteArrayInputStream(jsonStr.getBytes()); - String res = AndroidNetworkUtils.uploadFile(ANALYTICS_UPLOAD_URL, inputStream, ANALYTICS_FILE_NAME, true, additionalData); + String res = AndroidNetworkUtils.uploadFile(ANALYTICS_UPLOAD_URL, inputStream, ANALYTICS_FILE_NAME, true, additionalData, null); if (res != null) { return; } diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java index 96ce49d8db..727e7d683b 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java @@ -169,6 +169,11 @@ public class AudioNotesLayer extends OsmandMapLayer implements return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List objects, boolean unknownLocation) { if (tileBox.getZoom() >= startZoom) { diff --git a/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java b/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java index d34a306b79..9ffe2d12bf 100644 --- a/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/development/DevelopmentSettingsFragment.java @@ -48,6 +48,7 @@ public class DevelopmentSettingsFragment extends BaseSettingsFragment { setupSimulateInitialStartupPref(); setupShouldShowFreeVersionBannerPref(); setupTestVoiceCommandsPref(); + setupTestBackupsPref(); setupLogcatBufferPref(); Preference info = findPreference("info"); @@ -117,6 +118,12 @@ public class DevelopmentSettingsFragment extends BaseSettingsFragment { testVoiceCommands.setIconSpaceReserved(false); } + private void setupTestBackupsPref() { + Preference testBackups = findPreference("test_backup"); + testBackups.setIntent(new Intent(getActivity(), TestBackupActivity.class)); + testBackups.setIconSpaceReserved(false); + } + private void setupLogcatBufferPref() { Preference logcatBuffer = findPreference("logcat_buffer"); logcatBuffer.setIntent(new Intent(getActivity(), LogcatActivity.class)); diff --git a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java new file mode 100644 index 0000000000..0f1d1775bd --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java @@ -0,0 +1,595 @@ +package net.osmand.plus.development; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Patterns; +import android.util.TypedValue; +import android.view.View; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; + +import net.osmand.AndroidNetworkUtils; +import net.osmand.AndroidNetworkUtils.OnFilesUploadCallback; +import net.osmand.AndroidNetworkUtils.OnRequestResultListener; +import net.osmand.AndroidUtils; +import net.osmand.IndexConstants; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.ProgressImplementation; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.UiUtilities.DialogButtonType; +import net.osmand.plus.activities.OsmandActionBarActivity; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TestBackupActivity extends OsmandActionBarActivity { + + // TODO pass actual sub order id! + private static final String TEST_ORDER_ID = ""; + + private OsmandApplication app; + private OsmandSettings settings; + + private ProgressBar progressBar; + private View buttonRegister; + private View buttonVerify; + private View buttonBackup; + private View buttonRestore; + private EditText emailEditText; + private EditText tokenEditText; + private TextView infoView; + + public interface OnResultListener { + void onResult(boolean success, @Nullable String result); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + app = getMyApplication(); + settings = app.getSettings(); + final WeakReference activityRef = new WeakReference<>(this); + + boolean nightMode = !app.getSettings().isLightContent(); + int themeId = nightMode ? R.style.OsmandDarkTheme_NoActionbar : R.style.OsmandLightTheme_NoActionbar; + setTheme(themeId); + super.onCreate(savedInstanceState); + setContentView(R.layout.test_backup_layout); + Toolbar tb = findViewById(R.id.toolbar); + tb.setTitle("Backup test"); + + tb.setClickable(true); + Drawable icBack = ((OsmandApplication) getApplication()).getUIUtilities().getIcon(AndroidUtils.getNavigationIconResId(app)); + tb.setNavigationIcon(icBack); + tb.setNavigationContentDescription(R.string.access_shared_string_navigate_up); + tb.setBackgroundColor(getResources().getColor(resolveResourceId(this, R.attr.pstsTabBackground))); + tb.setTitleTextColor(getResources().getColor(resolveResourceId(this, R.attr.pstsTextColor))); + tb.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View v) { + finish(); + } + }); + + buttonRegister = findViewById(R.id.btn_register); + UiUtilities.setupDialogButton(nightMode, buttonRegister, DialogButtonType.PRIMARY, "Register"); + buttonVerify = findViewById(R.id.btn_verify); + UiUtilities.setupDialogButton(nightMode, buttonVerify, DialogButtonType.PRIMARY, "Verify"); + buttonBackup = findViewById(R.id.btn_backup); + UiUtilities.setupDialogButton(nightMode, buttonBackup, DialogButtonType.PRIMARY, "Backup"); + buttonRestore = findViewById(R.id.btn_restore); + UiUtilities.setupDialogButton(nightMode, buttonRestore, DialogButtonType.PRIMARY, "Restore"); + + tokenEditText = findViewById(R.id.edit_token); + infoView = findViewById(R.id.text_info); + progressBar = findViewById(R.id.progress_bar); + + buttonVerify.setEnabled(false); + emailEditText = findViewById(R.id.edit_email); + String email = settings.BACKUP_USER_EMAIL.get(); + if (!Algorithms.isEmpty(email)) { + emailEditText.setText(email); + } + buttonRegister.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String email = emailEditText.getText().toString(); + if (isEmailValid(email)) { + buttonRegister.setEnabled(false); + settings.BACKUP_USER_EMAIL.set(email); + progressBar.setVisibility(View.VISIBLE); + registerUser(email, new OnResultListener() { + @Override + public void onResult(boolean success, @Nullable String result) { + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + a.progressBar.setVisibility(View.GONE); + a.buttonRegister.setEnabled(!success); + a.buttonVerify.setEnabled(success); + a.tokenEditText.requestFocus(); + } + } + }); + } else { + emailEditText.requestFocus(); + emailEditText.setError(getString(R.string.osm_live_enter_email)); + } + } + }); + buttonVerify.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String token = tokenEditText.getText().toString(); + if (isTokenValid(token)) { + buttonVerify.setEnabled(false); + progressBar.setVisibility(View.VISIBLE); + registerDevice(token, new OnResultListener() { + @Override + public void onResult(boolean success, @Nullable String result) { + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + a.progressBar.setVisibility(View.GONE); + a.buttonVerify.setEnabled(!success); + a.loadBackupInfo(); + } + } + }); + } else { + tokenEditText.requestFocus(); + tokenEditText.setError("Token is not valid"); + buttonVerify.setEnabled(true); + } + } + }); + buttonBackup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + uploadFiles(); + } + }); + buttonRestore.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + } + }); + + loadBackupInfo(); + } + + private void loadBackupInfo() { + if (!Algorithms.isEmpty(getDeviceId()) && !Algorithms.isEmpty(getAccessToken())) { + final WeakReference activityRef = new WeakReference<>(this); + progressBar.setVisibility(View.VISIBLE); + loadBackupInfo(new OnResultListener() { + @Override + public void onResult(boolean success, @Nullable String result) { + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + a.progressBar.setVisibility(View.GONE); + a.infoView.setText(result); + a.infoView.requestFocus(); + } + } + }); + } + } + + private boolean isEmailValid(CharSequence target) { + return (!TextUtils.isEmpty(target) && Patterns.EMAIL_ADDRESS.matcher(target).matches()); + } + + private String getOrderId() { + return TEST_ORDER_ID; + } + + private String getDeviceId() { + return settings.BACKUP_DEVICE_ID.get(); + } + + private String getAccessToken() { + return settings.BACKUP_ACCESS_TOKEN.get(); + } + + private void registerUser(@NonNull String email, @Nullable final OnResultListener listener) { + Map params = new HashMap<>(); + params.put("email", email); + params.put("orderid", getOrderId()); + params.put("deviceid", app.getUserAndroidId()); + AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/user-register", params, "Register user", true, true, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + boolean success = false; + if (!Algorithms.isEmpty(resultJson)) { + try { + // {"status":"ok"} + JSONObject result = new JSONObject(resultJson); + String status = result.getString("status"); + success = status.equals("ok"); + app.showToastMessage(success + ? "You have been registered successfully. Please check for email with activation code." + : "User registration error: " + status); + } catch (JSONException e) { + app.showToastMessage("User registration error: json parsing"); + } + } else { + app.showToastMessage("User registration error: empty response"); + } + if (listener != null) { + listener.onResult(success, resultJson); + } + } + }); + } + + private void registerDevice(String token, @Nullable final OnResultListener listener) { + Map params = new HashMap<>(); + params.put("email", settings.BACKUP_USER_EMAIL.get()); + params.put("orderid", getOrderId()); + params.put("deviceid", app.getUserAndroidId()); + params.put("token", token); + AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/device-register", params, "Register device", true, true, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + boolean success = false; + if (!Algorithms.isEmpty(resultJson)) { + try { + /* + { + "id": 1034, + "userid": 1033, + "deviceid": "2fa8080d2985a777", + "orderid": "460000687003939", + "accesstoken": "4bc0a61f-397a-4c3e-9ffc-db382ec00372", + "udpatetime": "Apr 11, 2021, 11:32:20 AM" + } + */ + JSONObject result = new JSONObject(resultJson); + settings.BACKUP_DEVICE_ID.set(result.getString("id")); + settings.BACKUP_USER_ID.set(result.getString("userid")); + settings.BACKUP_NATIVE_DEVICE_ID.set(result.getString("deviceid")); + settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken")); + settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime")); + success = true; + app.showToastMessage("Device have been registered successfully"); + } catch (JSONException e) { + app.showToastMessage("Device registration error: json parsing"); + } + } else { + app.showToastMessage("Device registration error: empty response"); + } + if (listener != null) { + listener.onResult(success, resultJson); + } + } + }); + } + + private void uploadFiles() { + LoadGpxTask loadGpxTask = new LoadGpxTask(this, new LoadGpxTask.OnLoadGpxListener() { + @Override + public void onLoadGpxDone(@NonNull List result) { + uploadFiles(result); + } + }); + loadGpxTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this); + } + + private void uploadFiles(List gpxFiles) { + //{"status":"ok"} + final WeakReference activityRef = new WeakReference<>(this); + + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + Map headers = new HashMap<>(); + headers.put("Accept-Encoding", "deflate, gzip"); + + final Map gpxInfos = new HashMap<>(); + for (GpxInfo gpxFile : gpxFiles) { + gpxInfos.put(gpxFile.file, gpxFile); + } + final List files = new ArrayList<>(gpxInfos.keySet()); + File favoritesFile = app.getFavorites().getExternalFile(); + files.add(favoritesFile); + + final ProgressImplementation progress = ProgressImplementation.createProgressDialog(this, + "Create backup", "Uploading " + files.size() + " file(s) to server", ProgressDialog.STYLE_HORIZONTAL); + + AndroidNetworkUtils.uploadFilesAsync("https://osmand.net/userdata/upload-file", files, true, params, headers, new OnFilesUploadCallback() { + @Nullable + @Override + public Map getAdditionalParams(@NonNull File file) { + GpxInfo gpxInfo = gpxInfos.get(file); + Map additionaParams = new HashMap<>(); + additionaParams.put("name", gpxInfo == null ? file.getName() : gpxInfo.getFileName(true)); + additionaParams.put("type", Algorithms.getFileExtension(file)); + return additionaParams; + } + + @Override + public void onFileUploadProgress(@NonNull File file, int percent) { + Activity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + if (percent < 100) { + progress.startTask(file.getName(), percent); + } else { + progress.finishTask(); + } + } + } + + @Override + public void onFilesUploadDone(@NonNull Map errors) { + Activity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + }, 300); + app.showToastMessage("Uploaded " + (files.size() - errors.size() + " files" + + (errors.size() > 0 ? ". Errors: " + errors.size() : ""))); + loadBackupInfo(); + } + } + }); + } + + private void loadBackupInfo(@Nullable final OnResultListener listener) { + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/list-files", params, "Get backup info", true, false, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + boolean success = false; + StringBuilder resultString = new StringBuilder(); + if (!Algorithms.isEmpty(resultJson)) { + try { + /* + { + "totalZipSize": 21792, + "totalFileSize": 185920, + "totalFiles": 1, + "totalFileVersions": 2, + "uniqueFiles": [ + { + "userid": 1033, + "id": 7, + "deviceid": 1034, + "filesize": 92960, + "type": "gpx", + "name": "test/Day 2.gpx", + "updatetime": "Apr 11, 2021, 1:49:01 PM", + "updatetimems": 1618141741822, + "zipSize": 10896 + } + ], + "deviceid": 1034 + } + */ + JSONObject result = new JSONObject(resultJson); + String totalZipSize = result.getString("totalZipSize"); + String totalFiles = result.getString("totalFiles"); + String totalFileVersions = result.getString("totalFileVersions"); + JSONArray files = result.getJSONArray("uniqueFiles"); + resultString.append("Total files: ").append(totalFiles).append("\n"); + resultString.append("Total zip size: ").append(AndroidUtils.formatSize(app, Long.parseLong(totalZipSize))).append("\n"); + resultString.append("Total file versions: ").append(totalFileVersions); + + success = true; + } catch (JSONException e) { + } + } + if (listener != null) { + listener.onResult(success, resultString.toString()); + } + } + }); + } + + private boolean isTokenValid(String token) { + return token.matches("[0-9]+"); + } + + private int resolveResourceId(final Activity activity, final int attr) { + final TypedValue typedvalueattr = new TypedValue(); + activity.getTheme().resolveAttribute(attr, typedvalueattr, true); + return typedvalueattr.resourceId; + } + + private static class LoadGpxTask extends AsyncTask> { + + private final OsmandApplication app; + private final OnLoadGpxListener listener; + private final WeakReference activityRef; + private List result; + private ProgressImplementation progress; + + interface OnLoadGpxListener { + void onLoadGpxDone(@NonNull List result); + } + + LoadGpxTask(@NonNull Activity activity, @Nullable OnLoadGpxListener listener) { + this.activityRef = new WeakReference<>(activity); + this.app = (OsmandApplication) activity.getApplication(); + this.listener = listener; + } + + public List getResult() { + return result; + } + + @NonNull + @Override + protected List doInBackground(Activity... params) { + List result = new ArrayList<>(); + loadGPXData(app.getAppPath(IndexConstants.GPX_INDEX_DIR), result, this); + return result; + } + + public void loadFile(GpxInfo... loaded) { + publishProgress(loaded); + } + + @Override + protected void onPreExecute() { + Activity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + progress = ProgressImplementation.createProgressDialog(a, + "Create backup", "Collecting gpx files...", ProgressDialog.STYLE_HORIZONTAL); + } + } + + @Override + protected void onProgressUpdate(GpxInfo... values) { + Activity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + progress.startTask(values[0].getFileName(true), -1); + } + } + + @Override + protected void onPostExecute(@NonNull List result) { + this.result = result; + if (listener != null) { + listener.onLoadGpxDone(result); + } + Activity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + progress.finishTask(); + app.runInUIThread(new Runnable() { + @Override + public void run() { + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + }, 300); + } + } + + private void loadGPXData(File mapPath, List result, LoadGpxTask loadTask) { + if (mapPath.canRead()) { + List progress = new ArrayList<>(); + loadGPXFolder(mapPath, result, loadTask, progress, ""); + if (!progress.isEmpty()) { + loadTask.loadFile(progress.toArray(new GpxInfo[0])); + } + } + } + + private void loadGPXFolder(File mapPath, List result, LoadGpxTask loadTask, List progress, + String gpxSubfolder) { + File[] listFiles = mapPath.listFiles(); + if (listFiles != null) { + for (File gpxFile : listFiles) { + if (gpxFile.isDirectory()) { + String sub = gpxSubfolder.length() == 0 ? gpxFile.getName() : gpxSubfolder + "/" + + gpxFile.getName(); + loadGPXFolder(gpxFile, result, loadTask, progress, sub); + } else if (gpxFile.isFile() && gpxFile.getName().toLowerCase().endsWith(IndexConstants.GPX_FILE_EXT)) { + GpxInfo info = new GpxInfo(); + info.subfolder = gpxSubfolder; + info.file = gpxFile; + result.add(info); + progress.add(info); + if (progress.size() > 7) { + loadTask.loadFile(progress.toArray(new GpxInfo[0])); + progress.clear(); + } + } + } + } + } + } + + private static class GpxInfo { + public File file; + public String subfolder; + + private String name = null; + private int sz = -1; + private String fileName = null; + + public String getName() { + if (name == null) { + name = formatName(file.getName()); + } + return name; + } + + private String formatName(String name) { + int ext = name.lastIndexOf('.'); + if (ext != -1) { + name = name.substring(0, ext); + } + return name.replace('_', ' '); + } + + // Usage: AndroidUtils.formatSize(v.getContext(), getSize() * 1024l); + public int getSize() { + if (sz == -1) { + if (file == null) { + return -1; + } + sz = (int) ((file.length() + 512) >> 10); + } + return sz; + } + + public long getFileDate() { + if (file == null) { + return 0; + } + return file.lastModified(); + } + + public String getFileName(boolean includeSubfolder) { + String result; + if (fileName != null) { + result = fileName; + } else { + if (file == null) { + result = ""; + } else { + result = fileName = file.getName(); + } + } + if (includeSubfolder && !Algorithms.isEmpty(subfolder)) { + result = subfolder + "/" + result; + } + return result; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java b/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java index 09d7e7b7e4..9f80a25201 100644 --- a/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java +++ b/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java @@ -6,6 +6,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PointF; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import com.vividsolutions.jts.geom.Coordinate; @@ -346,6 +347,11 @@ class MapillaryVectorLayer extends MapTileLayer implements MapillaryLayer, ICont return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + private void getImagesFromPoint(RotatedTileBox tb, PointF point, List images) { Map points = this.visiblePoints; if (points != null) { diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java index 7e6a1439ee..4da4e357f5 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java @@ -7,6 +7,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import net.osmand.GPXUtilities.TrkSegment; @@ -516,6 +517,11 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + private Location getLocationFromLL(double lat, double lon) { Location l = new Location(""); l.setLatitude(lat); diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java index 013ba37d5b..23bff39f89 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java @@ -7,6 +7,7 @@ import android.view.View; import android.widget.EditText; import android.widget.Toast; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import net.osmand.PlatformUtil; @@ -467,6 +468,11 @@ public class OsmBugsLayer extends OsmandMapLayer implements IContextMenuProvider return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List res, boolean unknownLocation) { if (tileBox.getZoom() >= startZoom) { diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java index a0fe1c1af2..3b416ac915 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java @@ -194,6 +194,11 @@ public class OsmEditsLayer extends OsmandMapLayer implements ContextMenuLayer.IC return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List o, boolean unknownLocation) { if (tileBox.getZoom() >= startZoom) { diff --git a/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java b/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java index f1bc2c927a..412eba2323 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java @@ -13,7 +13,6 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; import androidx.annotation.DrawableRes; import androidx.annotation.StringRes; import androidx.appcompat.widget.SwitchCompat; @@ -32,10 +31,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static net.osmand.AndroidUtils.isLayoutRtl; + public abstract class SwitchableAction extends QuickAction { public static final String KEY_ID = "id"; - + protected static final String KEY_DIALOG = "dialog"; private transient EditText title; @@ -62,7 +63,7 @@ public abstract class SwitchableAction extends QuickAction { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.quick_action_switchable_action, parent, false); - final SwitchCompat showDialog = (SwitchCompat) view.findViewById(R.id.saveButton); + final SwitchCompat showDialog = view.findViewById(R.id.saveButton); if (!getParams().isEmpty()) { showDialog.setChecked(Boolean.valueOf(getParams().get(KEY_DIALOG))); } @@ -74,7 +75,7 @@ public abstract class SwitchableAction extends QuickAction { } }); - RecyclerView list = (RecyclerView) view.findViewById(R.id.list); + RecyclerView list = view.findViewById(R.id.list); adapter = new Adapter(activity, new QuickActionListFragment.OnStartDragListener() { @Override public void onStartDrag(RecyclerView.ViewHolder viewHolder) { @@ -92,9 +93,9 @@ public abstract class SwitchableAction extends QuickAction { list.setAdapter(adapter); - TextView dscrTitle = (TextView) view.findViewById(R.id.textDscrTitle); - TextView dscrHint = (TextView) view.findViewById(R.id.textDscrHint); - Button addBtn = (Button) view.findViewById(R.id.btnAdd); + TextView dscrTitle = view.findViewById(R.id.textDscrTitle); + TextView dscrHint = view.findViewById(R.id.textDscrHint); + Button addBtn = view.findViewById(R.id.btnAdd); dscrTitle.setText(parent.getContext().getString(getDiscrTitle()) + ":"); dscrHint.setText(getDiscrHint()); @@ -104,10 +105,18 @@ public abstract class SwitchableAction extends QuickAction { parent.addView(view); } + @Override + public String getActionText(OsmandApplication application) { + String item = getSelectedItem(application); + String arrowDirection = isLayoutRtl(application) ? "\u25c0" : "\u25b6"; + + return application.getString(R.string.ltr_or_rtl_combine_via_space, getTranslatedItemName(application, item), arrowDirection + "\u2026"); + } + @Override public boolean fillParams(View root, MapActivity activity) { - final RecyclerView list = (RecyclerView) root.findViewById(R.id.list); + final RecyclerView list = root.findViewById(R.id.list); final Adapter adapter = (Adapter) list.getAdapter(); boolean hasParams = adapter.itemsList != null && !adapter.itemsList.isEmpty(); @@ -124,7 +133,7 @@ public abstract class SwitchableAction extends QuickAction { public abstract List loadListFromParams(); public abstract void executeWithParams(MapActivity activity, String params); - + public abstract String getTranslatedItemName(Context context, String item); protected void showChooseDialog(FragmentManager fm) { @@ -143,7 +152,7 @@ public abstract class SwitchableAction extends QuickAction { private List itemsList = new ArrayList<>(); private final QuickActionListFragment.OnStartDragListener onStartDragListener; - private Context context; + private final Context context; public Adapter(Context context, QuickActionListFragment.OnStartDragListener onStartDragListener) { this.context = context; @@ -293,10 +302,10 @@ public abstract class SwitchableAction extends QuickAction { public ItemHolder(View itemView) { super(itemView); - title = (TextView) itemView.findViewById(R.id.title); - handleView = (ImageView) itemView.findViewById(R.id.handle_view); - closeBtn = (ImageView) itemView.findViewById(R.id.closeImageButton); - icon = (ImageView) itemView.findViewById(R.id.imageView); + title = itemView.findViewById(R.id.title); + handleView = itemView.findViewById(R.id.handle_view); + closeBtn = itemView.findViewById(R.id.closeImageButton); + icon = itemView.findViewById(R.id.imageView); } } } diff --git a/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java b/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java index 5838bfc1a2..eedfe1beb7 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java @@ -54,7 +54,7 @@ public class MapStyleAction extends SwitchableAction { if (current != null) { return current.getName(); } else { - return RendererRegistry.DEFAULT_RENDER; + return RendererRegistry.DEFAULT_RENDER; } } @@ -77,7 +77,7 @@ public class MapStyleAction extends SwitchableAction { executeWithParams(activity, nextStyle); } else { Toast.makeText(activity, R.string.quick_action_need_to_add_item_to_list, - Toast.LENGTH_LONG).show(); + Toast.LENGTH_LONG).show(); } } @@ -246,10 +246,4 @@ public class MapStyleAction extends SwitchableAction { : filters.get(0); } - @Override - public String getActionText(OsmandApplication application) { - String currentSource = application.getSettings().RENDERER.get(); - - return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); - } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/quickaction/actions/SwitchProfileAction.java b/OsmAnd/src/net/osmand/plus/quickaction/actions/SwitchProfileAction.java index da31b61466..456028881d 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/actions/SwitchProfileAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/actions/SwitchProfileAction.java @@ -27,6 +27,8 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import static net.osmand.AndroidUtils.isLayoutRtl; + public class SwitchProfileAction extends SwitchableAction { private final static String KEY_PROFILES = "profiles"; @@ -230,4 +232,9 @@ public class SwitchProfileAction extends SwitchableAction { } } } + + @Override + public String getActionText(OsmandApplication application) { + return getName(application); + } } diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java index 8190b6c995..2dc809488e 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java @@ -15,13 +15,13 @@ import com.google.gson.reflect.TypeToken; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickActionType; import net.osmand.plus.quickaction.SwitchableAction; +import net.osmand.plus.settings.backend.OsmandSettings; import java.lang.reflect.Type; import java.util.ArrayList; @@ -59,7 +59,7 @@ public class MapOverlayAction extends SwitchableAction> { @Override public String getSelectedItem(OsmandApplication app) { - return app.getSettings().MAP_OVERLAY.get(); + return app.getSettings().MAP_OVERLAY.get() != null ? app.getSettings().MAP_OVERLAY.get() : KEY_NO_OVERLAY; } @Override @@ -224,12 +224,4 @@ public class MapOverlayAction extends SwitchableAction> { getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); return super.fillParams(root, activity); } - - @Override - public String getActionText(OsmandApplication application) { - String currentSource = application.getSettings().MAP_OVERLAY.get() == null ? KEY_NO_OVERLAY - : application.getSettings().MAP_OVERLAY.get(); - - return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); - } } diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java index d81b1f12ed..8ace30844b 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java @@ -15,13 +15,13 @@ import com.google.gson.reflect.TypeToken; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickActionType; import net.osmand.plus.quickaction.SwitchableAction; +import net.osmand.plus.settings.backend.OsmandSettings; import java.lang.reflect.Type; import java.util.ArrayList; @@ -98,8 +98,8 @@ public class MapSourceAction extends SwitchableAction> { } Pair currentSource = settings.MAP_ONLINE_DATA.get() - ? new Pair<>(settings.MAP_TILE_SOURCES.get(), settings.MAP_TILE_SOURCES.get()) - : new Pair<>(LAYER_OSM_VECTOR, activity.getString(R.string.vector_data)); + ? new Pair<>(settings.MAP_TILE_SOURCES.get(), settings.MAP_TILE_SOURCES.get()) + : new Pair<>(LAYER_OSM_VECTOR, activity.getString(R.string.vector_data)); Pair nextSource = sources.get(0); int index = sources.indexOf(currentSource); @@ -135,7 +135,7 @@ public class MapSourceAction extends SwitchableAction> { return item; } } - + @Override protected int getAddBtnText() { return R.string.quick_action_map_source_action; @@ -214,13 +214,4 @@ public class MapSourceAction extends SwitchableAction> { getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); return super.fillParams(root, activity); } - - @Override - public String getActionText(OsmandApplication application) { - String currentSource = application.getSettings().MAP_ONLINE_DATA.get() - ? application.getSettings().MAP_TILE_SOURCES.get() - : application.getString(R.string.vector_data); - - return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); - } } diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java index ddb941e10b..167338add1 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java @@ -59,7 +59,7 @@ public class MapUnderlayAction extends SwitchableAction> { @Override public String getSelectedItem(OsmandApplication app) { - return app.getSettings().MAP_UNDERLAY.get(); + return app.getSettings().MAP_UNDERLAY.get() != null ? app.getSettings().MAP_UNDERLAY.get() : KEY_NO_UNDERLAY; } @Override @@ -103,7 +103,7 @@ public class MapUnderlayAction extends SwitchableAction> { int index = -1; final String currentSource = settings.MAP_UNDERLAY.get() == null ? KEY_NO_UNDERLAY - : settings.MAP_UNDERLAY.get(); + : settings.MAP_UNDERLAY.get(); for (int idx = 0; idx < sources.size(); idx++) { if (sources.get(idx).first.equals(currentSource)) { @@ -157,7 +157,7 @@ public class MapUnderlayAction extends SwitchableAction> { return item; } } - + @Override protected int getAddBtnText() { return R.string.quick_action_map_underlay_action; @@ -226,12 +226,4 @@ public class MapUnderlayAction extends SwitchableAction> { getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); return super.fillParams(root, activity); } - - @Override - public String getActionText(OsmandApplication application) { - String currentSource = application.getSettings().MAP_UNDERLAY.get() == null ? KEY_NO_UNDERLAY - : application.getSettings().MAP_UNDERLAY.get(); - - return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); - } } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index 501d84d608..1a77de50fb 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -1165,6 +1165,13 @@ public class OsmandSettings { public final OsmandPreference DISCOUNT_TOTAL_SHOW = new IntPreference(this, "discount_total_show", 0).makeGlobal(); public final OsmandPreference DISCOUNT_SHOW_DATETIME_MS = new LongPreference(this, "show_discount_datetime_ms", 0).makeGlobal(); + public final OsmandPreference BACKUP_USER_EMAIL = new StringPreference(this, "backup_user_email", "").makeGlobal(); + public final OsmandPreference BACKUP_USER_ID = new StringPreference(this, "backup_user_id", "").makeGlobal(); + public final OsmandPreference BACKUP_DEVICE_ID = new StringPreference(this, "backup_device_id", "").makeGlobal(); + public final OsmandPreference BACKUP_NATIVE_DEVICE_ID = new StringPreference(this, "backup_native_device_id", "").makeGlobal(); + public final OsmandPreference BACKUP_ACCESS_TOKEN = new StringPreference(this, "backup_access_token", "").makeGlobal(); + public final OsmandPreference BACKUP_ACCESS_TOKEN_UPDATE_TIME = new StringPreference(this, "backup_access_token_update_time", "").makeGlobal(); + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USER_OSM_BUG_NAME = new StringPreference(this, "user_osm_bug_name", "NoName/OsmAnd").makeGlobal().makeShared(); diff --git a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java index 4e5e6f6e0c..23bcd0d99b 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java @@ -852,9 +852,9 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card osmEditingPlugin.sendGPXFiles(mapActivity, this, gpxInfo); } } else if (buttonIndex == EDIT_BUTTON_INDEX) { + dismiss(); String fileName = Algorithms.getFileWithoutDirs(gpxFile.path); MeasurementToolFragment.showInstance(mapActivity.getSupportFragmentManager(), fileName); - dismiss(); } else if (buttonIndex == RENAME_BUTTON_INDEX) { FileUtils.renameFile(mapActivity, new File(gpxFile.path), this, true); } else if (buttonIndex == CHANGE_FOLDER_BUTTON_INDEX) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java index 8107943408..45600b8592 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java @@ -24,13 +24,14 @@ import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.data.RotatedTileBox; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.mapcontextmenu.MapContextMenu; +import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider; +import net.osmand.plus.views.layers.MapTextLayer.MapTextProvider; import net.osmand.plus.widgets.tools.CropCircleTransformation; import java.io.IOException; @@ -46,7 +47,7 @@ import java.util.concurrent.ConcurrentHashMap; import static net.osmand.aidl.ConnectedApp.AIDL_LAYERS_PREFIX; -public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider, MapTextLayer.MapTextProvider { +public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider, MapTextProvider { private static final float POINT_IMAGE_VERTICAL_OFFSET = 0.91f; @@ -281,6 +282,11 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List o, boolean unknownLocation) { if (isLayerEnabled()) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java index 4c2a63db9e..1efe5972b8 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java @@ -609,7 +609,7 @@ public class ContextMenuLayer extends OsmandMapLayer { String title = pointDescription == null ? "" : pointDescription.getName(); mAddGpxPointBottomSheetHelper.setTitle(title); view.getAnimatedDraggingThread().startMoving(latLon.getLatitude(), latLon.getLongitude(), view.getZoom(), true); - } else { + } else if (provider == null || !provider.showMenuAction(object)) { selectedObjectContextMenuProvider = provider; hideVisibleMenues(); activity.getMapViewTrackingUtilities().setMapLinkedToLocation(false); @@ -1106,6 +1106,8 @@ public class ContextMenuLayer extends OsmandMapLayer { boolean isObjectClickable(Object o); boolean runExclusiveAction(@Nullable Object o, boolean unknownLocation); + + boolean showMenuAction(@Nullable Object o); } public interface IMoveObjectProvider { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/DownloadedRegionsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/DownloadedRegionsLayer.java index a624b8412c..f170e005cb 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/DownloadedRegionsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/DownloadedRegionsLayer.java @@ -13,6 +13,7 @@ import android.util.DisplayMetrics; import android.view.WindowManager; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.IndexConstants; import net.osmand.binary.BinaryMapDataObject; @@ -580,6 +581,11 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + private void getWorldRegionFromPoint(RotatedTileBox tb, PointF point, List dataObjects) { int zoom = tb.getZoom(); if (zoom >= ZOOM_TO_SHOW_SELECTION_ST && zoom < ZOOM_TO_SHOW_SELECTION diff --git a/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java index 5e4f95f136..656568b425 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java @@ -227,6 +227,11 @@ public class FavouritesLayer extends OsmandMapLayer implements IContextMenuProvi return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List res, boolean unknownLocation) { if (this.settings.SHOW_FAVORITES.get() && tileBox.getZoom() >= startZoom) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 24a6d244bb..2531b78a51 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -1108,13 +1108,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM @Override public boolean runExclusiveAction(Object object, boolean unknownLocation) { - if (unknownLocation || !(object instanceof SelectedGpxPoint)) { - return false; - } - MapActivity mapActivity = (MapActivity) view.getContext(); - SelectedGpxPoint point = (SelectedGpxPoint) object; - TrackMenuFragment.showInstance(mapActivity, point.getSelectedGpxFile(), point, null, null, false); - return true; + return false; } @Override @@ -1158,14 +1152,24 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM SelectedGpxPoint selectedGpxPoint = (SelectedGpxPoint) trackPoints.get(0); WptPt wptPt = selectedGpxPoint.getSelectedPoint(); PointDescription description = getObjectName(selectedGpxPoint); - ContextMenuLayer contextMenuLayer = mapActivity.getMapLayers().getContextMenuLayer(); - contextMenuLayer.showContextMenu(new LatLon(wptPt.lat, wptPt.lon), description, selectedGpxPoint, this); + mapActivity.getContextMenu().show(new LatLon(wptPt.lat, wptPt.lon), description, selectedGpxPoint); return true; } } return false; } + @Override + public boolean showMenuAction(@Nullable Object object) { + if (!(object instanceof SelectedGpxPoint)) { + return false; + } + MapActivity mapActivity = (MapActivity) view.getContext(); + SelectedGpxPoint point = (SelectedGpxPoint) object; + TrackMenuFragment.showInstance(mapActivity, point.getSelectedGpxFile(), point, null, null, false); + return true; + } + @Override public LatLon getTextLocation(WptPt o) { return new LatLon(o.lat, o.lon); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/ImpassableRoadsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/ImpassableRoadsLayer.java index 09f84ba6e3..e2bd7944e2 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/ImpassableRoadsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/ImpassableRoadsLayer.java @@ -142,6 +142,11 @@ public class ImpassableRoadsLayer extends OsmandMapLayer implements return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List o, boolean unknownLocation) { if (tileBox.getZoom() >= START_ZOOM) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapMarkersLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapMarkersLayer.java index a8d79b4519..b6496222f8 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapMarkersLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapMarkersLayer.java @@ -531,6 +531,11 @@ public class MapMarkersLayer extends OsmandMapLayer implements IContextMenuProvi return true; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List o, boolean unknownLocation) { if (tileBox.getZoom() < 3 || !map.getMyApplication().getSettings().SHOW_MAP_MARKERS.get()) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java index c264b67b0f..1adeed7671 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java @@ -16,6 +16,7 @@ import android.widget.LinearLayout.LayoutParams; import android.widget.ScrollView; import android.widget.TextView; +import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; @@ -408,6 +409,11 @@ public class POIMapLayer extends OsmandMapLayer implements ContextMenuLayer.ICon return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public LatLon getTextLocation(Amenity o) { return o.getLocation(); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java index 2021f5d32f..29626e602e 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java @@ -13,6 +13,7 @@ import android.graphics.RectF; import android.graphics.drawable.LayerDrawable; import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; @@ -22,14 +23,15 @@ import net.osmand.PlatformUtil; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.data.RotatedTileBox; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.profiles.ProfileIconColors; +import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; +import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider; import org.apache.commons.logging.Log; @@ -38,7 +40,7 @@ import java.util.List; import static android.graphics.Paint.ANTI_ALIAS_FLAG; import static android.graphics.Paint.FILTER_BITMAP_FLAG; -public class PointLocationLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider { +public class PointLocationLayer extends OsmandMapLayer implements IContextMenuProvider { private static final Log LOG = PlatformUtil.getLog(PointLocationLayer.class); protected final static int RADIUS = 7; @@ -240,6 +242,11 @@ public class PointLocationLayer extends OsmandMapLayer implements ContextMenuLay return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + private LatLon getMyLocation() { Location location = locationProvider.getLastKnownLocation(); if (location != null) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/PointNavigationLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/PointNavigationLayer.java index 26899516e3..17ddaf6c6c 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/PointNavigationLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/PointNavigationLayer.java @@ -23,11 +23,13 @@ import net.osmand.plus.activities.MapActivity; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider; +import net.osmand.plus.views.layers.ContextMenuLayer.IMoveObjectProvider; import java.util.List; public class PointNavigationLayer extends OsmandMapLayer implements - IContextMenuProvider, ContextMenuLayer.IMoveObjectProvider { + IContextMenuProvider, IMoveObjectProvider { + protected final static int DIST_TO_SHOW = 80; private Paint mPoint; @@ -186,6 +188,11 @@ public class PointNavigationLayer extends OsmandMapLayer implements return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List o, boolean unknownLocation) { if (tileBox.getZoom() >= 3) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java index f73a44f0ba..01e6765000 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java @@ -46,6 +46,7 @@ import net.osmand.plus.routing.TransportRoutingHelper; import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; +import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider; import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWay; import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWayContext; import net.osmand.plus.views.layers.geometry.RouteGeometryWay; @@ -67,7 +68,7 @@ import java.util.Map; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_WIDTH_ATTR; -public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider { +public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider { private static final Log log = PlatformUtil.getLog(RouteLayer.class); @@ -815,4 +816,9 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont public boolean runExclusiveAction(@Nullable Object o, boolean unknownLocation) { return false; } + + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java index 082b8998df..14a8575df0 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java @@ -8,6 +8,7 @@ import android.util.DisplayMetrics; import android.view.WindowManager; import androidx.annotation.DrawableRes; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import net.osmand.ResultMatcher; @@ -21,16 +22,17 @@ import net.osmand.data.TransportStop; import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Way; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.base.PointImageDrawable; -import net.osmand.plus.settings.backend.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.base.PointImageDrawable; import net.osmand.plus.render.RenderingIcons; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.transport.TransportStopRoute; import net.osmand.plus.transport.TransportStopType; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; +import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider; import net.osmand.plus.views.layers.geometry.GeometryWay; import java.io.IOException; @@ -40,7 +42,7 @@ import java.util.Comparator; import java.util.List; import java.util.TreeSet; -public class TransportStopsLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider { +public class TransportStopsLayer extends OsmandMapLayer implements IContextMenuProvider { public static final String TRANSPORT_STOPS_OVER_MAP = "transportStops"; @@ -308,6 +310,11 @@ public class TransportStopsLayer extends OsmandMapLayer implements ContextMenuLa return false; } + @Override + public boolean showMenuAction(@Nullable Object o) { + return false; + } + @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List res, boolean unknownLocation) { if(tileBox.getZoom() >= startZoomRoute && stopRoute != null) { diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/WidgetsVisibilityHelper.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/WidgetsVisibilityHelper.java index 4c538ddba1..72d8814a80 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/WidgetsVisibilityHelper.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/WidgetsVisibilityHelper.java @@ -21,10 +21,10 @@ import java.lang.ref.WeakReference; public class WidgetsVisibilityHelper { - private MapActivity mapActivity; - private OsmandSettings settings; - private RoutingHelper routingHelper; - private MapActivityLayers mapLayers; + private final MapActivity mapActivity; + private final OsmandSettings settings; + private final RoutingHelper routingHelper; + private final MapActivityLayers mapLayers; public WidgetsVisibilityHelper(@NonNull MapActivity mapActivity) { this.mapActivity = mapActivity; @@ -104,7 +104,6 @@ public class WidgetsVisibilityHelper { public boolean shouldHideCompass() { return mapActivity.shouldHideTopControls() || isTrackDetailsMenuOpened() - || isInMeasurementToolMode() || isInPlanRouteMode() || isInChoosingRoutesMode() || isInTrackAppearanceMode() @@ -116,7 +115,6 @@ public class WidgetsVisibilityHelper { public boolean shouldShowTopButtons() { return !mapActivity.shouldHideTopControls() && !isTrackDetailsMenuOpened() - && !isInMeasurementToolMode() && !isInPlanRouteMode() && !isInChoosingRoutesMode() && !isInTrackAppearanceMode() diff --git a/build.gradle b/build.gradle index 48bcc21248..9c0feb1c3d 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } dependencies { //classpath 'com.android.tools.build:gradle:2.+' - classpath 'com.android.tools.build:gradle:4.1.2' + classpath 'com.android.tools.build:gradle:4.1.3' classpath 'com.google.gms:google-services:3.0.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"