From 9b5bbf71210b0b1e9c3320a8c1a401191ffdd543 Mon Sep 17 00:00:00 2001 From: simonpoole Date: Wed, 28 Feb 2024 20:27:54 +0100 Subject: [PATCH] Re-jig the welcome dialog to allow some initial setup This adds options to use the current best imagery, set auto-download, setup for pen or mouse usage and start authorization immediately after the modal has been closed. Fixes https://github.com/MarcusWolschon/osmeditor4android/issues/2430 Fixes https://github.com/MarcusWolschon/osmeditor4android/issues/1506 --- .../java/de/blau/android/TestUtils.java | 6 +- .../java/de/blau/android/dialogs/Newbie.java | 155 +++++++++++++----- .../de/blau/android/prefs/Preferences.java | 29 +++- src/main/java/de/blau/android/util/Util.java | 2 +- src/main/res/layout/welcome_tabs.xml | 149 ++++++++++++----- src/main/res/values/strings.xml | 36 +++- 6 files changed, 291 insertions(+), 86 deletions(-) diff --git a/src/androidTest/java/de/blau/android/TestUtils.java b/src/androidTest/java/de/blau/android/TestUtils.java index a306dbb79a..0f0eb1019e 100644 --- a/src/androidTest/java/de/blau/android/TestUtils.java +++ b/src/androidTest/java/de/blau/android/TestUtils.java @@ -98,7 +98,11 @@ public static void grantPermissons(@NonNull UiDevice device) { * @param ctx Android context */ public static void dismissStartUpDialogs(@NonNull UiDevice device, @NonNull Context ctx) { - clickText(device, true, ctx.getResources().getString(R.string.okay), false, false); + if (findText(device, false, ctx.getResources().getString(R.string.welcome_title))) { + clickText(device, true, ctx.getResources().getString(R.string.next), true, false); + clickResource(device, false, device.getCurrentPackageName() + ":id/authorize", false); + clickText(device, true, ctx.getResources().getString(R.string.welcome_start), true, false); + } } /** diff --git a/src/main/java/de/blau/android/dialogs/Newbie.java b/src/main/java/de/blau/android/dialogs/Newbie.java index 09a187ced0..f6645d2d04 100644 --- a/src/main/java/de/blau/android/dialogs/Newbie.java +++ b/src/main/java/de/blau/android/dialogs/Newbie.java @@ -1,43 +1,59 @@ package de.blau.android.dialogs; +import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnShowListener; +import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; -import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog.Builder; import androidx.appcompat.app.AppCompatDialog; +import androidx.appcompat.widget.SwitchCompat; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; -import androidx.viewpager.widget.PagerTabStrip; +import androidx.preference.PreferenceManager; +import de.blau.android.App; +import de.blau.android.Authorize; import de.blau.android.HelpViewer; import de.blau.android.Main; import de.blau.android.R; +import de.blau.android.osm.ViewBox; +import de.blau.android.prefs.AdvancedPrefDatabase; +import de.blau.android.prefs.Preferences; +import de.blau.android.resources.DataStyle; +import de.blau.android.resources.TileLayerSource; +import de.blau.android.resources.TileLayerSource.Category; +import de.blau.android.resources.TileLayerSource.TileType; import de.blau.android.util.ImmersiveDialogFragment; import de.blau.android.util.OnPageSelectedListener; import de.blau.android.util.ThemeUtils; import de.blau.android.util.Util; import de.blau.android.views.ExtendedViewPager; +import de.blau.android.views.layers.MapTilesLayer; /** * Display a dialog giving new users minimal instructions * */ public class Newbie extends ImmersiveDialogFragment { - private static final String DEBUG_TAG = Newbie.class.getSimpleName(); private static final String TAG = "fragment_newbie"; - private Main main; + private static final String PAGER_POS_KEY = "pagerPos"; + private static final String AUTHORIZE_KEY = "authorize"; + private static final String PEN_SETUP_KEY = "penSetup"; + private static final String AUTO_DOWNLOAD_KEY = "autoDownload"; + private static final String USE_IMAGERY_KEY = "useImagery"; + + private static final int SETTINGS_PAGE_INDEX = 1; + private static final int WELCOME_PAGE_INDEX = 0; /** * Display a dialog giving new users minimal instructions @@ -83,55 +99,83 @@ public void onAttach(Context context) { if (!(context instanceof Main)) { throw new ClassCastException(context.toString() + " can only be called from Main"); } - main = (Main) context; } @NonNull @Override public AppCompatDialog onCreateDialog(Bundle savedInstanceState) { final FragmentActivity activity = getActivity(); + if (!(activity instanceof Main)) { + throw new ClassCastException(activity.toString() + " can only be called from Main"); + } Builder builder = new AlertDialog.Builder(activity); builder.setIcon(null); builder.setTitle(R.string.welcome_title); final LayoutInflater inflater = ThemeUtils.getLayoutInflater(activity); final View layout = inflater.inflate(R.layout.welcome_tabs, null); + final SwitchCompat displayImagery = layout.findViewById(R.id.use_imagery); + final SwitchCompat autoDownload = layout.findViewById(R.id.auto_download); + final SwitchCompat penSetup = layout.findViewById(R.id.pen_setup); + final SwitchCompat authorize = layout.findViewById(R.id.authorize); final ExtendedViewPager pager = (ExtendedViewPager) layout.findViewById(R.id.pager); pager.setAdapter(new ViewPagerAdapter(activity, layout, new int[] { R.id.welcome_page, R.id.settings_page }, new int[] { R.string.confirm_upload_edits_page, R.string.menu_tags })); + // set saved state before the on page change listener is set + if (savedInstanceState != null) { + displayImagery.setChecked(savedInstanceState.getBoolean(USE_IMAGERY_KEY)); + autoDownload.setChecked(savedInstanceState.getBoolean(AUTO_DOWNLOAD_KEY)); + penSetup.setChecked(savedInstanceState.getBoolean(PEN_SETUP_KEY)); + authorize.setChecked(savedInstanceState.getBoolean(AUTHORIZE_KEY, true)); + pager.setCurrentItem(savedInstanceState.getInt(PAGER_POS_KEY, 0)); + } pager.addOnPageChangeListener((OnPageSelectedListener) position -> { AlertDialog dialog = ((AlertDialog) getDialog()); - if (dialog != null) { - Button positive = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - Button negative = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); - positive.clearFocus(); - if (position == 0) { - positive.setText(R.string.next); - positive.setOnClickListener((View v) -> { - pager.setCurrentItem(2); - }); - negative.setText(R.string.skip); - negative.setOnClickListener((View v) -> getDialog().dismiss()); - } - if (position == 1) { - positive.setText("Start mapping!"); - positive.setOnClickListener((View v) -> { - Context ctx = v.getContext(); - if (ctx instanceof Main) { - getDialog().dismiss(); - ((Main) ctx).gotoCurrentLocation(); - ((Main) ctx).setFollowGPS(true); - } - }); - negative.setText(R.string.back); - negative.setOnClickListener((View v) -> { - pager.setCurrentItem(0); - }); - } + if (dialog == null) { + Log.e(DEBUG_TAG, "Dialog null"); + return; + } + Button positive = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + Button negative = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); + positive.clearFocus(); + if (position == WELCOME_PAGE_INDEX) { + positive.setText(R.string.next); + positive.setOnClickListener((View v) -> pager.setCurrentItem(SETTINGS_PAGE_INDEX)); + negative.setText(R.string.skip); + negative.setOnClickListener((View v) -> getDialog().dismiss()); + } + if (position == SETTINGS_PAGE_INDEX) { + positive.setText(R.string.welcome_start); + positive.setOnClickListener((View v) -> { + ((Main) activity).gotoCurrentLocation(); + ((Main) activity).setFollowGPS(true); + + Preferences prefs = App.getPreferences(activity); + if (displayImagery.isChecked()) { + setBestBackground(activity); + } + + prefs.setPanAndZoomAutoDownload(autoDownload.isChecked()); + + boolean penConfig = penSetup.isChecked(); + prefs.setLargeDragArea(!penConfig); + prefs.setWayNodeDragging(penConfig); + prefs.setDataStyle(penConfig ? Preferences.DEFAULT_PEN_MAP_STYLE : Preferences.DEFAULT_MAP_STYLE); + + ((Main) activity).getMap().setPrefs(activity, prefs); + + Newbie.dismissDialog(activity); + + if (authorize.isChecked()) { + Authorize.startForResult(activity, null); + } + }); + negative.setText(R.string.back); + negative.setOnClickListener((View v) -> pager.setCurrentItem(0)); } }); String message = getString(R.string.welcome_message); - if (main.isFullScreen()) { + if (((Main) activity).isFullScreen()) { message = message + getString(R.string.welcome_message_fullscreen); } ((TextView) layout.findViewById(R.id.welcome_message)).setText(Util.fromHtml(message)); @@ -144,18 +188,47 @@ public AppCompatDialog onCreateDialog(Bundle savedInstanceState) { dialog.setOnShowListener((DialogInterface d) -> { Button neutral = dialog.getButton(DialogInterface.BUTTON_NEUTRAL); neutral.setOnClickListener((View v) -> { - Context ctx = v.getContext(); + Context ctx = getActivity(); if (ctx instanceof FragmentActivity) { HelpViewer.start((FragmentActivity) ctx, R.string.help_introduction); - } else { - System.out.println("Not a fragment activity"); + return; } + Log.e(DEBUG_TAG, "Not a fragment activity"); }); Button positive = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - positive.setOnClickListener((View v) -> { - pager.setCurrentItem(1); - }); + positive.setOnClickListener((View v) -> pager.setCurrentItem(SETTINGS_PAGE_INDEX)); }); return dialog; } + + /** + * Set the best background for the current ViewBox + * + * @param activity the current activity + */ + private void setBestBackground(@NonNull final FragmentActivity activity) { + final String[] ids = TileLayerSource.getIds(App.getLogic().getMap().getViewBox(), true, Category.photo, null); + if (ids.length > 0) { + TileLayerSource tileSource = TileLayerSource.get(activity, ids[0], false); + MapTilesLayer tileLayer = ((Main) activity).getMap().getBackgroundLayer(); + tileLayer.setRendererInfo(tileSource); + try (AdvancedPrefDatabase db = new AdvancedPrefDatabase(activity)) { + db.setLayerContentId(tileLayer.getIndex(), tileSource.getId()); + } + } else { + Log.w(DEBUG_TAG, "No applicable imagery found!"); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Log.d(DEBUG_TAG, "onSaveInstanceState"); + Dialog dialog = getDialog(); + outState.putBoolean(USE_IMAGERY_KEY, ((SwitchCompat) dialog.findViewById(R.id.use_imagery)).isChecked()); + outState.putBoolean(AUTO_DOWNLOAD_KEY, ((SwitchCompat) dialog.findViewById(R.id.auto_download)).isChecked()); + outState.putBoolean(PEN_SETUP_KEY, ((SwitchCompat) dialog.findViewById(R.id.pen_setup)).isChecked()); + outState.putBoolean(AUTHORIZE_KEY, ((SwitchCompat) dialog.findViewById(R.id.authorize)).isChecked()); + outState.putInt(PAGER_POS_KEY, ((ExtendedViewPager) dialog.findViewById(R.id.pager)).getCurrentItem()); + } } diff --git a/src/main/java/de/blau/android/prefs/Preferences.java b/src/main/java/de/blau/android/prefs/Preferences.java index 830f76c413..30d1ddc2d9 100755 --- a/src/main/java/de/blau/android/prefs/Preferences.java +++ b/src/main/java/de/blau/android/prefs/Preferences.java @@ -45,7 +45,7 @@ public class Preferences { private final boolean isAntiAliasingEnabled; private final boolean isKeepScreenOnEnabled; private final boolean useBackForUndo; - private final boolean largeDragArea; + private boolean largeDragArea; private final boolean tagFormEnabled; private String scaleLayer; private String mapProfile; @@ -122,7 +122,7 @@ public class Preferences { private final int maxOffsetDistance; private final Set enabledValidations; private final int autoNameCap; - private final boolean wayNodeDragging; + private boolean wayNodeDragging; private final boolean splitWindowForPropertyEditor; private final boolean useImperialUnits; private final boolean supportPresetLabels; @@ -140,7 +140,8 @@ public class Preferences { private final double maxCircleSegment; private final double minCircleSegment; - private static final String DEFAULT_MAP_PROFILE = "Color Round Nodes"; + public static final String DEFAULT_MAP_STYLE = "Color Round Nodes"; + public static final String DEFAULT_PEN_MAP_STYLE = "Pen Round Nodes"; private final SharedPreferences prefs; @@ -434,6 +435,16 @@ public boolean largeDragArea() { return largeDragArea; } + /** + * Enable or disable the large drag area + * + * @param enabled if true enable the large drag area + */ + public void setLargeDragArea(boolean enabled) { + largeDragArea = enabled; + prefs.edit().putBoolean(r.getString(R.string.config_largeDragArea_key), enabled).commit(); + } + /** * Get kind of scale that should be displayed * @@ -463,7 +474,7 @@ public String getDataStyle() { // check if we actually still have the profile if (DataStyle.getStyle(mapProfile) == null) { Log.w(DEBUG_TAG, "Style " + mapProfile + " missing, replacing by default"); - setDataStyle(DataStyle.getStyle(DEFAULT_MAP_PROFILE) == null ? DataStyle.getBuiltinStyleName() : DEFAULT_MAP_PROFILE); + setDataStyle(DataStyle.getStyle(DEFAULT_MAP_STYLE) == null ? DataStyle.getBuiltinStyleName() : DEFAULT_MAP_STYLE); } return mapProfile; } @@ -1674,6 +1685,16 @@ public boolean isWayNodeDraggingEnabled() { return wayNodeDragging; } + /** + * Enable or disable the way node dragging + * + * @param enabled if true enable way node dragging + */ + public void setWayNodeDragging(boolean enabled) { + wayNodeDragging = enabled; + prefs.edit().putBoolean(r.getString(R.string.config_wayNodeDragging_key), enabled).commit(); + } + /** * Check if we should try to use split window functionality for the PropertyEditor * diff --git a/src/main/java/de/blau/android/util/Util.java b/src/main/java/de/blau/android/util/Util.java index 03ed792460..e033d0d9d2 100644 --- a/src/main/java/de/blau/android/util/Util.java +++ b/src/main/java/de/blau/android/util/Util.java @@ -446,7 +446,7 @@ public void handleTag(boolean opening, String tag, Editable output, XMLReader xm output.append("\n"); } if ("li".equals(tag) && opening) { - output.append("\n\t•"); + output.append("\n\t"); } } } diff --git a/src/main/res/layout/welcome_tabs.xml b/src/main/res/layout/welcome_tabs.xml index 116cfd42c9..1ab36c5337 100644 --- a/src/main/res/layout/welcome_tabs.xml +++ b/src/main/res/layout/welcome_tabs.xml @@ -19,7 +19,8 @@ + android:layout_height="wrap_content" + android:paddingTop="16dp"/> - - + + + + + + - + + - + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 881298370c..51c04515cc 100755 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -96,11 +96,43 @@ Duplicate keys You have added the following key more than once, this needs to be resolved before you can proceed: Select file picker - + Welcome - Thank you for installing Vespucci.\n<br><br>To get started you need to know three things:\n<ul><li>tapping the lock icon in the upper left corner will switch between locked (no editing) and unlocked mode,</li><li>to edit OpenStreetMap you need to download the data for the area where you want to change something first (zoom in before attempting that),</li><li>to add a new object: tap the large green button and select what you want to add, then tap the position you want to locate it at.</li></ul>\nYou are all set to go now. Start mapping and have fun! + Thank you for installing Vespucci.\n<br><br>Vespucci is a powerful tool for editing OpenStreetMap. + It supports nearly everything on mobile that you can accomplish with a desktop editor, this however comes with a certain level of complexity. + The help pages on the device or online will answer most questions, if you need more support we recommend asking on Openstreetmaps community forum. + <br><br>To get started you need to know three things: + <ul><li>tapping the lock icon in the upper left corner will switch between locked (no editing) and unlocked mode. + </li><li>to add a new object tap the large green button and select what you want to add from the menu, then tap the position you want to locate it at. + </li><li>to open the nearby point of interest display drag the handle above the bottom menu bar up. + </li></ul> + On the next page you can set some initial preferences that will make your journey with Vespucci more pleasant. + \n<br><br>Your device has \"soft keys\" that are hidden from view. Swipe from the bottom to show them again. + To improve your initial experience we can configure some options right now, + you can skip this step and set individual preferences later if you want to. + + Display aerial imagery\nfor your current location + Aerial imagery sources are provided by third parties and it is unavoidable + that they can access your current network address and the location you are viewing. If you have privacy concerns don\'t + select this and we will display the standard OpenStreetMap map as the background. + + Automatically download data + "Automatically downloading OpenStreetMap data can cause significant mobile traffic. + If you are using a metered connection you might prefer to not enable this and manually download data for the area you want to work on. + + Configure for pen or mouse usage + If you are using a pen, a mouse or other pointing device, + selecting this option will make editing more efficient. + + Authorize now + You need to authorize Vespucci to upload to your OpenStreetMap account. + If you have your login and password handy you can start that automatically when you have completed this form, + otherwise the process will start automatically the first time you upload edits. + Read the introduction + Start! + This is a new Vespucci version New features and behaviour are detailed in the release notes. Read about the new version