Skip to content

Commit

Permalink
Merge branch 'select_elements_for_upload'
Browse files Browse the repository at this point in the history
  • Loading branch information
simonpoole committed Jan 12, 2025
2 parents 0e484ad + 2c7a2e4 commit 833aa54
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 46 deletions.
2 changes: 1 addition & 1 deletion documentation/docs/help/en/Main map display.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ To reposition or remove the "on-map" GPS button use the "Follow position button
Select either the transfer icon ![Transfer](../images/menu_transfer.png) or the "Transfer" menu item. This will display seven or eight options:

* **Upload data to OSM server...** - review and upload changes to OpenStreetMap, the entry is disabled if you haven't changed anything yet, or there is no network available. See [Uploading your changes](Uploading%20your%20changes.md) for more information *(requires authentication)* *(requires network connectivity)*
* **Review changes...** - review current changes
* **Review changes...** - review current changes, and potentially select them for upload.
* **Download current view** - download the area visible on the screen and merge it with existing data *(requires network connectivity)*
* **Clear and download current view** - clear any data in memory and then download the area visible on the screen *(requires network connectivity)*
* **Query Overpass...** - run a query against a Overpass API server, see [Overpass queries](#overpass_queries). *(requires network connectivity)*
Expand Down
148 changes: 131 additions & 17 deletions src/androidTest/java/de/blau/android/dialogs/ReviewChangesTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.blau.android.dialogs;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
Expand All @@ -16,12 +17,15 @@

import android.app.Instrumentation;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiSelector;
import de.blau.android.App;
Expand All @@ -40,9 +44,9 @@
@LargeTest
public class ReviewChangesTest {

Context context = null;
Main main = null;
UiDevice device = null;
Context context = null;
Main main = null;
UiDevice device = null;

@Rule
public ActivityTestRule<Main> mActivityRule = new ActivityTestRule<>(Main.class);
Expand Down Expand Up @@ -85,15 +89,15 @@ public void reviewChanges() {
assertTrue(TestUtils.findText(device, false, "#210461089"));
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.Done), true, false));

UiSelector uiSelector = new UiSelector().className("android.widget.Button").instance(0); // dialog close button
UiSelector uiSelector = new UiSelector().className("android.widget.Button").instance(1); // dialog close button
UiObject button = device.findObject(uiSelector);
try {
button.click();
} catch (UiObjectNotFoundException e1) {
fail(e1.getMessage());
}
}

/**
* Display changes to review
*/
Expand All @@ -102,49 +106,159 @@ public void reviewChanges2() {
Logic logic = App.getLogic();
Node bd = (Node) App.getDelegator().getOsmElement(Node.NAME, 101792984L);
assertNotNull(bd);
Map<String,String> tags = new HashMap<>(bd.getTags());
Map<String, String> tags = new HashMap<>(bd.getTags());
tags.put(Tags.KEY_NAME, "Dietikonberg");
tags.put(Tags.KEY_WIKIPEDIA, "en:Bergdietikon");

logic.setTags(main, bd, tags);
assertTrue(TestUtils.clickMenuButton(device, main.getString(R.string.menu_transfer), false, true));

assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_transfer_review), true, false));
assertTrue(TestUtils.findText(device, false, "Dietikonberg"));
assertTrue(TestUtils.clickText(device, false, "Dietikonberg", true, false));
assertTrue(TestUtils.findText(device, false, "#101792984"));

assertTrue(TestUtils.findText(device, false, "Bergdietikon"));
TestUtils.scrollTo("de:Bergdietikon", true);
// Scrolling horizontally would be nice here
// int middleV = device.getDisplayHeight() / 2;
// TestUtils.drag(device, device.getDisplayWidth() - 50f, middleV, 50f, middleV, 100);
// assertTrue(TestUtils.findText(device, false, "en:Bergdietikon"));

// Scrolling horizontally would be nice here
// int middleV = device.getDisplayHeight() / 2;
// TestUtils.drag(device, device.getDisplayWidth() - 50f, middleV, 50f, middleV, 100);
// assertTrue(TestUtils.findText(device, false, "en:Bergdietikon"));
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.Done), true, false));

UiSelector uiSelector = new UiSelector().className("android.widget.Button").instance(0); // dialog close button
UiSelector uiSelector = new UiSelector().className("android.widget.Button").instance(1); // dialog close button
UiObject button = device.findObject(uiSelector);
try {
button.click();
} catch (UiObjectNotFoundException e1) {
fail(e1.getMessage());
}
}

