diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 24811649b9..28d88ae8b9 100755 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -225,6 +225,25 @@ android:configChanges="orientation|screenSize|keyboardHidden|density|screenLayout|uiMode|fontScale" android:launchMode="singleInstance" android:theme="@style/Theme.AppCompat" /> + + + + + + + + + + + + + + Progress.dismissDialog(Authorize.this, Progress.PROGRESS_OAUTH); + private class OAuthWebViewClient extends OsmWebViewClient { @Override public boolean handleLoading(WebView view, Uri uri) { @@ -87,40 +78,19 @@ public boolean handleLoading(WebView view, Uri uri) { } @Override - public WebResourceResponse handleIntercept(WebView view, Uri uri) { - final String path = uri.getPath(); - if (path != null && path.toLowerCase().contains(MATOMO)) { - return new WebResourceResponse(MimeTypes.TEXTPLAIN, "utf-8", new ByteArrayInputStream("".getBytes())); - } - return super.handleIntercept(view, uri); + public void exit() { + Authorize.this.exit(); } @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - synchronized (progressLock) { - if (!progressShown) { - progressShown = true; - Progress.showDialog(Authorize.this, Progress.PROGRESS_OAUTH); - } - } - } + protected void showProgressDialog() { + Progress.showDialog(Authorize.this, Progress.PROGRESS_OAUTH); - @Override - public void onPageFinished(WebView view, String url) { - synchronized (progressLock) { - synchronized (webViewLock) { - if (progressShown && webView != null) { - webView.removeCallbacks(dismiss); - webView.postDelayed(dismiss, 500); - } - } - } } @Override - public void receivedError(WebView view, int errorCode, String description, String failingUrl) { - exit(); - ScreenMessage.toastTopError(view.getContext(), description); + protected void dismissProgressDialog() { + Progress.dismissDialog(Authorize.this, Progress.PROGRESS_OAUTH); } } diff --git a/src/main/java/de/blau/android/Main.java b/src/main/java/de/blau/android/Main.java index 8bc816df26..bf38967d5f 100644 --- a/src/main/java/de/blau/android/Main.java +++ b/src/main/java/de/blau/android/Main.java @@ -2528,6 +2528,9 @@ public boolean read(FragmentActivity currentActivity, Uri fileUri) { case R.id.tag_menu_reset_address_prediction: Address.resetLastAddresses(this); return true; + case R.id.menu_tools_signup: + Signup.startForResult(this, null); + break; case R.id.menu_tools_oauth_reset: // reset the current OAuth tokens if (server.getOAuth()) { try (AdvancedPrefDatabase prefdb = new AdvancedPrefDatabase(this)) { diff --git a/src/main/java/de/blau/android/Signup.java b/src/main/java/de/blau/android/Signup.java new file mode 100644 index 0000000000..b4bdd7f228 --- /dev/null +++ b/src/main/java/de/blau/android/Signup.java @@ -0,0 +1,122 @@ +package de.blau.android; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.webkit.WebView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; +import de.blau.android.dialogs.Progress; +import de.blau.android.prefs.Preferences; +import de.blau.android.util.ActivityResultHandler; +import de.blau.android.util.OsmWebViewClient; +import de.blau.android.util.WebViewActivity; + +/** + * Create a new OSM account + * + * Depends on the path of the page with the sign up form + * + * https://master.apis.dev.openstreetmap.org/user/new + * + * the format of confirmation url received in the received e-mail (see the app manifest) + * + * https://master.apis.dev.openstreetmap.org/user/SimonDev1234/confirm?confirm_string=7x4FHpz7zvd1O8Z8hd70NIxVQDvIXpFH + * + * and the query parameter added when the "Start mapping" button in clicked + * + * @author Simon + * + */ +public class Signup extends WebViewActivity { + + private static final String DEBUG_TAG = Signup.class.getSimpleName(); + + public static final int REQUEST_CODE = Signup.class.hashCode() & 0x0000FFFF; + + private static final String EDIT_HELP_VALUE = "1"; + private static final String EDIT_HELP_PARAM = "edit_help"; + private static final String USER_NEW_PATH = "/user/new"; + + /** + * Start a Signup activity + * + * @param activity calling activity + * @param listener an ActivityResult.Listener to process the result or null + */ + public static void startForResult(@NonNull FragmentActivity activity, @Nullable ActivityResultHandler.Listener listener) { + Log.d(DEBUG_TAG, "startForResult"); + if (!hasWebView(activity)) { + return; + } + Log.d(DEBUG_TAG, "request code " + REQUEST_CODE); + if (listener != null) { + if (activity instanceof ActivityResultHandler) { + ((ActivityResultHandler) activity).setResultListener(REQUEST_CODE, listener); + } else { + throw new ClassCastException("activity must implement ActivityResultHandler"); + } + } + + Intent intent = new Intent(activity, Signup.class); + activity.startActivityForResult(intent, REQUEST_CODE); + } + + private class SignupViewClient extends OsmWebViewClient { + + @Override + public boolean handleLoading(WebView view, Uri uri) { + final String editHelp = uri.getQueryParameter(EDIT_HELP_PARAM); + if (editHelp != null && editHelp.equals(EDIT_HELP_VALUE)) { + exit(); + return true; + } + return false; + } + + @Override + public void exit() { + Signup.this.exit(); + } + + @Override + protected void showProgressDialog() { + Progress.showDialog(Signup.this, Progress.PROGRESS_WEBSITE); + } + + @Override + protected void dismissProgressDialog() { + Progress.dismissDialog(Signup.this, Progress.PROGRESS_WEBSITE); + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + final Preferences prefs = App.getPreferences(this); + if (prefs.lightThemeEnabled()) { + setTheme(R.style.Theme_customMain_Light); + } + super.onCreate(savedInstanceState); + + String dataUrl = getIntent().getDataString(); + String signupUrl = dataUrl == null ? prefs.getServer().getWebsiteBaseUrl() + USER_NEW_PATH : dataUrl; + + Log.d(DEBUG_TAG, "signup for " + signupUrl); + synchronized (webViewLock) { + webView = new WebView(this); + setContentView(webView); + webView.getSettings().setJavaScriptEnabled(true); + webView.setWebViewClient(new SignupViewClient()); + loadUrlOrRestore(savedInstanceState, signupUrl); + } + } + + @Override + protected void onNewIntent(Intent intent) { + Log.d(DEBUG_TAG, "onNewIntent"); + super.onNewIntent(intent); + loadUrlOrRestore(null, intent.getDataString()); + } +} \ No newline at end of file diff --git a/src/main/java/de/blau/android/dialogs/Progress.java b/src/main/java/de/blau/android/dialogs/Progress.java index de93b05103..3a22f7d0e1 100644 --- a/src/main/java/de/blau/android/dialogs/Progress.java +++ b/src/main/java/de/blau/android/dialogs/Progress.java @@ -37,6 +37,7 @@ public class Progress extends ImmersiveDialogFragment { public static final int PROGRESS_LOADING_PRESET = 14; public static final int PROGRESS_IMPORTING_FILE = 15; public static final int PROGRESS_DOWNLOAD_TASKS = 16; + public static final int PROGRESS_WEBSITE = 17; private int dialogType; @@ -116,6 +117,7 @@ public static void dismissAll(@NonNull FragmentActivity activity) { dismissDialog(activity, PROGRESS_LOADING_PRESET); dismissDialog(activity, PROGRESS_IMPORTING_FILE); dismissDialog(activity, PROGRESS_DOWNLOAD_TASKS); + dismissDialog(activity, PROGRESS_WEBSITE); } /** @@ -159,6 +161,8 @@ private static String getTag(int dialogType) { return "dialog_progress_importing_file"; case PROGRESS_DOWNLOAD_TASKS: return "dialog_progress_download_tasks"; + case PROGRESS_WEBSITE: + return "dialog_progress_website"; default: Log.w(DEBUG_TAG, "Unknown dialog type " + dialogType); } diff --git a/src/main/java/de/blau/android/dialogs/ProgressDialog.java b/src/main/java/de/blau/android/dialogs/ProgressDialog.java index 325af2100e..1ef13fb199 100644 --- a/src/main/java/de/blau/android/dialogs/ProgressDialog.java +++ b/src/main/java/de/blau/android/dialogs/ProgressDialog.java @@ -99,6 +99,10 @@ public static AlertDialog get(@NonNull Context ctx, int dialogType) { titleId = R.string.progress_title; messageId = R.string.progress_download_tasks_message; break; + case Progress.PROGRESS_WEBSITE: + titleId = R.string.progress_general_title; + messageId = R.string.progress_website; + break; default: return null; } diff --git a/src/main/java/de/blau/android/util/DownloadActivity.java b/src/main/java/de/blau/android/util/DownloadActivity.java index 6456428e71..8a154e3c37 100644 --- a/src/main/java/de/blau/android/util/DownloadActivity.java +++ b/src/main/java/de/blau/android/util/DownloadActivity.java @@ -107,7 +107,7 @@ public boolean handleLoading(@NonNull WebView view, @NonNull Uri uri) { @Override protected WebResourceResponse handleIntercept(WebView view, Uri uri) { if (FAVICON.equals(uri.getLastPathSegment())) { - return new WebResourceResponse(MimeTypes.PNG, "utf-8", new ByteArrayInputStream("".getBytes())); + return emptyResponse(); } return super.handleIntercept(view, uri); } diff --git a/src/main/java/de/blau/android/util/OsmWebViewClient.java b/src/main/java/de/blau/android/util/OsmWebViewClient.java new file mode 100644 index 0000000000..50d3b86402 --- /dev/null +++ b/src/main/java/de/blau/android/util/OsmWebViewClient.java @@ -0,0 +1,94 @@ +package de.blau.android.util; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; +import android.util.Log; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import androidx.annotation.Nullable; + +/** + * WebViewClient for some OSM website specifics + * + * @author Simon + * + */ +public abstract class OsmWebViewClient extends UpdatedWebViewClient { + + private static final String DEBUG_TAG = OsmWebViewClient.class.getSimpleName(); + + private static final String MATOMO = "matomo"; + + private Runnable dismiss = () -> dismissProgressDialog(); + + private Object progressLock = new Object(); + private boolean progressShown = false; + + /** + * Manipulate the response to a query + * + * @param view the WebView + * @param uri the request Uri + * @return a WebResourceResponse or null if normal processing should continue + */ + @Nullable + protected WebResourceResponse handleIntercept(WebView view, Uri uri) { // NOSONAR + // remove known trackers + final String path = uri.getPath(); + if (path != null && path.toLowerCase().contains(MATOMO)) { + return emptyResponse(); + } + return null; + } + + @Override + public void receivedError(WebView view, int errorCode, String description, String failingUrl) { + exit(); + ScreenMessage.toastTopError(view.getContext(), description); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + synchronized (progressLock) { + if (!progressShown) { + progressShown = true; + showProgressDialog(); + } + } + } + + @Override + public void onPageFinished(WebView view, String url) { + synchronized (progressLock) { + if (view != null) { // shouldn't happen but historically has + Context context = view.getContext(); + if (context instanceof WebViewActivity) { + synchronized (((WebViewActivity) context).webViewLock) { + if (progressShown) { + view.removeCallbacks(dismiss); + view.postDelayed(dismiss, 500); + } + } + return; + } + } + Log.e(DEBUG_TAG, "onPageFinish context not a WebViewActivity"); + } + } + + /** + * Exit the activity holding the WebView + */ + protected abstract void exit(); + + /** + * Show the progress dialog + */ + protected abstract void showProgressDialog(); + + /** + * Dismiss the progress dialog + */ + protected abstract void dismissProgressDialog(); +} diff --git a/src/main/java/de/blau/android/util/UpdatedWebViewClient.java b/src/main/java/de/blau/android/util/UpdatedWebViewClient.java index a3eaae0e3d..cb5d8b828c 100644 --- a/src/main/java/de/blau/android/util/UpdatedWebViewClient.java +++ b/src/main/java/de/blau/android/util/UpdatedWebViewClient.java @@ -1,5 +1,7 @@ package de.blau.android.util; +import java.io.ByteArrayInputStream; + import android.annotation.TargetApi; import android.net.Uri; import android.os.Build; @@ -10,6 +12,7 @@ import android.webkit.WebViewClient; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import de.blau.android.contract.MimeTypes; /** * Class to handle some of the deprecations in Androids WebViewClient @@ -18,7 +21,7 @@ * */ public abstract class UpdatedWebViewClient extends WebViewClient { - + @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // nOSONAR @@ -66,6 +69,15 @@ protected WebResourceResponse handleIntercept(WebView view, Uri uri) { // NOSONA return null; } + /** + * Create and return an empty WebResourceResponse + * + * @return an empty response + */ + protected WebResourceResponse emptyResponse() { + return new WebResourceResponse(MimeTypes.TEXTPLAIN, "utf-8", new ByteArrayInputStream("".getBytes())); + } + @SuppressWarnings("deprecation") @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { // NOSONAR diff --git a/src/main/res/menu-v24/main_menu_nosubmenus.xml b/src/main/res/menu-v24/main_menu_nosubmenus.xml index 6c78ea5c58..e8833e5b6c 100644 --- a/src/main/res/menu-v24/main_menu_nosubmenus.xml +++ b/src/main/res/menu-v24/main_menu_nosubmenus.xml @@ -188,6 +188,9 @@ android:id="@+id/tag_menu_reset_address_prediction" app:showAsAction="never" android:title="@string/tag_menu_reset_address_prediction" /> + diff --git a/src/main/res/menu/main_menu.xml b/src/main/res/menu/main_menu.xml index 58ab46367b..f789f12d81 100644 --- a/src/main/res/menu/main_menu.xml +++ b/src/main/res/menu/main_menu.xml @@ -197,6 +197,9 @@ android:id="@+id/tag_menu_reset_address_prediction" app:showAsAction="never" android:title="@string/tag_menu_reset_address_prediction" /> + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 37782d953c..5f40d86bf1 100755 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -102,6 +102,7 @@ Querying OAM… Pruning… Migrating… + Loading website… "%1$s" deleted "%1$s" changed @@ -1036,9 +1037,10 @@ %1$s [wms] Apply stored offset to imagery - Contrast - Reset OAuth - Authorise OAuth + Contrast… + Reset authorisation (OAuth 1/2) + Authorise app (OAuth 1/2)… + Create OSM account… Tag filter Preset filter Include way nodes