diff --git a/src/androidTest/java/de/blau/android/validation/ValidatorRulesUITest.java b/src/androidTest/java/de/blau/android/validation/ValidatorRulesUITest.java new file mode 100644 index 0000000000..e6807a7bf5 --- /dev/null +++ b/src/androidTest/java/de/blau/android/validation/ValidatorRulesUITest.java @@ -0,0 +1,115 @@ +package de.blau.android.validation; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.app.Instrumentation; +import android.content.Context; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; + +import de.blau.android.Main; +import de.blau.android.R; +import de.blau.android.prefs.PrefEditorFragment; +import de.blau.android.prefs.Preferences; +import de.blau.android.validation.ValidatorRulesUI; +import de.blau.android.LayerUtils; +import de.blau.android.TestUtils; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ValidatorRulesUITest { + + Context context = null; + Instrumentation.ActivityMonitor monitor = null; + Instrumentation instrumentation = null; + UiDevice device = null; + Main main = null; + ValidatorRulesUI validatorRulesUI; + + @Rule + public ActivityTestRule
mActivityRule = new ActivityTestRule<>(Main.class); + + /** + * Pre-test setup + */ + @Before + public void setup() { + instrumentation = InstrumentationRegistry.getInstrumentation(); + device = UiDevice.getInstance(instrumentation); + context = instrumentation.getTargetContext(); + monitor = instrumentation.addMonitor(ValidatorRulesUI.class.getName(), null, false); + main = mActivityRule.getActivity(); + Preferences prefs = new Preferences(context); + LayerUtils.removeImageryLayers(context); + + TestUtils.grantPermissons(device); + TestUtils.dismissStartUpDialogs(device, main); + TestUtils.stopEasyEdit(main); + + } + +// @After +// public void teardown(){ instrumentation.removeMonitor(monitor);} + + @Test + public void addRuleset(){ +// ValidatorRulesUI Vri = new ValidatorRulesUI(); +// Vri.manageRulesetContents(context); +// TestUtils.clickText(device, false, "shop", true); + } +} +// @Test +// public void testToggleHeaderCheckbox() { +// // Open the resurveyList and check the initial state +// Cursor resurveyCursor = ValidatorRulesDatabase.queryResurveyByName(writableDb, ValidatorRulesDatabase.DEFAULT_RULESET_NAME); +// ResurveyAdapter resurveyAdapter = new ResurveyAdapter(writableDb, context, resurveyCursor); +// resurveyList.setAdapter(resurveyAdapter); +// runUiThreadTasksIncludingDelayedTasks(); +// +// +// // Click the header checkbox +// headerEnabled.performClick(); +// headerEnabled.performClick(); +// runUiThreadTasksIncludingDelayedTasks(); +// +// // Verify that all child checkboxes are checked +// assertFalse(headerEnabled.isChecked()); +// for (int i = 0; i < resurveyList.getChildCount(); i++) { +// View childView = resurveyList.getChildAt(i); +// CheckBox childCheckBox = childView.findViewById(R.id.resurvey_enabled); +// assertFalse(childCheckBox.isChecked()); +// } +// +// // Click the "Done" button to save the changes +// // Assuming you have a method to get the "Done" button and click it +// clickDoneButton(); +// +// // Re-open the resurveyList +// resurveyCursor = ValidatorRulesDatabase.queryResurveyByName(writableDb, ValidatorRulesDatabase.DEFAULT_RULESET_NAME); +// resurveyAdapter = new ResurveyAdapter(writableDb, context, resurveyCursor); +// resurveyList.setAdapter(resurveyAdapter); +// runUiThreadTasksIncludingDelayedTasks(); +// +// // Verify that the changes are persisted +// assertFalse(headerEnabled.isChecked()); +// for (int i = 0; i < resurveyList.getChildCount(); i++) { +// View childView = resurveyList.getChildAt(i); +// CheckBox childCheckBox = childView.findViewById(R.id.resurvey_enabled); +// assertFalse(childCheckBox.isChecked()); +// } +// } +// +//// Helper method to click the "Done" button +//private void clickDoneButton() { +// // Implement the logic to find and click the "Done" button +// // based on your application's UI structure +// } +//} diff --git a/src/main/java/de/blau/android/validation/ValidatorRulesDatabase.java b/src/main/java/de/blau/android/validation/ValidatorRulesDatabase.java index 722fccdbc8..ae7fd230e7 100644 --- a/src/main/java/de/blau/android/validation/ValidatorRulesDatabase.java +++ b/src/main/java/de/blau/android/validation/ValidatorRulesDatabase.java @@ -1,13 +1,19 @@ package de.blau.android.validation; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + +import de.blau.android.bookmarks.BookmarkStorage; import de.blau.android.util.collections.MultiHashMap; /** @@ -19,10 +25,10 @@ public final class ValidatorRulesDatabase { /** * Table: rulesets (id INTEGER, name TEXT) Table: resurvey (ruleset INTEGER, key TEXT, value TEXT DEFAULT NULL, days - * INTEGER DEFAULT 365, FOREIGN KEY(ruleset) REFERENCES rulesets(id)) Table: check (ruleset INTEGER, key TEXT, + * INTEGER DEFAULT 365, enabled INTEGER DEFAULT 1, FOREIGN KEY(ruleset) REFERENCES rulesets(id)) Table: check (ruleset INTEGER, key TEXT, * optional INTEGER DEFAULT 0, FOREIGN KEY(ruleset) REFERENCES rulesets(id)) */ - + private static final String DEBUG_TAG = ValidatorRulesDatabase.class.getSimpleName().substring(0, Math.min(23, ValidatorRulesDatabase.class.getSimpleName().length())); static final String DEFAULT_RULESET_NAME = "Default"; static final int DEFAULT_RULESET = 0; private static final String RULESET_TABLE = "rulesets"; @@ -36,11 +42,13 @@ public final class ValidatorRulesDatabase { static final String RULESET_FIELD = "ruleset"; private static final String CHECK_TABLE = "checktags"; static final String OPTIONAL_FIELD = "optional"; + static final String ENABLED_FIELD = "enabled"; - static final String QUERY_RESURVEY_DEFAULT = "SELECT resurveytags.rowid as _id, key, value, is_regexp, days FROM resurveytags WHERE ruleset = " + static final String QUERY_RESURVEY_DEFAULT = "SELECT resurveytags.rowid as _id, key, value, is_regexp, days, enabled FROM resurveytags WHERE ruleset = " + DEFAULT_RULESET + " ORDER BY key, value"; - static final String QUERY_RESURVEY_BY_ROWID = "SELECT key, value, is_regexp, days FROM resurveytags WHERE rowid=?"; - static final String QUERY_RESURVEY_BY_NAME = "SELECT resurveytags.rowid as _id, key, value, is_regexp, days FROM resurveytags, rulesets WHERE ruleset = rulesets.id and rulesets.name = ? ORDER BY key, value"; + static final String QUERY_RESURVEY_ALL_ID = "SELECT resurveytags.rowid as _id FROM resurveytags"; + static final String QUERY_RESURVEY_BY_ROWID = "SELECT key, value, is_regexp, days, enabled FROM resurveytags WHERE rowid=?"; + static final String QUERY_RESURVEY_BY_NAME = "SELECT resurveytags.rowid as _id, key, value, is_regexp, days, enabled FROM resurveytags, rulesets WHERE ruleset = rulesets.id and rulesets.name = ? ORDER BY key, value"; static final String QUERY_CHECK_DEFAULT = "SELECT checktags.rowid as _id, key, optional FROM checktags WHERE ruleset = " + DEFAULT_RULESET + " ORDER BY key"; @@ -77,19 +85,21 @@ public static void addRuleset(SQLiteDatabase db, int id, String name) { @Nullable public static MultiHashMap getDefaultResurvey(@NonNull SQLiteDatabase database) { MultiHashMap result = null; - Cursor dbresult = database.query(RESURVEY_TABLE, new String[] { KEY_FIELD, VALUE_FIELD, ISREGEXP_FIELD, DAYS_FIELD }, + Cursor dbresult = database.query(RESURVEY_TABLE, new String[] { KEY_FIELD, VALUE_FIELD, ISREGEXP_FIELD, DAYS_FIELD, ENABLED_FIELD }, RULESET_FIELD + " = " + DEFAULT_RULESET, null, null, null, KEY_FIELD + "," + VALUE_FIELD); if (dbresult.getCount() >= 1) { result = new MultiHashMap<>(); boolean haveEntry = dbresult.moveToFirst(); while (haveEntry) { - PatternAndAge v = new PatternAndAge(); - v.setValue(dbresult.getString(1)); - v.setIsRegexp(dbresult.getInt(2) == 1); - v.setAge(dbresult.getLong(3) * 24 * 3600); // days -> secs - result.add(dbresult.getString(0), v); - haveEntry = dbresult.moveToNext(); + if (dbresult.getInt(dbresult.getColumnIndexOrThrow(ValidatorRulesDatabase.ENABLED_FIELD)) == 1) { + PatternAndAge v = new PatternAndAge(); + v.setValue(dbresult.getString(1)); + v.setIsRegexp(dbresult.getInt(2) == 1); + v.setAge(dbresult.getLong(3) * 24 * 3600); // days -> secs + result.add(dbresult.getString(0), v); + haveEntry = dbresult.moveToNext(); + } } } dbresult.close(); @@ -110,6 +120,29 @@ public static Cursor queryResurveyByName(@NonNull SQLiteDatabase database, @Null return database.rawQuery(ValidatorRulesDatabase.QUERY_RESURVEY_BY_NAME, new String[] { name }); } } + /** + * Return all the resurvey ids at the time a new instance is created + * + * @param database readable database + * @return a List of resurvey ids + */ + @Nullable + public static List getAllResurveyIds(@NonNull SQLiteDatabase database) { + List result = new ArrayList<>(); + Cursor dbresult = database.rawQuery(ValidatorRulesDatabase.QUERY_RESURVEY_ALL_ID, null); + int counter = 0; + if (dbresult.getCount() >= 1) { + boolean haveEntry = dbresult.moveToFirst(); + while (haveEntry) { + result.add(dbresult.getInt(dbresult.getColumnIndexOrThrow("_id"))); + haveEntry = dbresult.moveToNext(); + counter++; + } + } + dbresult.close(); + Log.d(DEBUG_TAG, "Retrieved" + counter +"ids from validator database"); + return result; + } /** * Add an entry to the resurvey table @@ -121,13 +154,14 @@ public static Cursor queryResurveyByName(@NonNull SQLiteDatabase database, @Null * @param isRegexp if true the value is a regexp * @param days how man days old the object should max be */ - public static void addResurvey(@NonNull SQLiteDatabase db, int ruleSetId, @NonNull String key, @Nullable String value, boolean isRegexp, int days) { + public static void addResurvey(@NonNull SQLiteDatabase db, int ruleSetId, @NonNull String key, @Nullable String value, boolean isRegexp, int days, boolean enabled) { ContentValues values = new ContentValues(); values.put(RULESET_FIELD, ruleSetId); values.put(KEY_FIELD, key); values.put(VALUE_FIELD, value); values.put(ISREGEXP_FIELD, isRegexp ? 1 : 0); values.put(DAYS_FIELD, days); + values.put(ENABLED_FIELD, enabled ? 1 : 0); db.insert(RESURVEY_TABLE, null, values); } @@ -160,6 +194,33 @@ static void deleteResurvey(final SQLiteDatabase db, final int id) { db.delete(RESURVEY_TABLE, "rowid=?", new String[] { Integer.toString(id) }); } + /** + * Change the enabled row of the resurvey table. + * + * @param db writable database + * @param idIsEnabledMap rowid and their checkbox state + */ + public static void enableResurvey(SQLiteDatabase db, Map idIsEnabledMap) { + List contentValuesList = new ArrayList<>(); + for (Map.Entry entry : idIsEnabledMap.entrySet()) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ENABLED_FIELD, entry.getValue() ? 1 : 0); + contentValuesList.add(contentValues); + } + StringBuilder selectionBuilder = new StringBuilder("rowid IN ("); + List selectionArgs = new ArrayList<>(); + for (int key : idIsEnabledMap.keySet()) { + selectionBuilder.append("?,"); + selectionArgs.add(String.valueOf(key)); + } + selectionBuilder.replace(selectionBuilder.length() - 1, selectionBuilder.length(), ")"); + String selection = selectionBuilder.toString(); + + int rowsUpdated = db.update(RESURVEY_TABLE, contentValuesList.get(0), selection, selectionArgs.toArray(new String[0])); + Log.d(DEBUG_TAG, "Rulesets updated: " + rowsUpdated); + + } + /** * Return the default resurvey entries if any * diff --git a/src/main/java/de/blau/android/validation/ValidatorRulesDatabaseHelper.java b/src/main/java/de/blau/android/validation/ValidatorRulesDatabaseHelper.java index f02412feed..eb8e001782 100644 --- a/src/main/java/de/blau/android/validation/ValidatorRulesDatabaseHelper.java +++ b/src/main/java/de/blau/android/validation/ValidatorRulesDatabaseHelper.java @@ -12,7 +12,7 @@ public class ValidatorRulesDatabaseHelper extends SQLiteOpenHelper { private static final String DEBUG_TAG = ValidatorRulesDatabaseHelper.class.getSimpleName().substring(0, Math.min(23, ValidatorRulesDatabaseHelper.class.getSimpleName().length())); private static final String DATABASE_NAME = "validator_rules"; - private static final int DATABASE_VERSION = 3; + private static final int DATABASE_VERSION = 4; static final int ONE_YEAR = 365; /** @@ -31,18 +31,18 @@ public void onCreate(SQLiteDatabase db) { ValidatorRulesDatabase.addRuleset(db, ValidatorRulesDatabase.DEFAULT_RULESET, ValidatorRulesDatabase.DEFAULT_RULESET_NAME); db.execSQL( - "CREATE TABLE resurveytags (ruleset INTEGER, key TEXT, value TEXT DEFAULT NULL, is_regexp INTEGER DEFAULT 0, days INTEGER DEFAULT 365, FOREIGN KEY(ruleset) REFERENCES rulesets(id))"); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_SHOP, null, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_RESTAURANT, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_FAST_FOOD, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_CAFE, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_PUB, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_BAR, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_TOILETS, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_LANDUSE, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_HIGHWAY, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_RAILWAY, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR); - ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_BUILDING, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR); + "CREATE TABLE resurveytags (ruleset INTEGER, key TEXT, value TEXT DEFAULT NULL, is_regexp INTEGER DEFAULT 0, days INTEGER DEFAULT 365, enabled INTEGER DEFAULT 1, FOREIGN KEY(ruleset) REFERENCES rulesets(id))"); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_SHOP, null, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_RESTAURANT, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_FAST_FOOD, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_CAFE, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_PUB, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_BAR, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_AMENITY, Tags.VALUE_TOILETS, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_LANDUSE, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_HIGHWAY, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_RAILWAY, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR, true); + ValidatorRulesDatabase.addResurvey(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_BUILDING, Tags.VALUE_CONSTRUCTION, false, ONE_YEAR, true); db.execSQL("CREATE TABLE checktags (ruleset INTEGER, key TEXT, optional INTEGER DEFAULT 0, FOREIGN KEY(ruleset) REFERENCES rulesets(id))"); ValidatorRulesDatabase.addCheck(db, ValidatorRulesDatabase.DEFAULT_RULESET, Tags.KEY_OPENING_HOURS, false); @@ -62,5 +62,8 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion <= 2 && newVersion >= 3) { db.execSQL("UPDATE checktags SET key='name|ref' WHERE key='name'"); } + if (oldVersion <= 3 && newVersion >= 4) { + db.execSQL("ALTER TABLE resurveytags ADD COLUMN enabled INTEGER DEFAULT 1"); + } } } diff --git a/src/main/java/de/blau/android/validation/ValidatorRulesUI.java b/src/main/java/de/blau/android/validation/ValidatorRulesUI.java index f24232ac61..937356f1ef 100644 --- a/src/main/java/de/blau/android/validation/ValidatorRulesUI.java +++ b/src/main/java/de/blau/android/validation/ValidatorRulesUI.java @@ -1,7 +1,7 @@ package de.blau.android.validation; import com.google.android.material.floatingactionbutton.FloatingActionButton; - +import static de.blau.android.osm.Tags.IMPORTANT_TAGS; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; @@ -10,6 +10,8 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageView; @@ -20,11 +22,21 @@ import androidx.appcompat.widget.PopupMenu; import androidx.cursoradapter.widget.CursorAdapter; import androidx.viewpager.widget.PagerTabStrip; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import ch.poole.android.numberpicker.library.NumberPicker; import de.blau.android.App; +import de.blau.android.AsyncResult; +import de.blau.android.ErrorCodes; import de.blau.android.R; import de.blau.android.dialogs.ViewPagerAdapter; import de.blau.android.filter.Filter; +import de.blau.android.presets.Preset; +import de.blau.android.util.ExecutorTask; +import de.blau.android.util.StringWithDescription; import de.blau.android.util.ThemeUtils; import de.blau.android.views.ExtendedViewPager; @@ -36,6 +48,7 @@ public class ValidatorRulesUI { */ private ResurveyAdapter resurveyAdapter; private CheckAdapter checkAdapter; + private Map idIsEnabledMap = new HashMap<>(); /** * Show a list of the templates in the database, selection will either load a template or start the edit dialog on @@ -67,8 +80,30 @@ public void manageRulesetContents(@NonNull final Context context) { Cursor checkCursor = ValidatorRulesDatabase.queryCheckByName(writableDb, ValidatorRulesDatabase.DEFAULT_RULESET_NAME); checkAdapter = new CheckAdapter(writableDb, context, checkCursor); checkList.setAdapter(checkAdapter); - alertDialog.setNeutralButton(R.string.done, null); + + CheckBox headerEnabled = (CheckBox) rulesetView.findViewById(R.id.header_enabled); + headerEnabled.setOnClickListener(v -> { + boolean isChecked = ((CheckBox) v).isChecked(); + List allResurveyIds = ValidatorRulesDatabase.getAllResurveyIds(writableDb); + for (Integer id : allResurveyIds) { + int index = allResurveyIds.indexOf(id); + if (index < resurveyList.getChildCount()) { + View childView = resurveyList.getChildAt(index); + Log.d(DEBUG_TAG, "Header Checkbox triggered change in childView = " + index); + CheckBox checkBox = (CheckBox) childView.findViewById(R.id.resurvey_enabled); + checkBox.setChecked(isChecked); + idIsEnabledMap.put(id, isChecked); + } + } + }); + alertDialog.setNeutralButton(R.string.done, (dialog, which) -> { + if(!idIsEnabledMap.isEmpty()) { + ValidatorRulesDatabase.enableResurvey(writableDb, idIsEnabledMap); + newResurveyCursor(writableDb); + } + }); alertDialog.setOnDismissListener(dialog -> { + resetValidator(context); resurveyCursor.close(); writableDb.close(); vrDb.close(); @@ -123,12 +158,29 @@ public void bindView(final View view, final Context context, Cursor cursor) { String value = cursor.getString(cursor.getColumnIndexOrThrow(ValidatorRulesDatabase.VALUE_FIELD)); String key = cursor.getString(cursor.getColumnIndexOrThrow(ValidatorRulesDatabase.KEY_FIELD)); int days = cursor.getInt(cursor.getColumnIndexOrThrow(ValidatorRulesDatabase.DAYS_FIELD)); + boolean isEnabled; + if (idIsEnabledMap.containsKey(id)) { + isEnabled = idIsEnabledMap.get(id); + } else { + isEnabled = cursor.getInt(cursor.getColumnIndexOrThrow(ValidatorRulesDatabase.ENABLED_FIELD)) == 1; + } + TextView valueView = (TextView) view.findViewById(R.id.value); valueView.setText(value != null ? value : "*"); TextView keyView = (TextView) view.findViewById(R.id.key); keyView.setText(key); TextView daysView = (TextView) view.findViewById(R.id.days); daysView.setText(Integer.toString(days)); + CheckBox enabled = (CheckBox) view.findViewById(R.id.resurvey_enabled); + enabled.setChecked(isEnabled); + enabled.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + boolean isChecked = enabled.isChecked(); + idIsEnabledMap.put(id, isChecked); // Update the map instead of the database directly + enabled.setChecked(isChecked); + } + }); view.setOnClickListener(v -> { Integer tag = (Integer) view.getTag(); showResurveyDialog(context, db, true, tag != null ? tag : -1); @@ -161,10 +213,12 @@ private void showResurveyDialog(@NonNull final Context context, @NonNull final S View templateView = LayoutInflater.from(context).inflate(R.layout.validator_ruleset_resurvey_item, null); alertDialog.setView(templateView); - final EditText keyEdit = (EditText) templateView.findViewById(R.id.resurvey_key); - final EditText valueEdit = (EditText) templateView.findViewById(R.id.resurvey_value); + final AutoCompleteTextView keyEdit = (AutoCompleteTextView) templateView.findViewById(R.id.resurvey_key); + final AutoCompleteTextView valueEdit = (AutoCompleteTextView) templateView.findViewById(R.id.resurvey_value); final CheckBox regexpCheck = (CheckBox) templateView.findViewById(R.id.resurvey_is_regexp); final NumberPicker daysPicker = (NumberPicker) templateView.findViewById(R.id.resurvey_days); + List matchingKeys = new ArrayList<>(); + List matchingValues = new ArrayList<>(); if (existing) { Cursor cursor = db.rawQuery(ValidatorRulesDatabase.QUERY_RESURVEY_BY_ROWID, new String[] { Integer.toString(id) }); if (cursor.moveToFirst()) { @@ -192,14 +246,59 @@ private void showResurveyDialog(@NonNull final Context context, @NonNull final S alertDialog.setTitle(R.string.add_resurvey_title); daysPicker.setValue(ValidatorRulesDatabaseHelper.ONE_YEAR); } + Preset[] presets = App.getCurrentPresets(context); + matchingKeys.addAll(IMPORTANT_TAGS); //object-keys + if (matchingKeys.contains(keyEdit)) { + matchingKeys.remove(keyEdit); + } + Collections.sort(matchingKeys); + ArrayAdapter empty = new ArrayAdapter<>(context, R.layout.autocomplete_row, new String[0]); + keyEdit.setAdapter(empty); + valueEdit.setAdapter(empty); + View.OnClickListener autocompleteOnClick = v -> { + if (v.hasFocus()) { + ((AutoCompleteTextView) v).showDropDown(); + } + }; + keyEdit.setOnClickListener(autocompleteOnClick); + keyEdit.setOnFocusChangeListener((v, hasFocus) -> { + if (hasFocus) { + keyEdit.showDropDown(); + keyEdit.setAdapter(new ArrayAdapter<>(context, R.layout.autocomplete_row, matchingKeys)); + } + }); + keyEdit.setOnItemClickListener((parent, view, position, id1) -> { + String enabledKey = parent.getItemAtPosition(position).toString(); + keyEdit.setText(enabledKey); + }); + valueEdit.setOnClickListener(autocompleteOnClick); + valueEdit.setOnFocusChangeListener((v, hasFocus) -> { + Log.d(DEBUG_TAG, "onFocusChange value"); + if (hasFocus) { + valueEdit.showDropDown(); + matchingValues.clear(); + for (StringWithDescription s : Preset.getAutocompleteValues(presets, null, keyEdit.getText().toString().trim())) { + String values = s.getValue(); + if (!matchingValues.contains(values)) { + matchingValues.add(values); + } + } + Collections.sort(matchingValues); + valueEdit.setAdapter(new ArrayAdapter<>(context, R.layout.autocomplete_row, matchingValues)); + } + }); + valueEdit.setOnItemClickListener((parent, view, position, id12) -> { + String enabledKey = parent.getItemAtPosition(position).toString(); + valueEdit.setText(enabledKey); + }); alertDialog.setNegativeButton(R.string.cancel, null); alertDialog.setPositiveButton(R.string.save, (dialog, which) -> { if (!existing) { - ValidatorRulesDatabase.addResurvey(db, 0, keyEdit.getText().toString(), valueEdit.getText().toString(), regexpCheck.isChecked(), - daysPicker.getValue()); + ValidatorRulesDatabase.addResurvey(db, 0, keyEdit.getText().toString().trim(), valueEdit.getText().toString().trim(), regexpCheck.isChecked(), + daysPicker.getValue(), true); } else { - ValidatorRulesDatabase.updateResurvey(db, id, keyEdit.getText().toString(), valueEdit.getText().toString(), regexpCheck.isChecked(), + ValidatorRulesDatabase.updateResurvey(db, id, keyEdit.getText().toString().trim(), valueEdit.getText().toString().trim(), regexpCheck.isChecked(), daysPicker.getValue()); } newResurveyCursor(db); diff --git a/src/main/res/layout/validator_ruleset_list.xml b/src/main/res/layout/validator_ruleset_list.xml index 1da489a9cb..9a754699c6 100644 --- a/src/main/res/layout/validator_ruleset_list.xml +++ b/src/main/res/layout/validator_ruleset_list.xml @@ -32,6 +32,14 @@ android:layout_marginTop="5dp" android:layout_marginBottom="1dp" android:orientation="horizontal"> + + @@ -14,17 +15,21 @@ android:layout_alignParentTop="true" android:textAppearance="?android:attr/textAppearanceMedium" android:text="@string/Key"/> - - + android:layout_marginEnd="?attr/dialogPreferredPadding" + android:layout_marginRight="?attr/dialogPreferredPadding" + android:completionThreshold="1" + android:dropDownWidth="wrap_content" + android:dropDownHeight="300dp" + android:inputType="textNoSuggestions" + android:saveEnabled="false" /> - - +