/**
* Goto element
*/
@Test
public void gotoChange() {
assertTrue(TestUtils.clickMenuButton(device, main.getString(R.string.menu_transfer), false, true));

assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_transfer_review), true, false));
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_transfer_review), true, false));
assertTrue(TestUtils.findText(device, false, "address 777 Schulstrasse"));
assertTrue(TestUtils.clickText(device, false, "address 777 Schulstrasse", true, false));
assertTrue(TestUtils.findText(device, false, "#210461089"));
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.goto_element), true, false));

TestUtils.clickAwayTip(device, main);
assertTrue(TestUtils.findText(device, false, main.getString(R.string.actionmode_wayselect)));
}

/**
* Select changes to upload
*/
@Test
public void selectChanges() {
Logic logic = App.getLogic();
Node bd = (Node) App.getDelegator().getOsmElement(Node.NAME, 101792984L);
assertNotNull(bd);
Map<String, String> tags = new HashMap<>(bd.getTags());
tags.put(Tags.KEY_NAME, "Dietikonberg");
tags.put(Tags.KEY_WIKIPEDIA, "en:Bergdietikon");

logic.setTags(main, bd, tags);
assertTrue(TestUtils.clickMenuButton(device, main.getString(R.string.menu_transfer), false, true));

assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_transfer_review), true, false));
UiSelector uiSelector0 = new UiSelector().className("android.widget.Button").instance(0); //

UiObject uploadButton = device.findObject(uiSelector0);
assertNotNull(uploadButton);
try {
assertFalse(uploadButton.isEnabled());
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}

UiObject2 itemText = TestUtils.findObjectWithText(device, false, "Dietikonberg", 1000, false);
assertNotNull(itemText);
UiObject2 itemCheckBox = itemText.getParent().findObject(By.res(device.getCurrentPackageName() + ":id/checkBox1"));

itemCheckBox.click();
TestUtils.sleep(5000); // hack
uploadButton = device.findObject(uiSelector0);
assertNotNull(uploadButton);
try {
assertTrue(uploadButton.isEnabled());
uploadButton.click();
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
assertTrue(TestUtils.findText(device, false, main.getString(R.string.confirm_upload_title), 5000));
UiSelector uiSelector1 = new UiSelector().className("android.widget.Button").instance(0); // dialog upload
// button
UiObject noButton = device.findObject(uiSelector1);
try {
noButton.clickAndWaitForNewWindow();
} catch (UiObjectNotFoundException e1) {
fail(e1.getMessage());
}
}

/**
* Select changes to upload
*/
@Test
public void selectChanges2() {
Logic logic = App.getLogic();
Node bd = (Node) App.getDelegator().getOsmElement(Node.NAME, 101792984L);
assertNotNull(bd);
Map<String, String> tags = new HashMap<>(bd.getTags());
tags.put(Tags.KEY_NAME, "Dietikonberg");
tags.put(Tags.KEY_WIKIPEDIA, "en:Bergdietikon");

logic.setTags(main, bd, tags);
assertTrue(TestUtils.clickMenuButton(device, main.getString(R.string.menu_transfer), false, true));

assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_transfer_review), true, false));
UiSelector uiSelector0 = new UiSelector().className("android.widget.Button").instance(0); //

UiObject uploadButton = device.findObject(uiSelector0);
assertNotNull(uploadButton);
try {
assertFalse(uploadButton.isEnabled());
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}

UiObject allCheckBox = TestUtils.findObjectWithResourceId(device, false, device.getCurrentPackageName() + ":id/checkBoxAll");
try {
allCheckBox.click();
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}

UiObject2 itemText = TestUtils.findObjectWithText(device, false, "Dietikonberg", 1000, false);
assertNotNull(itemText);
UiObject2 itemCheckBox = itemText.getParent().findObject(By.res(device.getCurrentPackageName() + ":id/checkBox1"));
assertTrue(itemCheckBox.isChecked());

allCheckBox = TestUtils.findObjectWithResourceId(device, false, device.getCurrentPackageName() + ":id/checkBoxAll");
try {
allCheckBox.click();
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}

