Support regular expression search in game load/save activity

Standard Java regular expression syntax is supported. The search is
case insensitive.
This commit is contained in:
Peter Osterlund 2019-09-22 08:48:42 +02:00
parent 15b62e9bc2
commit fbb2091d45
4 changed files with 66 additions and 17 deletions

View File

@ -72,6 +72,7 @@ public abstract class EditPGN extends AppCompatActivity {
private String lastSearchString = "";
private String lastFileName = "";
private long lastModTime = -1;
private boolean useRegExp = false;
private Thread workThread = null;
private boolean canceled = false;
@ -95,11 +96,13 @@ public abstract class EditPGN extends AppCompatActivity {
lastFileName = savedInstanceState.getString("lastFileName");
if (lastFileName == null) lastFileName = "";
lastModTime = savedInstanceState.getLong("lastModTime");
useRegExp = savedInstanceState.getBoolean("useRegExpSearch");
} else {
defaultFilePos = settings.getLong("defaultFilePos", 0);
lastSearchString = settings.getString("lastSearchString", "");
lastFileName = settings.getString("lastFileName", "");
lastModTime = settings.getLong("lastModTime", 0);
useRegExp = settings.getBoolean("useRegExpSearch", false);
}
Intent i = getIntent();
@ -131,14 +134,14 @@ public abstract class EditPGN extends AppCompatActivity {
workThread = new Thread(() -> {
if (!readFile())
return;
GameAdapter.ItemMatcher<GameInfo> m =
GameAdapter.getItemMatcher(lastSearchString, useRegExp);
int itemNo = getItemNo(gamesInFile, defaultFilePos) + (next ? 1 : -1);
if (next) {
while (itemNo < gamesInFile.size() &&
!GameAdapter.matchItem(gamesInFile.get(itemNo), lastSearchString))
while (itemNo < gamesInFile.size() && !m.matches(gamesInFile.get(itemNo)))
itemNo++;
} else {
while (itemNo >= 0 &&
!GameAdapter.matchItem(gamesInFile.get(itemNo), lastSearchString))
while (itemNo >= 0 && !m.matches(gamesInFile.get(itemNo)))
itemNo--;
}
final int loadItem = itemNo;
@ -205,6 +208,7 @@ public abstract class EditPGN extends AppCompatActivity {
outState.putString("lastSearchString", lastSearchString);
outState.putString("lastFileName", lastFileName);
outState.putLong("lastModTime", lastModTime);
outState.putBoolean("useRegExpSearch", useRegExp);
}
@Override
@ -214,6 +218,7 @@ public abstract class EditPGN extends AppCompatActivity {
editor.putString("lastSearchString", lastSearchString);
editor.putString("lastFileName", lastFileName);
editor.putLong("lastModTime", lastModTime);
editor.putBoolean("useRegExpSearch", useRegExp);
editor.apply();
super.onPause();
}
@ -234,6 +239,8 @@ public abstract class EditPGN extends AppCompatActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.edit_file_options_menu, menu);
MenuItem item = menu.findItem(R.id.regexp_search);
item.setChecked(useRegExp);
return true;
}
@ -243,6 +250,14 @@ public abstract class EditPGN extends AppCompatActivity {
case R.id.item_delete_file:
reShowDialog(DELETE_PGN_FILE_DIALOG);
break;
case R.id.regexp_search:
useRegExp = !useRegExp;
item.setChecked(useRegExp);
if (binding != null) {
String s = binding.selectGameFilter.getText().toString();
setFilterString(s);
}
break;
}
return false;
}
@ -480,17 +495,20 @@ public abstract class EditPGN extends AppCompatActivity {
}
private void setFilterString(String s) {
boolean regExp = useRegExp;
Filter.FilterListener listener = (count) -> {
ArrayList<GameInfo> arr = aa.getValues();
int itemNo = getItemNo(arr, currentFilePos);
if (itemNo < 0)
itemNo = 0;
while (itemNo < arr.size() &&
!GameAdapter.matchItem(arr.get(itemNo), lastSearchString))
GameAdapter.ItemMatcher<GameInfo> m =
GameAdapter.getItemMatcher(lastSearchString, regExp);
while (itemNo < arr.size() && !m.matches(arr.get(itemNo)))
itemNo++;
if (itemNo < arr.size())
binding.listView.setSelectionFromTop(itemNo, 0);
};
aa.setUseRegExp(regExp);
aa.getFilter().filter(s, listener);
}

View File

@ -28,6 +28,8 @@ import android.widget.Filterable;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* An adapter for displaying an ArrayList<GameInfo> in a ListView.
@ -38,6 +40,7 @@ public class GameAdapter<T> extends BaseAdapter implements Filterable {
private final LayoutInflater inflater;
private int resource;
private GameFilter filter; // Initialized at first use
private boolean useRegExp = false; // If true, use regular expression in filter
public GameAdapter(Context context, int resource, ArrayList<T> objects) {
origValues = objects;
@ -83,6 +86,10 @@ public class GameAdapter<T> extends BaseAdapter implements Filterable {
return filter;
}
public void setUseRegExp(boolean regExp) {
useRegExp = regExp;
}
private class GameFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
@ -91,10 +98,10 @@ public class GameAdapter<T> extends BaseAdapter implements Filterable {
res.values = origValues;
res.count = origValues.size();
} else {
String s = constraint.toString().toLowerCase();
ItemMatcher<T> m = getItemMatcher(constraint.toString(), useRegExp);
ArrayList<T> newValues = new ArrayList<>();
for (T item : origValues)
if (matchItem(item, s))
if (m.matches(item))
newValues.add(item);
res.values = newValues;
res.count = newValues.size();
@ -110,10 +117,27 @@ public class GameAdapter<T> extends BaseAdapter implements Filterable {
}
}
/** Return true if matchStr matches item.
* @param item The item to check. The toString() value converted to lowercase is used.
* @param matchStr The match string. Must be lowercase. */
static <U> boolean matchItem(U item, String matchStr) {
return item.toString().toLowerCase().contains(matchStr);
interface ItemMatcher<U> {
/** Return true if item matches the search criteria. */
boolean matches(U item);
}
/** Return an object that determines if an item matches given search criteria.
* @param matchStr The match string.
* @param useRegExp If true matchStr is interpreted as a regular expression. */
static <U> ItemMatcher<U> getItemMatcher(String matchStr, boolean useRegExp) {
if (useRegExp) {
Pattern tmp;
try {
tmp = Pattern.compile(matchStr, Pattern.CASE_INSENSITIVE);
} catch (PatternSyntaxException ex) {
tmp = null;
}
Pattern p = tmp;
return item -> p == null || p.matcher(item.toString()).find();
} else {
String s = matchStr.toLowerCase();
return item -> item.toString().toLowerCase().contains(s);
}
}
}

View File

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/item_delete_file"
android:title="@string/delete_file" />
<item
android:id="@+id/regexp_search"
android:title="@string/regexp_search"
android:checkable="true"
app:showAsAction="never" />
</menu>

View File

@ -196,6 +196,7 @@ If you are running on battery power, it is recommended that you change settings
<string name="delete_file">Delete file</string>
<string name="delete_file_question">Delete file?</string>
<string name="delete_named_file">Delete file %s?</string>
<string name="regexp_search">Regular Expression Search</string>
<string name="game_saved">Game saved</string>
<string name="failed_to_save_game">Failed to save game</string>
<string name="failed_to_delete_game">Failed to delete game</string>