Fix backup error handling

This commit is contained in:
max-klaus 2021-04-23 20:40:06 +03:00
parent 3ce4082997
commit 9d39e2f805
9 changed files with 322 additions and 147 deletions

View file

@ -48,13 +48,14 @@ public class AndroidNetworkUtils {
private static final Log LOG = PlatformUtil.getLog(AndroidNetworkUtils.class); private static final Log LOG = PlatformUtil.getLog(AndroidNetworkUtils.class);
public interface OnRequestResultListener { public interface OnRequestResultListener {
void onResult(String result); void onResult(@Nullable String result, @Nullable String error);
} }
public interface OnFilesUploadCallback { public interface OnFilesUploadCallback {
@Nullable @Nullable
Map<String, String> getAdditionalParams(@NonNull File file); Map<String, String> getAdditionalParams(@NonNull File file);
void onFileUploadProgress(@NonNull File file, int percent); void onFileUploadProgress(@NonNull File file, int percent);
void onFileUploadDone(@NonNull File file);
void onFilesUploadDone(@NonNull Map<File, String> errors); void onFilesUploadDone(@NonNull Map<File, String> errors);
} }
@ -64,16 +65,26 @@ public class AndroidNetworkUtils {
void onFileDownloadProgress(@NonNull File file, int percent); void onFileDownloadProgress(@NonNull File file, int percent);
@WorkerThread @WorkerThread
void onFileDownloadedAsync(@NonNull File file); void onFileDownloadedAsync(@NonNull File file);
void onFileDownloadDone(@NonNull File file);
void onFilesDownloadDone(@NonNull Map<File, String> errors); void onFilesDownloadDone(@NonNull Map<File, String> errors);
} }
public static class RequestResponse { public static class RequestResponse {
private Request request; private final Request request;
private String response; private final String response;
private final String error;
RequestResponse(@NonNull Request request, @Nullable String response) { RequestResponse(@NonNull Request request, @Nullable String response) {
this.request = request; this.request = request;
this.response = response; 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() { public Request getRequest() {
@ -83,6 +94,10 @@ public class AndroidNetworkUtils {
public String getResponse() { public String getResponse() {
return response; return response;
} }
public String getError() {
return error;
}
} }
public interface OnSendRequestsListener { public interface OnSendRequestsListener {
@ -109,11 +124,18 @@ public class AndroidNetworkUtils {
for (Request request : requests) { for (Request request : requests) {
RequestResponse requestResponse; RequestResponse requestResponse;
try { try {
String response = sendRequest(ctx, request.getUrl(), request.getParameters(), final String[] response = {null, null};
request.getUserOperation(), request.isToastAllowed(), request.isPost()); sendRequest(ctx, request.getUrl(), request.getParameters(),
requestResponse = new RequestResponse(request, response); 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) { } catch (Exception e) {
requestResponse = new RequestResponse(request, null); requestResponse = new RequestResponse(request, null, "Unexpected error");
} }
responses.add(requestResponse); responses.add(requestResponse);
publishProgress(requestResponse); publishProgress(requestResponse);
@ -157,21 +179,29 @@ public class AndroidNetworkUtils {
final boolean post, final boolean post,
final OnRequestResultListener listener, final OnRequestResultListener listener,
final Executor executor) { final Executor executor) {
new AsyncTask<Void, Void, String>() { new AsyncTask<Void, Void, String[]>() {
@Override @Override
protected String doInBackground(Void... params) { protected String[] doInBackground(Void... params) {
final String[] res = {null, null};
try { try {
return sendRequest(ctx, url, parameters, userOperation, toastAllowed, post); sendRequest(ctx, url, parameters, userOperation, toastAllowed, post, new OnRequestResultListener() {
} catch (Exception e) { @Override
return null; public void onResult(@Nullable String result, @Nullable String error) {
res[0] = result;
res[1] = error;
} }
});
} catch (Exception e) {
// ignore
}
return res;
} }
@Override @Override
protected void onPostExecute(String response) { protected void onPostExecute(String[] response) {
if (listener != null) { if (listener != null) {
listener.onResult(response); listener.onResult(response[0], response[1]);
} }
} }
@ -255,7 +285,7 @@ public class AndroidNetworkUtils {
} catch (Exception e) { } catch (Exception e) {
errors.put(file, e.getMessage()); errors.put(file, e.getMessage());
} }
publishProgress(file, Integer.MAX_VALUE); publishProgress(file, -1);
} }
return errors; return errors;
} }
@ -263,7 +293,13 @@ public class AndroidNetworkUtils {
@Override @Override
protected void onProgressUpdate(Object... objects) { protected void onProgressUpdate(Object... objects) {
if (callback != null) { 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, public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
@Nullable Map<String, String> parameters, @Nullable Map<String, String> parameters,
@Nullable String userOperation, boolean toastAllowed, boolean post) { @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<String, String> parameters,
@Nullable String userOperation, boolean toastAllowed, boolean post,
@Nullable OnRequestResultListener listener) {
String result = null;
String error = null;
HttpURLConnection connection = null; HttpURLConnection connection = null;
try { try {
String params = null; String params = null;
if (parameters != null && parameters.size() > 0) { if (parameters != null && parameters.size() > 0) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -312,68 +356,66 @@ public class AndroidNetworkUtils {
output.write(params.getBytes("UTF-8")); output.write(params.getBytes("UTF-8"));
output.flush(); output.flush();
output.close(); output.close();
} else { } else {
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
connection.connect(); connection.connect();
} }
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { 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) { if (toastAllowed && ctx != null) {
String msg = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "") showToast(ctx, error);
+ ctx.getString(R.string.failed_op) + ": " }
+ connection.getResponseMessage(); InputStream errorStream = connection.getErrorStream();
showToast(ctx, msg); if (errorStream != null) {
error = streamToString(errorStream);
} }
} else { } else {
StringBuilder responseBody = new StringBuilder(); result = streamToString(connection.getInputStream());
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();
}
} catch (NullPointerException e) { } catch (NullPointerException e) {
// that's tricky case why NPE is thrown to fix that problem httpClient could be used // 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) { if (toastAllowed && ctx != null) {
String msg = ctx.getString(R.string.auth_failed); showToast(ctx, error);
showToast(ctx, msg);
} }
} catch (MalformedURLException e) { } 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) { if (toastAllowed && ctx != null) {
showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template) showToast(ctx, error);
+ ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation));
} }
} catch (IOException e) { } 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) { if (toastAllowed && ctx != null) {
showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template) showToast(ctx, error);
+ ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation));
} }
} finally { } finally {
if (connection != null) { if (connection != null) {
connection.disconnect(); connection.disconnect();
} }
} }
if (listener != null) {
listener.onResult(result, error);
}
return null; return null;
} }
@ -401,12 +443,16 @@ public class AndroidNetworkUtils {
public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) { public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) {
String error = null; String error = null;
try { try {
URLConnection connection = NetworkUtils.getHttpURLConnection(url); HttpURLConnection connection = NetworkUtils.getHttpURLConnection(url);
connection.setConnectTimeout(CONNECTION_TIMEOUT); connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setReadTimeout(CONNECTION_TIMEOUT); connection.setReadTimeout(CONNECTION_TIMEOUT);
if (gzip) { if (gzip) {
connection.setRequestProperty("Accept-Encoding", "deflate, gzip"); connection.setRequestProperty("Accept-Encoding", "deflate, gzip");
} }
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return streamToString(connection.getErrorStream());
} else {
InputStream inputStream = gzip InputStream inputStream = gzip
? new GZIPInputStream(connection.getInputStream()) ? new GZIPInputStream(connection.getInputStream())
: new BufferedInputStream(connection.getInputStream(), 8 * 1024); : new BufferedInputStream(connection.getInputStream(), 8 * 1024);
@ -420,16 +466,41 @@ public class AndroidNetworkUtils {
Algorithms.closeStream(inputStream); Algorithms.closeStream(inputStream);
Algorithms.closeStream(stream); Algorithms.closeStream(stream);
} }
}
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
error = e.getMessage(); error = e.getMessage();
LOG.error("UnknownHostException, cannot download file " + url + " " + error); LOG.error("UnknownHostException, cannot download file " + url + " " + error);
} catch (Exception e) { } catch (Exception e) {
error = e.getMessage(); error = e.getMessage();
LOG.warn("Cannot download file : " + url, e); LOG.warn("Cannot download file: " + url, e);
} }
return error; 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"; private static final String BOUNDARY = "CowMooCowMooCowCowCow";
public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip, public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip,
@ -444,7 +515,6 @@ public class AndroidNetworkUtils {
@NonNull Map<String, String> additionalParams, @NonNull Map<String, String> additionalParams,
@Nullable Map<String, String> headers, @Nullable Map<String, String> headers,
@Nullable IProgress progress) { @Nullable IProgress progress) {
URL url;
try { try {
boolean firstPrm = !urlText.contains("?"); boolean firstPrm = !urlText.contains("?");
StringBuilder sb = new StringBuilder(urlText); StringBuilder sb = new StringBuilder(urlText);
@ -455,7 +525,7 @@ public class AndroidNetworkUtils {
urlText = sb.toString(); urlText = sb.toString();
LOG.info("Start uploading file to " + urlText + " " + fileName); LOG.info("Start uploading file to " + urlText + " " + fileName);
url = new URL(urlText); URL url = new URL(urlText);
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true); conn.setDoInput(true);
@ -496,6 +566,10 @@ public class AndroidNetworkUtils {
LOG.info("Finish uploading file " + fileName); LOG.info("Finish uploading file " + fileName);
LOG.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage()); LOG.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage());
if (conn.getResponseCode() != 200) { if (conn.getResponseCode() != 200) {
InputStream errorStream = conn.getErrorStream();
if (errorStream != null) {
return streamToString(errorStream);
}
return conn.getResponseMessage(); return conn.getResponseMessage();
} }
InputStream is = conn.getInputStream(); InputStream is = conn.getInputStream();
@ -575,7 +649,7 @@ public class AndroidNetworkUtils {
} catch (Exception e) { } catch (Exception e) {
errors.put(file, e.getMessage()); errors.put(file, e.getMessage());
} }
publishProgress(file, Integer.MAX_VALUE); publishProgress(file, -1);
} }
return errors; return errors;
} }
@ -583,7 +657,13 @@ public class AndroidNetworkUtils {
@Override @Override
protected void onProgressUpdate(Object... objects) { protected void onProgressUpdate(Object... objects) {
if (callback != null) { 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 { public static class Request {
private String url; private final String url;
private Map<String, String> parameters; private final Map<String, String> parameters;
private String userOperation; private final String userOperation;
private boolean toastAllowed; private final boolean toastAllowed;
private boolean post; private final boolean post;
public Request(String url, Map<String, String> parameters, String userOperation, boolean toastAllowed, boolean post) { public Request(String url, Map<String, String> parameters, String userOperation, boolean toastAllowed, boolean post) {
this.url = url; this.url = url;

View file

@ -225,7 +225,7 @@ public class CustomRegion extends WorldRegion {
&& app.getSettings().isInternetConnectionAvailable()) { && app.getSettings().isInternetConnectionAvailable()) {
OnRequestResultListener resultListener = new OnRequestResultListener() { OnRequestResultListener resultListener = new OnRequestResultListener() {
@Override @Override
public void onResult(String result) { public void onResult(@Nullable String result, @Nullable String error) {
if (!Algorithms.isEmpty(result)) { if (!Algorithms.isEmpty(result)) {
if ("json".equalsIgnoreCase(dynamicDownloadItems.format)) { if ("json".equalsIgnoreCase(dynamicDownloadItems.format)) {
dynamicItemsJson = mapJsonItems(result); dynamicItemsJson = mapJsonItems(result);

View file

@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; 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_EMPTY_RESPONSE_ERROR = 2;
public final static int STATUS_SERVER_ERROR = 3; public final static int STATUS_SERVER_ERROR = 3;
public interface OnResultListener {
void onResult(int status, @Nullable String message, @Nullable JSONObject json);
}
public interface OnRegisterUserListener { public interface OnRegisterUserListener {
void onRegisterUser(int status, @Nullable String message); void onRegisterUser(int status, @Nullable String message);
} }
@ -83,7 +80,6 @@ public class BackupHelper {
public interface OnCollectLocalFilesListener { public interface OnCollectLocalFilesListener {
void onFileCollected(@NonNull GpxFileInfo fileInfo); void onFileCollected(@NonNull GpxFileInfo fileInfo);
void onFilesCollected(@NonNull List<GpxFileInfo> fileInfos); void onFilesCollected(@NonNull List<GpxFileInfo> fileInfos);
} }
@ -93,20 +89,21 @@ public class BackupHelper {
public interface OnUploadFilesListener { public interface OnUploadFilesListener {
void onFileUploadProgress(@NonNull File file, int progress); void onFileUploadProgress(@NonNull File file, int progress);
void onFileUploadDone(@NonNull File file);
void onFilesUploadDone(@NonNull Map<File, String> errors); void onFilesUploadDone(@NonNull Map<File, String> errors);
} }
public interface OnDeleteFilesListener { public interface OnDeleteFilesListener {
void onFileDeleteProgress(@NonNull UserFile file); void onFileDeleteProgress(@NonNull UserFile file);
void onFilesDeleteDone(@NonNull Map<UserFile, String> errors); void onFilesDeleteDone(@NonNull Map<UserFile, String> errors);
} }
public interface OnDownloadFileListener { public interface OnDownloadFileListener {
void onFileDownloadProgress(@NonNull UserFile userFile, int progress); void onFileDownloadProgress(@NonNull UserFile userFile, int progress);
@WorkerThread @WorkerThread
void onFileDownloadedAsync(@NonNull File file); void onFileDownloadedAsync(@NonNull File file);
void onFileDownloaded(@NonNull File file);
void onFilesDownloadDone(@NonNull Map<File, String> errors); void onFilesDownloadDone(@NonNull Map<File, String> errors);
} }
@ -174,20 +171,22 @@ public class BackupHelper {
params.put("orderid", orderId); params.put("orderid", orderId);
} }
params.put("deviceid", app.getUserAndroidId()); 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 @Override
public void onResult(String resultJson) { public void onResult(@Nullable String resultJson, @Nullable String error) {
int status; int status;
String message; 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 { try {
JSONObject result = new JSONObject(resultJson); JSONObject result = new JSONObject(resultJson);
String statusStr = result.getString("status"); if (result.has("status") && "ok".equals(result.getString("status"))) {
if (statusStr.equals("ok")) {
message = "You have been registered successfully. Please check for email with activation code."; message = "You have been registered successfully. Please check for email with activation code.";
status = STATUS_SUCCESS; status = STATUS_SUCCESS;
} else { } else {
message = "User registration error: " + statusStr; message = "User registration error: unknown";
status = STATUS_SERVER_ERROR; status = STATUS_SERVER_ERROR;
} }
} catch (JSONException e) { } catch (JSONException e) {
@ -217,12 +216,15 @@ public class BackupHelper {
params.put("deviceid", androidId); params.put("deviceid", androidId);
} }
params.put("token", token); 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 @Override
public void onResult(String resultJson) { public void onResult(@Nullable String resultJson, @Nullable String error) {
int status; int status;
String message; 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 { try {
JSONObject result = new JSONObject(resultJson); JSONObject result = new JSONObject(resultJson);
settings.BACKUP_DEVICE_ID.set(result.getString("id")); 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_NATIVE_DEVICE_ID.set(result.getString("deviceid"));
settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken")); settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken"));
settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime")); settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime"));
status = STATUS_SUCCESS;
message = "Device have been registered successfully"; message = "Device have been registered successfully";
status = STATUS_SUCCESS;
} catch (JSONException e) { } catch (JSONException e) {
message = "Device registration error: json parsing"; message = "Device registration error: json parsing";
status = STATUS_PARSE_JSON_ERROR; status = STATUS_PARSE_JSON_ERROR;
@ -271,14 +274,6 @@ public class BackupHelper {
additionaParams.put("name", gpxFileInfo.getFileName(true)); additionaParams.put("name", gpxFileInfo.getFileName(true));
additionaParams.put("type", Algorithms.getFileExtension(file)); additionaParams.put("type", Algorithms.getFileExtension(file));
gpxFileInfo.uploadTime = System.currentTimeMillis(); 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)); additionaParams.put("clienttime", String.valueOf(gpxFileInfo.uploadTime));
} }
return additionaParams; 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 @Override
public void onFilesUploadDone(@NonNull Map<File, String> errors) { public void onFilesUploadDone(@NonNull Map<File, String> errors) {
if (errors.isEmpty()) { if (errors.isEmpty()) {
settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1); settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1);
} }
if (listener != null) { if (listener != null) {
listener.onFilesUploadDone(errors); listener.onFilesUploadDone(resolveServerErrors(errors));
} }
} }
}, EXECUTOR); }, EXECUTOR);
@ -338,17 +351,29 @@ public class BackupHelper {
for (RequestResponse response : results) { for (RequestResponse response : results) {
UserFile userFile = filesMap.get(response.getRequest()); UserFile userFile = filesMap.get(response.getRequest());
if (userFile != null) { if (userFile != null) {
String responseStr = response.getResponse();
boolean success; boolean success;
String message = null;
String errorStr = response.getError();
if (!Algorithms.isEmpty(errorStr)) {
message = parseServerError(errorStr);
success = false;
} else {
String responseStr = response.getResponse();
try { try {
JSONObject json = new JSONObject(responseStr); JSONObject result = new JSONObject(responseStr);
String status = json.getString("status"); if (result.has("status") && "ok".equals(result.getString("status"))) {
success = status.equalsIgnoreCase("ok"); success = true;
} catch (JSONException e) { } else {
message = "Unknown error";
success = false; success = false;
} }
} catch (JSONException e) {
message = "Json parsing error";
success = false;
}
}
if (!success) { if (!success) {
errors.put(userFile, responseStr); errors.put(userFile, message);
} }
} }
} }
@ -364,13 +389,16 @@ public class BackupHelper {
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
params.put("deviceid", getDeviceId()); params.put("deviceid", getDeviceId());
params.put("accessToken", getAccessToken()); 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 @Override
public void onResult(String resultJson) { public void onResult(@Nullable String resultJson, @Nullable String error) {
int status; int status;
String message; String message;
List<UserFile> userFiles = new ArrayList<>(); List<UserFile> 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 { try {
JSONObject result = new JSONObject(resultJson); JSONObject result = new JSONObject(resultJson);
String totalZipSize = result.getString("totalZipSize"); 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 @Override
public void onFileDownloadedAsync(@NonNull File file) { public void onFileDownloadedAsync(@NonNull File file) {
if (listener != null) { if (listener != null) {
@ -435,7 +470,7 @@ public class BackupHelper {
@Override @Override
public void onFilesDownloadDone(@NonNull Map<File, String> errors) { public void onFilesDownloadDone(@NonNull Map<File, String> errors) {
if (listener != null) { if (listener != null) {
listener.onFilesDownloadDone(errors); listener.onFilesDownloadDone(resolveServerErrors(errors));
} }
} }
}, EXECUTOR); }, EXECUTOR);
@ -523,6 +558,36 @@ public class BackupHelper {
task.executeOnExecutor(EXECUTOR); task.executeOnExecutor(EXECUTOR);
} }
private Map<File, String> resolveServerErrors(@NonNull Map<File, String> errors) {
Map<File, String> resolvedErrors = new HashMap<>();
for (Entry<File, String> 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") @SuppressLint("StaticFieldLeak")
public void generateBackupInfo(@NonNull final List<GpxFileInfo> localFiles, @NonNull final List<UserFile> remoteFiles, public void generateBackupInfo(@NonNull final List<GpxFileInfo> localFiles, @NonNull final List<UserFile> remoteFiles,
@Nullable final OnGenerateBackupInfoListener listener) { @Nullable final OnGenerateBackupInfoListener listener) {

View file

@ -179,6 +179,11 @@ public class BackupTask {
} }
} }
@Override
public void onFileUploadDone(@NonNull File file) {
onTaskProgressDone();
}
@Override @Override
public void onFilesUploadDone(@NonNull Map<File, String> errors) { public void onFilesUploadDone(@NonNull Map<File, String> errors) {
uploadErrors = errors; uploadErrors = errors;
@ -223,6 +228,11 @@ public class BackupTask {
} }
} }
@Override
public void onFileDownloaded(@NonNull File file) {
onTaskProgressDone();
}
@Override @Override
public void onFileDownloadedAsync(@NonNull File file) { public void onFileDownloadedAsync(@NonNull File file) {
UserFile userFile = filesMap.get(file); UserFile userFile = filesMap.get(file);
@ -299,7 +309,7 @@ public class BackupTask {
progress.startTask((String) objects[0], -1); progress.startTask((String) objects[0], -1);
} else if (objects[0] instanceof Integer) { } else if (objects[0] instanceof Integer) {
int progressValue = (Integer) objects[0]; int progressValue = (Integer) objects[0];
if (progressValue < Integer.MAX_VALUE) { if (progressValue >= 0) {
progress.progress(progressValue); progress.progress(progressValue);
} else { } else {
progress.finishTask(); 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) { private void onError(@NonNull String message) {
this.error = message; this.error = message;
runningTasks.clear(); runningTasks.clear();

View file

@ -137,6 +137,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
a.buttonVerify.setVisibility(View.VISIBLE); a.buttonVerify.setVisibility(View.VISIBLE);
a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS); a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS);
a.tokenEditText.requestFocus(); a.tokenEditText.requestFocus();
a.infoView.setText(message);
} }
} }
}); });
@ -162,11 +163,12 @@ public class TestBackupActivity extends OsmandActionBarActivity {
a.progressBar.setVisibility(View.GONE); a.progressBar.setVisibility(View.GONE);
a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS); a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS);
if (status == BackupHelper.STATUS_SUCCESS) { if (status == BackupHelper.STATUS_SUCCESS) {
tokenEdit.setVisibility(View.GONE); a.tokenEdit.setVisibility(View.GONE);
buttonVerify.setVisibility(View.GONE); a.buttonVerify.setVisibility(View.GONE);
}
a.prepareBackup(); a.prepareBackup();
} }
a.infoView.setText(message);
}
} }
}); });
} else { } else {
@ -196,15 +198,19 @@ public class TestBackupActivity extends OsmandActionBarActivity {
String description; String description;
if (error != null) { if (error != null) {
description = error; description = error;
} else if (uploadErrors == null && downloadErrors == null) { } else if (uploadErrors == null && deleteErrors == null) {
description = "No data"; description = "No data";
} else { } else {
description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error); description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error);
} }
a.infoView.setText(description); a.infoView.setText(description);
a.infoView.requestFocus(); a.infoView.requestFocus();
a.prepareBackup();
a.buttonBackup.setEnabled(true); 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.setText(description);
a.infoView.requestFocus(); a.infoView.requestFocus();
a.prepareBackup();
a.buttonRestore.setEnabled(true); 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<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors, @Nullable Map<UserFile, String> deleteErrors, @Nullable String error) { private String getBackupErrorsDescription(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors, @Nullable Map<UserFile, String> deleteErrors, @Nullable String error) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (!Algorithms.isEmpty(uploadErrors)) { if (!Algorithms.isEmpty(uploadErrors)) {
sb.append("--- Upload errors ---").append("\n"); sb.append("--- Upload errors ---").append("\n\n");
for (Entry<File, String> uploadEntry : uploadErrors.entrySet()) { for (Entry<File, String> 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)) { if (!Algorithms.isEmpty(downloadErrors)) {
sb.append("--- Download errors ---").append("\n"); sb.append("--- Download errors ---").append("\n\n");
for (Entry<File, String> downloadEntry : downloadErrors.entrySet()) { for (Entry<File, String> 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)) { if (!Algorithms.isEmpty(deleteErrors)) {
sb.append("--- Delete errors ---").append("\n"); sb.append("--- Delete errors ---").append("\n\n");
for (Entry<UserFile, String> deleteEntry : deleteErrors.entrySet()) { for (Entry<UserFile, String> 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(); return sb.length() == 0 ? "OK" : sb.toString();
@ -272,32 +282,32 @@ public class TestBackupActivity extends OsmandActionBarActivity {
private String getBackupDescription(@NonNull BackupInfo backupInfo) { private String getBackupDescription(@NonNull BackupInfo backupInfo) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (!Algorithms.isEmpty(backupInfo.filesToUpload)) { 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) { for (GpxFileInfo info : backupInfo.filesToUpload) {
sb.append(info.getFileName(true)) sb.append(info.getFileName(true))
.append(" L: ").append(DF.format(new Date(info.getFileDate()))) .append(" L: ").append(DF.format(new Date(info.getFileDate())))
.append(" U: ").append(DF.format(new Date(info.uploadTime))) .append(" U: ").append(DF.format(new Date(info.uploadTime)))
.append("\n"); .append("\n\n");
} }
} }
if (!Algorithms.isEmpty(backupInfo.filesToDownload)) { 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) { for (UserFile userFile : backupInfo.filesToDownload) {
sb.append(userFile.getName()) sb.append(userFile.getName())
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems()))) .append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
.append("\n"); .append("\n\n");
} }
} }
if (!Algorithms.isEmpty(backupInfo.filesToDelete)) { 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) { for (UserFile userFile : backupInfo.filesToDelete) {
sb.append(userFile.getName()) sb.append(userFile.getName())
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems()))) .append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
.append("\n"); .append("\n\n");
} }
} }
if (!Algorithms.isEmpty(backupInfo.filesToMerge)) { if (!Algorithms.isEmpty(backupInfo.filesToMerge)) {
sb.append("\n").append("--- Conflicts ---").append("\n"); sb.append("\n").append("--- Conflicts ---").append("\n\n");
for (Pair<GpxFileInfo, UserFile> localRemote : backupInfo.filesToMerge) { for (Pair<GpxFileInfo, UserFile> localRemote : backupInfo.filesToMerge) {
GpxFileInfo local = localRemote.first; GpxFileInfo local = localRemote.first;
UserFile remote = localRemote.second; UserFile remote = localRemote.second;
@ -305,7 +315,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
.append(" L: ").append(DF.format(new Date(local.getFileDate()))) .append(" L: ").append(DF.format(new Date(local.getFileDate())))
.append(" U: ").append(DF.format(new Date(local.uploadTime))) .append(" U: ").append(DF.format(new Date(local.uploadTime)))
.append(" R: ").append(DF.format(new Date(remote.getClienttimems()))) .append(" R: ").append(DF.format(new Date(remote.getClienttimems())))
.append("\n"); .append("\n\n");
} }
} }
return sb.toString(); return sb.toString();

View file

@ -8,6 +8,9 @@ import android.os.AsyncTask;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils;
import net.osmand.AndroidNetworkUtils.OnRequestResultListener; import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
import net.osmand.AndroidNetworkUtils.OnSendRequestsListener; import net.osmand.AndroidNetworkUtils.OnSendRequestsListener;
@ -41,9 +44,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public abstract class InAppPurchaseHelper { public abstract class InAppPurchaseHelper {
// Debug tag, for logging // Debug tag, for logging
protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class); protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class);
@ -466,7 +466,7 @@ public abstract class InAppPurchaseHelper {
protected void onSkuDetailsResponseDone(List<PurchaseInfo> purchaseInfoList) { protected void onSkuDetailsResponseDone(List<PurchaseInfo> purchaseInfoList) {
final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() { final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() {
@Override @Override
public void onResult(String result) { public void onResult(@Nullable String result, @Nullable String error) {
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY); notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
notifyGetItems(); notifyGetItems();
stop(true); stop(true);
@ -477,7 +477,7 @@ public abstract class InAppPurchaseHelper {
if (purchaseInfoList.size() > 0) { if (purchaseInfoList.size() > 0) {
sendTokens(purchaseInfoList, listener); sendTokens(purchaseInfoList, listener);
} else { } else {
listener.onResult("OK"); listener.onResult("OK", null);
} }
} }
@ -503,7 +503,7 @@ public abstract class InAppPurchaseHelper {
liveUpdatesPurchase.setState(ctx, SubscriptionState.UNDEFINED); liveUpdatesPurchase.setState(ctx, SubscriptionState.UNDEFINED);
sendTokens(Collections.singletonList(info), new OnRequestResultListener() { sendTokens(Collections.singletonList(info), new OnRequestResultListener() {
@Override @Override
public void onResult(String result) { public void onResult(@Nullable String result, @Nullable String error) {
boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get(); boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get();
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true); ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true); ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true);
@ -642,7 +642,7 @@ public abstract class InAppPurchaseHelper {
} }
} }
if (listener != null) { if (listener != null) {
listener.onResult("OK"); listener.onResult("OK", null);
} }
} }
@ -695,7 +695,7 @@ public abstract class InAppPurchaseHelper {
} catch (Exception e) { } catch (Exception e) {
logError("SendToken Error", e); logError("SendToken Error", e);
if (listener != null) { if (listener != null) {
listener.onResult("Error"); listener.onResult("Error", null);
} }
} }
} }

View file

@ -6,6 +6,7 @@ import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils;
import net.osmand.AndroidNetworkUtils.OnRequestResultListener; import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
@ -194,7 +195,7 @@ public class PerformLiveUpdateAsyncTask
AndroidNetworkUtils.sendRequestAsync( AndroidNetworkUtils.sendRequestAsync(
app, LiveUpdatesFragment.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() { app, LiveUpdatesFragment.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() {
@Override @Override
public void onResult(String result) { public void onResult(@Nullable String result, @Nullable String error) {
if (!Algorithms.isEmpty(result)) { if (!Algorithms.isEmpty(result)) {
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
source.setTimeZone(TimeZone.getTimeZone("UTC")); source.setTimeZone(TimeZone.getTimeZone("UTC"));

View file

@ -208,7 +208,7 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
"https://osmand.net/subscription/update", "https://osmand.net/subscription/update",
parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() { parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override @Override
public void onResult(String result) { public void onResult(@Nullable String result, @Nullable String error) {
dismissProgress(null); dismissProgress(null);
OsmandApplication app = getMyApplication(); OsmandApplication app = getMyApplication();
if (result != null) { if (result != null) {

View file

@ -6,6 +6,8 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.Nullable;
import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
@ -71,7 +73,7 @@ public class SendSearchQueryBottomSheet extends MenuBottomSheetDialogFragment {
AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/api/missing_search", params, AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/api/missing_search", params,
null, true, true, new AndroidNetworkUtils.OnRequestResultListener() { null, true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override @Override
public void onResult(String result) { public void onResult(@Nullable String result, @Nullable String error) {
if (result != null && isAdded()) { if (result != null && isAdded()) {
try { try {
JSONObject obj = new JSONObject(result); JSONObject obj = new JSONObject(result);