itemText = TestUtils.findObjectWithText(device, false, "Dietikonberg", 1000, false);
assertNotNull(itemText);
itemCheckBox = itemText.getParent().findObject(By.res(device.getCurrentPackageName() + ":id/checkBox1"));
assertFalse(itemCheckBox.isChecked());

UiSelector uiSelector1 = new UiSelector().className("android.widget.Button").instance(1); // dialog close button
UiObject button = device.findObject(uiSelector1);
try {
button.click();
} catch (UiObjectNotFoundException e1) {
fail(e1.getMessage());
}
}
}
4 changes: 2 additions & 2 deletions src/main/assets/help/en/Main map display.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ <h3>Layer control</h3>
<li><strong>+</strong> button:
<ul>
<li>for disabled layers that can only be displayed once it will show a corresponding &quot;Enable ...&quot; entry that will turn the layer on.</li>
<li><strong>Add GeoJSON layer</strong> Loads a GeoJSON layer from a file in to a new GeoJSON layer.</li>
<li><strong>Add GeoJSON layer</strong> Loads a GeoJSON layer from a file in to a new GeoJSON layer, this will load CVS files with suitable (WGS84) longitude and latitude columns and write a converted file to the Vespucci directory.</li>
<li><strong>Add background imagery layer</strong> Adds a tile based imagery layer from the internal configuration, which can be from ELI or JOSM, or a custom imagery layer.</li>
<li><strong>Add overlay imagery layer</strong> As above but assumes that the layer is partially transparent.</li>
<li><strong>Enable photo layer</strong> Enables the photo layer this will display clickable icons for photos that will start an internal or external viewer. Which photos can be displayed depends strongly on your Android version and settings <a href="Advanced%20preferences.md">Advanced preferences</a>.</li>
Expand Down Expand Up @@ -187,7 +187,7 @@ <h3><img src="../images/menu_transfer.png" alt="Transfer" /> Transfer</h3>
<p>Select either the transfer icon <img src="../images/menu_transfer.png" alt="Transfer" /> or the &quot;Transfer&quot; menu item. This will display seven or eight options:</p>
<ul>
<li><strong>Upload data to OSM server...</strong> - review and upload changes to OpenStreetMap, the entry is disabled if you haven't changed anything yet, or there is no network available. See <a href="Uploading%20your%20changes.md">Uploading your changes</a> for more information <em>(requires authentication)</em> <em>(requires network connectivity)</em></li>
<li><strong>Review changes...</strong> - review current changes</li>
<li><strong>Review changes...</strong> - review current changes, and potentially select them for upload.</li>
<li><strong>Download current view</strong> - download the area visible on the screen and merge it with existing data <em>(requires network connectivity)</em></li>
<li><strong>Clear and download current view</strong> - clear any data in memory and then download the area visible on the screen <em>(requires network connectivity)</em></li>
<li><strong>Query Overpass...</strong> - run a query against a Overpass API server, see <a href="#overpass_queries">Overpass queries</a>. <em>(requires network connectivity)</em></li>
Expand Down
60 changes: 39 additions & 21 deletions src/main/java/de/blau/android/dialogs/AbstractReviewDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
Expand Down Expand Up @@ -47,7 +49,7 @@ public abstract class AbstractReviewDialog extends ImmersiveDialogFragment {

protected List<OsmElement> elements = null;

private static final Comparator<ChangedElement> DEFAULT_COMPARATOR = (ce0, ce1) -> {
protected static final Comparator<ChangedElement> DEFAULT_COMPARATOR = (ce0, ce1) -> {
OsmElement element0 = ce0.element;
OsmElement element1 = ce1.element;
int problems0 = element0.getCachedProblems();
Expand Down Expand Up @@ -99,10 +101,11 @@ public abstract class AbstractReviewDialog extends ImmersiveDialogFragment {
public void onStart() {
super.onStart();
Log.d(DEBUG_TAG, "onStart");
addChangesToView(getActivity(), (ListView) requireDialog().findViewById(R.id.upload_changes), elements, DEFAULT_COMPARATOR,
getArguments().getString(TAG_KEY));
createChangesView();
}

protected abstract void createChangesView();

/**
* Add changes to a ListView
*
Expand All @@ -111,26 +114,16 @@ public void onStart() {
* @param elements a List of OsmElement or null
* @param comparator a Comparator for sorting the changes
* @param parentTag tag of parent dialog
* @param itemResource the layout resource for the item
*/
private void addChangesToView(@NonNull final FragmentActivity activity, @NonNull final ListView changesView, @Nullable List<OsmElement> elements,
@NonNull Comparator<ChangedElement> comparator, @Nullable String parentTag) {
protected void addChangesToView(@NonNull final FragmentActivity activity, @NonNull final ListView changesView, @Nullable List<OsmElement> elements,
@NonNull Comparator<ChangedElement> comparator, @Nullable String parentTag, int itemResource) {
ExtendedValidator validator = new ExtendedValidator(activity, App.getDefaultValidator(activity));
final ChangedElement[] changes = getPendingChanges(activity.getResources(), elements == null ? App.getLogic().getPendingChangedElements() : elements);
revalidate(activity, validator, changes);
Arrays.sort(changes, comparator);

changesView.setAdapter(new ValidatorArrayAdapter(activity, R.layout.changes_list_item, changes, validator));
changesView.setOnItemClickListener((parent, view, position, id) -> {
ChangedElement clicked = changes[position];
OsmElement element = clicked.element;
byte elemenState = element.getState();
boolean deleted = elemenState == OsmElement.STATE_DELETED;
if (elemenState == OsmElement.STATE_MODIFIED || deleted) {
ElementInfo.showDialog(activity, 0, element, !deleted, parentTag);
} else {
ElementInfo.showDialog(activity, element, !deleted, parentTag);
}
});
changesView.setAdapter(new ValidatorArrayAdapter(activity, itemResource, changes, validator, parentTag));
}

/**
Expand All @@ -151,9 +144,10 @@ private void revalidate(@NonNull Context context, @NonNull Validator validator,
}
}

static class ChangedElement {
protected static class ChangedElement {
final OsmElement element;
final String description;
boolean selected;

/**
* Construct a new instance
Expand Down Expand Up @@ -197,10 +191,11 @@ private static ChangedElement[] getPendingChanges(@NonNull Resources resources,
* @author Simon Poole
*
*/
private static class ValidatorArrayAdapter extends ArrayAdapter<ChangedElement> {
protected static class ValidatorArrayAdapter extends ArrayAdapter<ChangedElement> {
final ChangedElement[] elements;
final Validator validator;
final ColorStateList colorStateList;
final String parentTag;

/**
* Construct a new instance
Expand All @@ -209,11 +204,14 @@ private static class ValidatorArrayAdapter extends ArrayAdapter<ChangedElement>
* @param resource the resource id of the per item layout
* @param elements the array holding the elements
* @param validator the Validator to use
* @param parentTag
*/
public ValidatorArrayAdapter(@NonNull Context context, int resource, @NonNull ChangedElement[] elements, @NonNull Validator validator) {
super(context, resource, elements);
public ValidatorArrayAdapter(@NonNull Context context, int resource, @NonNull final ChangedElement[] elements, @NonNull Validator validator,
@Nullable String parentTag) {
super(context, resource, R.id.text1, elements);
this.elements = elements;
this.validator = validator;
this.parentTag = parentTag;
colorStateList = ColorStateList.valueOf(ThemeUtils.getStyleAttribColorValue(context, R.attr.snack_error, R.color.material_red));
}

Expand All @@ -228,9 +226,29 @@ public View getView(int position, View convertView, ViewGroup container) {
} else {
setTintList(textView, null);
}
textView.setOnClickListener(view -> {
ChangedElement clicked = elements[position];
OsmElement e = clicked.element;
byte elemenState = element.getState();
boolean deleted = elemenState == OsmElement.STATE_DELETED;
final FragmentActivity fragmentActivity = (FragmentActivity) view.getContext();
if (elemenState == OsmElement.STATE_MODIFIED || deleted) {
ElementInfo.showDialog(fragmentActivity, 0, e, !deleted, parentTag);
} else {
ElementInfo.showDialog(fragmentActivity, e, !deleted, parentTag);
}
});
} else {
Log.e("ValidatorAdapterView", "position " + position + " view is null");
}
CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkBox1);
if (checkBox != null) {
checkBox.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
elements[position].selected = isChecked;
notifyDataSetChanged();
});
checkBox.setChecked(elements[position].selected);
}
return v;
}

Expand Down
Loading

0 comments on commit 833aa54

Please sign in to comment.