diff --git a/OsmAnd/res/drawable/ic_action_alert_circle.xml b/OsmAnd/res/drawable/ic_action_alert_circle.xml
new file mode 100644
index 0000000000..38e1947883
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_alert_circle.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_cellular.xml b/OsmAnd/res/drawable/ic_action_cellular.xml
new file mode 100644
index 0000000000..0d154db6b7
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_cellular.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_cellular_disable.xml b/OsmAnd/res/drawable/ic_action_cellular_disable.xml
new file mode 100644
index 0000000000..06a8cc4845
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_cellular_disable.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_cloud.xml b/OsmAnd/res/drawable/ic_action_cloud.xml
new file mode 100644
index 0000000000..4d778fb6e5
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_cloud.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_cloud_alert.xml b/OsmAnd/res/drawable/ic_action_cloud_alert.xml
new file mode 100644
index 0000000000..61b5efee0b
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_cloud_alert.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_cloud_upload.xml b/OsmAnd/res/drawable/ic_action_cloud_upload.xml
new file mode 100644
index 0000000000..7b2497436d
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_cloud_upload.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_cloud_upload_colored.xml b/OsmAnd/res/drawable/ic_action_cloud_upload_colored.xml
new file mode 100644
index 0000000000..3336a301b1
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_cloud_upload_colored.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_read_from_file.xml b/OsmAnd/res/drawable/ic_action_read_from_file.xml
new file mode 100644
index 0000000000..3492a4bcd2
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_read_from_file.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_restore.xml b/OsmAnd/res/drawable/ic_action_restore.xml
new file mode 100644
index 0000000000..dfe803dc2c
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_restore.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OsmAnd/res/drawable/ic_action_user_folder.xml b/OsmAnd/res/drawable/ic_action_user_folder.xml
new file mode 100644
index 0000000000..2990186fb5
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_user_folder.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/OsmAnd/src/net/osmand/AndroidNetworkUtils.java b/OsmAnd/src/net/osmand/AndroidNetworkUtils.java
index f25d08565b..46812373ad 100644
--- a/OsmAnd/src/net/osmand/AndroidNetworkUtils.java
+++ b/OsmAnd/src/net/osmand/AndroidNetworkUtils.java
@@ -48,13 +48,14 @@ public class AndroidNetworkUtils {
private static final Log LOG = PlatformUtil.getLog(AndroidNetworkUtils.class);
public interface OnRequestResultListener {
- void onResult(String result);
+ void onResult(@Nullable String result, @Nullable String error);
}
public interface OnFilesUploadCallback {
@Nullable
Map getAdditionalParams(@NonNull File file);
void onFileUploadProgress(@NonNull File file, int percent);
+ void onFileUploadDone(@NonNull File file);
void onFilesUploadDone(@NonNull Map errors);
}
@@ -64,16 +65,26 @@ public class AndroidNetworkUtils {
void onFileDownloadProgress(@NonNull File file, int percent);
@WorkerThread
void onFileDownloadedAsync(@NonNull File file);
+
+ void onFileDownloadDone(@NonNull File file);
void onFilesDownloadDone(@NonNull Map errors);
}
public static class RequestResponse {
- private Request request;
- private String response;
+ private final Request request;
+ private final String response;
+ private final String error;
RequestResponse(@NonNull Request request, @Nullable String response) {
this.request = request;
this.response = response;
+ this.error = null;
+ }
+
+ RequestResponse(@NonNull Request request, @Nullable String response, @Nullable String error) {
+ this.request = request;
+ this.response = response;
+ this.error = error;
}
public Request getRequest() {
@@ -83,6 +94,10 @@ public class AndroidNetworkUtils {
public String getResponse() {
return response;
}
+
+ public String getError() {
+ return error;
+ }
}
public interface OnSendRequestsListener {
@@ -109,11 +124,18 @@ public class AndroidNetworkUtils {
for (Request request : requests) {
RequestResponse requestResponse;
try {
- String response = sendRequest(ctx, request.getUrl(), request.getParameters(),
- request.getUserOperation(), request.isToastAllowed(), request.isPost());
- requestResponse = new RequestResponse(request, response);
+ final String[] response = {null, null};
+ sendRequest(ctx, request.getUrl(), request.getParameters(),
+ request.getUserOperation(), request.isToastAllowed(), request.isPost(), new OnRequestResultListener() {
+ @Override
+ public void onResult(@Nullable String result, @Nullable String error) {
+ response[0] = result;
+ response[1] = error;
+ }
+ });
+ requestResponse = new RequestResponse(request, response[0], response[1]);
} catch (Exception e) {
- requestResponse = new RequestResponse(request, null);
+ requestResponse = new RequestResponse(request, null, "Unexpected error");
}
responses.add(requestResponse);
publishProgress(requestResponse);
@@ -157,21 +179,29 @@ public class AndroidNetworkUtils {
final boolean post,
final OnRequestResultListener listener,
final Executor executor) {
- new AsyncTask() {
+ new AsyncTask() {
@Override
- protected String doInBackground(Void... params) {
+ protected String[] doInBackground(Void... params) {
+ final String[] res = {null, null};
try {
- return sendRequest(ctx, url, parameters, userOperation, toastAllowed, post);
+ sendRequest(ctx, url, parameters, userOperation, toastAllowed, post, new OnRequestResultListener() {
+ @Override
+ public void onResult(@Nullable String result, @Nullable String error) {
+ res[0] = result;
+ res[1] = error;
+ }
+ });
} catch (Exception e) {
- return null;
+ // ignore
}
+ return res;
}
@Override
- protected void onPostExecute(String response) {
+ protected void onPostExecute(String[] response) {
if (listener != null) {
- listener.onResult(response);
+ listener.onResult(response[0], response[1]);
}
}
@@ -255,7 +285,7 @@ public class AndroidNetworkUtils {
} catch (Exception e) {
errors.put(file, e.getMessage());
}
- publishProgress(file, Integer.MAX_VALUE);
+ publishProgress(file, -1);
}
return errors;
}
@@ -263,7 +293,13 @@ public class AndroidNetworkUtils {
@Override
protected void onProgressUpdate(Object... objects) {
if (callback != null) {
- callback.onFileDownloadProgress((File) objects[0], (Integer) objects[1]);
+ File file = (File) objects[0];
+ Integer progress = (Integer) objects[1];
+ if (progress >= 0) {
+ callback.onFileDownloadProgress(file, progress);
+ } else {
+ callback.onFileDownloadDone(file);
+ }
}
}
@@ -280,9 +316,17 @@ public class AndroidNetworkUtils {
public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
@Nullable Map parameters,
@Nullable String userOperation, boolean toastAllowed, boolean post) {
+ return sendRequest(ctx, url, parameters, userOperation, toastAllowed, post, null);
+ }
+
+ public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
+ @Nullable Map parameters,
+ @Nullable String userOperation, boolean toastAllowed, boolean post,
+ @Nullable OnRequestResultListener listener) {
+ String result = null;
+ String error = null;
HttpURLConnection connection = null;
try {
-
String params = null;
if (parameters != null && parameters.size() > 0) {
StringBuilder sb = new StringBuilder();
@@ -312,68 +356,66 @@ public class AndroidNetworkUtils {
output.write(params.getBytes("UTF-8"));
output.flush();
output.close();
-
} else {
-
connection.setRequestMethod("GET");
connection.connect();
}
-
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ if (ctx != null) {
+ error = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
+ + ctx.getString(R.string.failed_op) + ": " + connection.getResponseMessage();
+ } else {
+ error = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
+ + "failed: " + connection.getResponseMessage();
+ }
if (toastAllowed && ctx != null) {
- String msg = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
- + ctx.getString(R.string.failed_op) + ": "
- + connection.getResponseMessage();
- showToast(ctx, msg);
+ showToast(ctx, error);
+ }
+ InputStream errorStream = connection.getErrorStream();
+ if (errorStream != null) {
+ error = streamToString(errorStream);
}
} else {
- StringBuilder responseBody = new StringBuilder();
- responseBody.setLength(0);
- InputStream i = connection.getInputStream();
- if (i != null) {
- BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256);
- String s;
- boolean f = true;
- while ((s = in.readLine()) != null) {
- if (!f) {
- responseBody.append("\n");
- } else {
- f = false;
- }
- responseBody.append(s);
- }
- try {
- in.close();
- i.close();
- } catch (Exception e) {
- // ignore exception
- }
- }
- return responseBody.toString();
+ result = streamToString(connection.getInputStream());
}
-
} catch (NullPointerException e) {
// that's tricky case why NPE is thrown to fix that problem httpClient could be used
+ if (ctx != null) {
+ error = ctx.getString(R.string.auth_failed);
+ } else {
+ error = "Authorization failed";
+ }
if (toastAllowed && ctx != null) {
- String msg = ctx.getString(R.string.auth_failed);
- showToast(ctx, msg);
+ showToast(ctx, error);
}
} catch (MalformedURLException e) {
+ if (ctx != null) {
+ error = MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
+ + ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation);
+ } else {
+ error = "Action " + userOperation + ": Unexpected error";
+ }
if (toastAllowed && ctx != null) {
- showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
- + ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation));
+ showToast(ctx, error);
}
} catch (IOException e) {
+ if (ctx != null) {
+ error = MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
+ + ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation);
+ } else {
+ error = "Action " + userOperation + ": I/O error";
+ }
if (toastAllowed && ctx != null) {
- showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
- + ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation));
+ showToast(ctx, error);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
-
+ if (listener != null) {
+ listener.onResult(result, error);
+ }
return null;
}
@@ -401,35 +443,64 @@ public class AndroidNetworkUtils {
public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) {
String error = null;
try {
- URLConnection connection = NetworkUtils.getHttpURLConnection(url);
+ HttpURLConnection connection = NetworkUtils.getHttpURLConnection(url);
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setReadTimeout(CONNECTION_TIMEOUT);
if (gzip) {
connection.setRequestProperty("Accept-Encoding", "deflate, gzip");
}
- InputStream inputStream = gzip
- ? new GZIPInputStream(connection.getInputStream())
- : new BufferedInputStream(connection.getInputStream(), 8 * 1024);
- fileToSave.getParentFile().mkdirs();
- OutputStream stream = null;
- try {
- stream = new FileOutputStream(fileToSave);
- Algorithms.streamCopy(inputStream, stream, progress, 1024);
- stream.flush();
- } finally {
- Algorithms.closeStream(inputStream);
- Algorithms.closeStream(stream);
+ connection.connect();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ return streamToString(connection.getErrorStream());
+ } else {
+ InputStream inputStream = gzip
+ ? new GZIPInputStream(connection.getInputStream())
+ : new BufferedInputStream(connection.getInputStream(), 8 * 1024);
+ fileToSave.getParentFile().mkdirs();
+ OutputStream stream = null;
+ try {
+ stream = new FileOutputStream(fileToSave);
+ Algorithms.streamCopy(inputStream, stream, progress, 1024);
+ stream.flush();
+ } finally {
+ Algorithms.closeStream(inputStream);
+ Algorithms.closeStream(stream);
+ }
}
} catch (UnknownHostException e) {
error = e.getMessage();
LOG.error("UnknownHostException, cannot download file " + url + " " + error);
} catch (Exception e) {
error = e.getMessage();
- LOG.warn("Cannot download file : " + url, e);
+ LOG.warn("Cannot download file: " + url, e);
}
return error;
}
+ private static String streamToString(InputStream inputStream) throws IOException {
+ StringBuilder result = new StringBuilder();
+ if (inputStream != null) {
+ BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 256);
+ String buffer;
+ boolean f = true;
+ while ((buffer = in.readLine()) != null) {
+ if (!f) {
+ result.append("\n");
+ } else {
+ f = false;
+ }
+ result.append(buffer);
+ }
+ try {
+ in.close();
+ inputStream.close();
+ } catch (Exception e) {
+ // ignore exception
+ }
+ }
+ return result.toString();
+ }
+
private static final String BOUNDARY = "CowMooCowMooCowCowCow";
public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip,
@@ -444,7 +515,6 @@ public class AndroidNetworkUtils {
@NonNull Map additionalParams,
@Nullable Map headers,
@Nullable IProgress progress) {
- URL url;
try {
boolean firstPrm = !urlText.contains("?");
StringBuilder sb = new StringBuilder(urlText);
@@ -455,7 +525,7 @@ public class AndroidNetworkUtils {
urlText = sb.toString();
LOG.info("Start uploading file to " + urlText + " " + fileName);
- url = new URL(urlText);
+ URL url = new URL(urlText);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
@@ -496,6 +566,10 @@ public class AndroidNetworkUtils {
LOG.info("Finish uploading file " + fileName);
LOG.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage());
if (conn.getResponseCode() != 200) {
+ InputStream errorStream = conn.getErrorStream();
+ if (errorStream != null) {
+ return streamToString(errorStream);
+ }
return conn.getResponseMessage();
}
InputStream is = conn.getInputStream();
@@ -575,7 +649,7 @@ public class AndroidNetworkUtils {
} catch (Exception e) {
errors.put(file, e.getMessage());
}
- publishProgress(file, Integer.MAX_VALUE);
+ publishProgress(file, -1);
}
return errors;
}
@@ -583,7 +657,13 @@ public class AndroidNetworkUtils {
@Override
protected void onProgressUpdate(Object... objects) {
if (callback != null) {
- callback.onFileUploadProgress((File) objects[0], (Integer) objects[1]);
+ File file = (File) objects[0];
+ Integer progress = (Integer) objects[1];
+ if (progress >= 0) {
+ callback.onFileUploadProgress(file, progress);
+ } else {
+ callback.onFileUploadDone(file);
+ }
}
}
@@ -602,11 +682,11 @@ public class AndroidNetworkUtils {
}
public static class Request {
- private String url;
- private Map parameters;
- private String userOperation;
- private boolean toastAllowed;
- private boolean post;
+ private final String url;
+ private final Map parameters;
+ private final String userOperation;
+ private final boolean toastAllowed;
+ private final boolean post;
public Request(String url, Map parameters, String userOperation, boolean toastAllowed, boolean post) {
this.url = url;
diff --git a/OsmAnd/src/net/osmand/plus/CustomRegion.java b/OsmAnd/src/net/osmand/plus/CustomRegion.java
index 7cf4a3c9f9..dd72c6008b 100644
--- a/OsmAnd/src/net/osmand/plus/CustomRegion.java
+++ b/OsmAnd/src/net/osmand/plus/CustomRegion.java
@@ -225,7 +225,7 @@ public class CustomRegion extends WorldRegion {
&& app.getSettings().isInternetConnectionAvailable()) {
OnRequestResultListener resultListener = new OnRequestResultListener() {
@Override
- public void onResult(String result) {
+ public void onResult(@Nullable String result, @Nullable String error) {
if (!Algorithms.isEmpty(result)) {
if ("json".equalsIgnoreCase(dynamicDownloadItems.format)) {
dynamicItemsJson = mapJsonItems(result);
diff --git a/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java b/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java
index 87423ec582..5bdedcaa48 100644
--- a/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java
+++ b/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java
@@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -65,10 +66,6 @@ public class BackupHelper {
public final static int STATUS_EMPTY_RESPONSE_ERROR = 2;
public final static int STATUS_SERVER_ERROR = 3;
- public interface OnResultListener {
- void onResult(int status, @Nullable String message, @Nullable JSONObject json);
- }
-
public interface OnRegisterUserListener {
void onRegisterUser(int status, @Nullable String message);
}
@@ -83,7 +80,6 @@ public class BackupHelper {
public interface OnCollectLocalFilesListener {
void onFileCollected(@NonNull GpxFileInfo fileInfo);
-
void onFilesCollected(@NonNull List fileInfos);
}
@@ -93,20 +89,21 @@ public class BackupHelper {
public interface OnUploadFilesListener {
void onFileUploadProgress(@NonNull File file, int progress);
-
+ void onFileUploadDone(@NonNull File file);
void onFilesUploadDone(@NonNull Map errors);
}
public interface OnDeleteFilesListener {
void onFileDeleteProgress(@NonNull UserFile file);
-
void onFilesDeleteDone(@NonNull Map errors);
}
public interface OnDownloadFileListener {
void onFileDownloadProgress(@NonNull UserFile userFile, int progress);
+
@WorkerThread
void onFileDownloadedAsync(@NonNull File file);
+ void onFileDownloaded(@NonNull File file);
void onFilesDownloadDone(@NonNull Map errors);
}
@@ -174,20 +171,22 @@ public class BackupHelper {
params.put("orderid", orderId);
}
params.put("deviceid", app.getUserAndroidId());
- AndroidNetworkUtils.sendRequestAsync(app, USER_REGISTER_URL, params, "Register user", true, true, new OnRequestResultListener() {
+ AndroidNetworkUtils.sendRequestAsync(app, USER_REGISTER_URL, params, "Register user", false, true, new OnRequestResultListener() {
@Override
- public void onResult(String resultJson) {
+ public void onResult(@Nullable String resultJson, @Nullable String error) {
int status;
String message;
- if (!Algorithms.isEmpty(resultJson)) {
+ if (!Algorithms.isEmpty(error)) {
+ message = "User registration error: " + parseServerError(error);
+ status = STATUS_SERVER_ERROR;
+ } else if (!Algorithms.isEmpty(resultJson)) {
try {
JSONObject result = new JSONObject(resultJson);
- String statusStr = result.getString("status");
- if (statusStr.equals("ok")) {
+ if (result.has("status") && "ok".equals(result.getString("status"))) {
message = "You have been registered successfully. Please check for email with activation code.";
status = STATUS_SUCCESS;
} else {
- message = "User registration error: " + statusStr;
+ message = "User registration error: unknown";
status = STATUS_SERVER_ERROR;
}
} catch (JSONException e) {
@@ -217,12 +216,15 @@ public class BackupHelper {
params.put("deviceid", androidId);
}
params.put("token", token);
- AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", true, true, new OnRequestResultListener() {
+ AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", false, true, new OnRequestResultListener() {
@Override
- public void onResult(String resultJson) {
+ public void onResult(@Nullable String resultJson, @Nullable String error) {
int status;
String message;
- if (!Algorithms.isEmpty(resultJson)) {
+ if (!Algorithms.isEmpty(error)) {
+ message = "Device registration error: " + parseServerError(error);
+ status = STATUS_SERVER_ERROR;
+ } else if (!Algorithms.isEmpty(resultJson)) {
try {
JSONObject result = new JSONObject(resultJson);
settings.BACKUP_DEVICE_ID.set(result.getString("id"));
@@ -230,8 +232,9 @@ public class BackupHelper {
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"));
- status = STATUS_SUCCESS;
+
message = "Device have been registered successfully";
+ status = STATUS_SUCCESS;
} catch (JSONException e) {
message = "Device registration error: json parsing";
status = STATUS_PARSE_JSON_ERROR;
@@ -271,14 +274,6 @@ public class BackupHelper {
additionaParams.put("name", gpxFileInfo.getFileName(true));
additionaParams.put("type", Algorithms.getFileExtension(file));
gpxFileInfo.uploadTime = System.currentTimeMillis();
- if (file.equals(favoritesFile)) {
- favouritesHelper.setLastUploadedTime(gpxFileInfo.uploadTime);
- } else {
- GpxDataItem gpxItem = gpxHelper.getItem(file);
- if (gpxItem != null) {
- gpxHelper.updateLastUploadedTime(gpxItem, gpxFileInfo.uploadTime);
- }
- }
additionaParams.put("clienttime", String.valueOf(gpxFileInfo.uploadTime));
}
return additionaParams;
@@ -291,13 +286,31 @@ public class BackupHelper {
}
}
+ @Override
+ public void onFileUploadDone(@NonNull File file) {
+ if (listener != null) {
+ GpxFileInfo gpxFileInfo = gpxInfos.get(file);
+ if (gpxFileInfo != null) {
+ if (file.equals(favoritesFile)) {
+ favouritesHelper.setLastUploadedTime(gpxFileInfo.uploadTime);
+ } else {
+ GpxDataItem gpxItem = gpxHelper.getItem(file);
+ if (gpxItem != null) {
+ gpxHelper.updateLastUploadedTime(gpxItem, gpxFileInfo.uploadTime);
+ }
+ }
+ }
+ listener.onFileUploadDone(file);
+ }
+ }
+
@Override
public void onFilesUploadDone(@NonNull Map errors) {
if (errors.isEmpty()) {
settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1);
}
if (listener != null) {
- listener.onFilesUploadDone(errors);
+ listener.onFilesUploadDone(resolveServerErrors(errors));
}
}
}, EXECUTOR);
@@ -338,17 +351,29 @@ public class BackupHelper {
for (RequestResponse response : results) {
UserFile userFile = filesMap.get(response.getRequest());
if (userFile != null) {
- String responseStr = response.getResponse();
boolean success;
- try {
- JSONObject json = new JSONObject(responseStr);
- String status = json.getString("status");
- success = status.equalsIgnoreCase("ok");
- } catch (JSONException e) {
+ String message = null;
+ String errorStr = response.getError();
+ if (!Algorithms.isEmpty(errorStr)) {
+ message = parseServerError(errorStr);
success = false;
+ } else {
+ String responseStr = response.getResponse();
+ try {
+ JSONObject result = new JSONObject(responseStr);
+ if (result.has("status") && "ok".equals(result.getString("status"))) {
+ success = true;
+ } else {
+ message = "Unknown error";
+ success = false;
+ }
+ } catch (JSONException e) {
+ message = "Json parsing error";
+ success = false;
+ }
}
if (!success) {
- errors.put(userFile, responseStr);
+ errors.put(userFile, message);
}
}
}
@@ -364,13 +389,16 @@ public class BackupHelper {
Map params = new HashMap<>();
params.put("deviceid", getDeviceId());
params.put("accessToken", getAccessToken());
- AndroidNetworkUtils.sendRequestAsync(app, LIST_FILES_URL, params, "Download file list", true, false, new OnRequestResultListener() {
+ AndroidNetworkUtils.sendRequestAsync(app, LIST_FILES_URL, params, "Download file list", false, false, new OnRequestResultListener() {
@Override
- public void onResult(String resultJson) {
+ public void onResult(@Nullable String resultJson, @Nullable String error) {
int status;
String message;
List userFiles = new ArrayList<>();
- if (!Algorithms.isEmpty(resultJson)) {
+ if (!Algorithms.isEmpty(error)) {
+ status = STATUS_SERVER_ERROR;
+ message = "Download file list error: " + parseServerError(error);
+ } else if (!Algorithms.isEmpty(resultJson)) {
try {
JSONObject result = new JSONObject(resultJson);
String totalZipSize = result.getString("totalZipSize");
@@ -425,6 +453,13 @@ public class BackupHelper {
}
}
+ @Override
+ public void onFileDownloadDone(@NonNull File file) {
+ if (listener != null) {
+ listener.onFileDownloaded(file);
+ }
+ }
+
@Override
public void onFileDownloadedAsync(@NonNull File file) {
if (listener != null) {
@@ -435,7 +470,7 @@ public class BackupHelper {
@Override
public void onFilesDownloadDone(@NonNull Map errors) {
if (listener != null) {
- listener.onFilesDownloadDone(errors);
+ listener.onFilesDownloadDone(resolveServerErrors(errors));
}
}
}, EXECUTOR);
@@ -523,6 +558,36 @@ public class BackupHelper {
task.executeOnExecutor(EXECUTOR);
}
+ private Map resolveServerErrors(@NonNull Map errors) {
+ Map resolvedErrors = new HashMap<>();
+ for (Entry fileError : errors.entrySet()) {
+ File file = fileError.getKey();
+ String errorStr = fileError.getValue();
+ try {
+ JSONObject errorJson = new JSONObject(errorStr);
+ JSONObject error = errorJson.getJSONObject("error");
+ errorStr = "Error " + error.getInt("errorCode") + " (" + error.getString("message") + ")";
+ } catch (JSONException e) {
+ // ignore
+ }
+ resolvedErrors.put(file, errorStr);
+ }
+ return resolvedErrors;
+ }
+
+ private String parseServerError(@NonNull String error) {
+ try {
+ JSONObject resultError = new JSONObject(error);
+ if (resultError.has("error")) {
+ JSONObject errorObj = resultError.getJSONObject("error");
+ return errorObj.getInt("errorCode") + " (" + errorObj.getString("message") + ")";
+ }
+ } catch (JSONException e) {
+ // ignore
+ }
+ return error;
+ }
+
@SuppressLint("StaticFieldLeak")
public void generateBackupInfo(@NonNull final List localFiles, @NonNull final List remoteFiles,
@Nullable final OnGenerateBackupInfoListener listener) {
diff --git a/OsmAnd/src/net/osmand/plus/backup/BackupTask.java b/OsmAnd/src/net/osmand/plus/backup/BackupTask.java
index 27a698f73d..ed8313a9ac 100644
--- a/OsmAnd/src/net/osmand/plus/backup/BackupTask.java
+++ b/OsmAnd/src/net/osmand/plus/backup/BackupTask.java
@@ -179,6 +179,11 @@ public class BackupTask {
}
}
+ @Override
+ public void onFileUploadDone(@NonNull File file) {
+ onTaskProgressDone();
+ }
+
@Override
public void onFilesUploadDone(@NonNull Map errors) {
uploadErrors = errors;
@@ -223,6 +228,11 @@ public class BackupTask {
}
}
+ @Override
+ public void onFileDownloaded(@NonNull File file) {
+ onTaskProgressDone();
+ }
+
@Override
public void onFileDownloadedAsync(@NonNull File file) {
UserFile userFile = filesMap.get(file);
@@ -299,7 +309,7 @@ public class BackupTask {
progress.startTask((String) objects[0], -1);
} else if (objects[0] instanceof Integer) {
int progressValue = (Integer) objects[0];
- if (progressValue < Integer.MAX_VALUE) {
+ if (progressValue >= 0) {
progress.progress(progressValue);
} else {
progress.finishTask();
@@ -312,6 +322,13 @@ public class BackupTask {
}
}
+ private void onTaskProgressDone() {
+ Context ctx = contextRef.get();
+ if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
+ progress.finishTask();
+ }
+ }
+
private void onError(@NonNull String message) {
this.error = message;
runningTasks.clear();
diff --git a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java
index 8f78c63151..54057adb72 100644
--- a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java
+++ b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java
@@ -137,6 +137,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
a.buttonVerify.setVisibility(View.VISIBLE);
a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS);
a.tokenEditText.requestFocus();
+ a.infoView.setText(message);
}
}
});
@@ -162,10 +163,11 @@ public class TestBackupActivity extends OsmandActionBarActivity {
a.progressBar.setVisibility(View.GONE);
a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS);
if (status == BackupHelper.STATUS_SUCCESS) {
- tokenEdit.setVisibility(View.GONE);
- buttonVerify.setVisibility(View.GONE);
+ a.tokenEdit.setVisibility(View.GONE);
+ a.buttonVerify.setVisibility(View.GONE);
+ a.prepareBackup();
}
- a.prepareBackup();
+ a.infoView.setText(message);
}
}
});
@@ -196,15 +198,19 @@ public class TestBackupActivity extends OsmandActionBarActivity {
String description;
if (error != null) {
description = error;
- } else if (uploadErrors == null && downloadErrors == null) {
+ } else if (uploadErrors == null && deleteErrors == null) {
description = "No data";
} else {
description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error);
}
a.infoView.setText(description);
a.infoView.requestFocus();
- a.prepareBackup();
a.buttonBackup.setEnabled(true);
+ if (Algorithms.isEmpty(description)) {
+ a.prepareBackup();
+ } else {
+ a.backupInfo = null;
+ }
}
}
});
@@ -233,8 +239,12 @@ public class TestBackupActivity extends OsmandActionBarActivity {
}
a.infoView.setText(description);
a.infoView.requestFocus();
- a.prepareBackup();
a.buttonRestore.setEnabled(true);
+ if (Algorithms.isEmpty(description)) {
+ a.prepareBackup();
+ } else {
+ a.backupInfo = null;
+ }
}
}
});
@@ -249,21 +259,21 @@ public class TestBackupActivity extends OsmandActionBarActivity {
private String getBackupErrorsDescription(@Nullable Map uploadErrors, @Nullable Map downloadErrors, @Nullable Map deleteErrors, @Nullable String error) {
StringBuilder sb = new StringBuilder();
if (!Algorithms.isEmpty(uploadErrors)) {
- sb.append("--- Upload errors ---").append("\n");
+ sb.append("--- Upload errors ---").append("\n\n");
for (Entry uploadEntry : uploadErrors.entrySet()) {
- sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n");
+ sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n\n");
}
}
if (!Algorithms.isEmpty(downloadErrors)) {
- sb.append("--- Download errors ---").append("\n");
+ sb.append("--- Download errors ---").append("\n\n");
for (Entry downloadEntry : downloadErrors.entrySet()) {
- sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n");
+ sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n\n");
}
}
if (!Algorithms.isEmpty(deleteErrors)) {
- sb.append("--- Delete errors ---").append("\n");
+ sb.append("--- Delete errors ---").append("\n\n");
for (Entry deleteEntry : deleteErrors.entrySet()) {
- sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n");
+ sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n\n");
}
}
return sb.length() == 0 ? "OK" : sb.toString();
@@ -272,32 +282,32 @@ public class TestBackupActivity extends OsmandActionBarActivity {
private String getBackupDescription(@NonNull BackupInfo backupInfo) {
StringBuilder sb = new StringBuilder();
if (!Algorithms.isEmpty(backupInfo.filesToUpload)) {
- sb.append("\n").append("--- Upload ---").append("\n");
+ sb.append("\n").append("--- Upload ---").append("\n\n");
for (GpxFileInfo info : backupInfo.filesToUpload) {
sb.append(info.getFileName(true))
.append(" L: ").append(DF.format(new Date(info.getFileDate())))
.append(" U: ").append(DF.format(new Date(info.uploadTime)))
- .append("\n");
+ .append("\n\n");
}
}
if (!Algorithms.isEmpty(backupInfo.filesToDownload)) {
- sb.append("\n").append("--- Download ---").append("\n");
+ sb.append("\n").append("--- Download ---").append("\n\n");
for (UserFile userFile : backupInfo.filesToDownload) {
sb.append(userFile.getName())
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
- .append("\n");
+ .append("\n\n");
}
}
if (!Algorithms.isEmpty(backupInfo.filesToDelete)) {
- sb.append("\n").append("--- Delete ---").append("\n");
+ sb.append("\n").append("--- Delete ---").append("\n\n");
for (UserFile userFile : backupInfo.filesToDelete) {
sb.append(userFile.getName())
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
- .append("\n");
+ .append("\n\n");
}
}
if (!Algorithms.isEmpty(backupInfo.filesToMerge)) {
- sb.append("\n").append("--- Conflicts ---").append("\n");
+ sb.append("\n").append("--- Conflicts ---").append("\n\n");
for (Pair localRemote : backupInfo.filesToMerge) {
GpxFileInfo local = localRemote.first;
UserFile remote = localRemote.second;
@@ -305,7 +315,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
.append(" L: ").append(DF.format(new Date(local.getFileDate())))
.append(" U: ").append(DF.format(new Date(local.uploadTime)))
.append(" R: ").append(DF.format(new Date(remote.getClienttimems())))
- .append("\n");
+ .append("\n\n");
}
}
return sb.toString();
diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java
index 8a54df1b86..a9d53da11e 100644
--- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java
+++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java
@@ -8,6 +8,9 @@ import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import net.osmand.AndroidNetworkUtils;
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
import net.osmand.AndroidNetworkUtils.OnSendRequestsListener;
@@ -41,9 +44,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
public abstract class InAppPurchaseHelper {
// Debug tag, for logging
protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class);
@@ -466,7 +466,7 @@ public abstract class InAppPurchaseHelper {
protected void onSkuDetailsResponseDone(List purchaseInfoList) {
final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() {
@Override
- public void onResult(String result) {
+ public void onResult(@Nullable String result, @Nullable String error) {
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
notifyGetItems();
stop(true);
@@ -477,7 +477,7 @@ public abstract class InAppPurchaseHelper {
if (purchaseInfoList.size() > 0) {
sendTokens(purchaseInfoList, listener);
} else {
- listener.onResult("OK");
+ listener.onResult("OK", null);
}
}
@@ -503,7 +503,7 @@ public abstract class InAppPurchaseHelper {
liveUpdatesPurchase.setState(ctx, SubscriptionState.UNDEFINED);
sendTokens(Collections.singletonList(info), new OnRequestResultListener() {
@Override
- public void onResult(String result) {
+ public void onResult(@Nullable String result, @Nullable String error) {
boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get();
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true);
@@ -642,7 +642,7 @@ public abstract class InAppPurchaseHelper {
}
}
if (listener != null) {
- listener.onResult("OK");
+ listener.onResult("OK", null);
}
}
@@ -695,7 +695,7 @@ public abstract class InAppPurchaseHelper {
} catch (Exception e) {
logError("SendToken Error", e);
if (listener != null) {
- listener.onResult("Error");
+ listener.onResult("Error", null);
}
}
}
diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java b/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java
index 9729cd9b3a..1386101792 100644
--- a/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java
+++ b/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java
@@ -6,6 +6,7 @@ import android.content.Context;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import net.osmand.AndroidNetworkUtils;
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
@@ -194,7 +195,7 @@ public class PerformLiveUpdateAsyncTask
AndroidNetworkUtils.sendRequestAsync(
app, LiveUpdatesFragment.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() {
@Override
- public void onResult(String result) {
+ public void onResult(@Nullable String result, @Nullable String error) {
if (!Algorithms.isEmpty(result)) {
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
source.setTimeZone(TimeZone.getTimeZone("UTC"));
diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java
index ba8990f64f..099a23282d 100644
--- a/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java
+++ b/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java
@@ -208,7 +208,7 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
"https://osmand.net/subscription/update",
parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override
- public void onResult(String result) {
+ public void onResult(@Nullable String result, @Nullable String error) {
dismissProgress(null);
OsmandApplication app = getMyApplication();
if (result != null) {
diff --git a/OsmAnd/src/net/osmand/plus/search/SendSearchQueryBottomSheet.java b/OsmAnd/src/net/osmand/plus/search/SendSearchQueryBottomSheet.java
index 39e91503ec..375cfffd20 100644
--- a/OsmAnd/src/net/osmand/plus/search/SendSearchQueryBottomSheet.java
+++ b/OsmAnd/src/net/osmand/plus/search/SendSearchQueryBottomSheet.java
@@ -6,6 +6,8 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import net.osmand.AndroidNetworkUtils;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
@@ -71,7 +73,7 @@ public class SendSearchQueryBottomSheet extends MenuBottomSheetDialogFragment {
AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/api/missing_search", params,
null, true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override
- public void onResult(String result) {
+ public void onResult(@Nullable String result, @Nullable String error) {
if (result != null && isAdded()) {
try {
JSONObject obj = new JSONObject(result);