Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2017-03-24 18:13:46 +01:00
commit a4cb36b009
8 changed files with 647 additions and 577 deletions

View file

@ -40,24 +40,21 @@ import java.util.concurrent.atomic.AtomicInteger;
public class SearchUICore {
private static final int TIMEOUT_BETWEEN_CHARS = 200;
private static final Log LOG = PlatformUtil.getLog(SearchUICore.class);
private static final Log LOG = PlatformUtil.getLog(SearchUICore.class);
private SearchPhrase phrase;
private SearchResultCollection currentSearchResult;
private SearchResultCollection currentSearchResult;
private ThreadPoolExecutor singleThreadedExecutor;
private LinkedBlockingQueue<Runnable> taskQueue;
private Runnable onSearchStart = null;
private Runnable onResultsComplete = null;
private AtomicInteger requestNumber = new AtomicInteger();
private int totalLimit = -1; // -1 unlimited - not used
List<SearchCoreAPI> apis = new ArrayList<>();
private SearchSettings searchSettings;
private MapPoiTypes poiTypes;
private SearchCoreFactory.SearchCityByNameAPI searchCityByNameAPI;
private SearchCoreFactory.SearchPostcodeAPI searchPostcodeAPI;
private Class<? extends SearchCoreAPI> searchApiClass;
public SearchUICore(MapPoiTypes poiTypes, String locale, boolean transliterate) {
this.poiTypes = poiTypes;
@ -69,82 +66,68 @@ public class SearchUICore {
singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, taskQueue);
}
public Class<? extends SearchCoreAPI> getSearchApiClass() {
return searchApiClass;
}
public void setSearchApiClass(Class<? extends SearchCoreAPI> searchApiClass) {
this.searchApiClass = searchApiClass;
}
public boolean isInCustomSearch() {
return searchApiClass != null;
}
public static class SearchResultCollection {
private List<SearchResult> searchResults;
private SearchPhrase phrase;
private static final int DEPTH_TO_CHECK_SAME_SEARCH_RESULTS = 20;
public SearchResultCollection(SearchPhrase phrase) {
searchResults = new ArrayList<>();
this.phrase = phrase;
}
public SearchResultCollection combineWithCollection(SearchResultCollection collection, boolean resort, boolean removeDuplicates) {
SearchResultCollection src = new SearchResultCollection(phrase);
src.addSearchResults(searchResults, false, false);
src.addSearchResults(collection.searchResults, resort, removeDuplicates);
return src;
}
public SearchResultCollection addSearchResults(List<SearchResult> sr, boolean resortAll, boolean removeDuplicates) {
if(resortAll) {
if (resortAll) {
this.searchResults.addAll(sr);
sortSearchResults();
if(removeDuplicates) {
if (removeDuplicates) {
filterSearchDuplicateResults();
}
} else {
if(!removeDuplicates) {
if (!removeDuplicates) {
this.searchResults.addAll(sr);
} else {
ArrayList<SearchResult> addedResults = new ArrayList<>(sr);
SearchResultComparator cmp = new SearchResultComparator(phrase);
Collections.sort(addedResults, cmp);
filterSearchDuplicateResults(addedResults);
int i = 0;
int i = 0;
int j = 0;
while(j < addedResults.size()) {
while (j < addedResults.size()) {
SearchResult addedResult = addedResults.get(j);
if(i >= searchResults.size()) {
if (i >= searchResults.size()) {
int k = 0;
boolean same = false;
while(searchResults.size() > k && k < DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) {
if(sameSearchResult(addedResult, searchResults.get(searchResults.size() - k - 1))) {
while (searchResults.size() > k && k < DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) {
if (sameSearchResult(addedResult, searchResults.get(searchResults.size() - k - 1))) {
same = true;
break;
}
k++;
}
if(!same) {
if (!same) {
searchResults.add(addedResult);
}
j++;
continue;
}
SearchResult existingResult = searchResults.get(i);
if(sameSearchResult(addedResult, existingResult)) {
if (sameSearchResult(addedResult, existingResult)) {
j++;
continue;
}
int compare = cmp.compare(existingResult, addedResult);
if(compare == 0) {
if (compare == 0) {
// existingResult == addedResult
j++;
} else if(compare > 0) {
} else if (compare > 0) {
// existingResult > addedResult
this.searchResults.add(addedResults.get(j));
j++;
@ -153,61 +136,58 @@ public class SearchUICore {
i++;
}
}
}
}
return this;
}
public List<SearchResult> getCurrentSearchResults() {
return Collections.unmodifiableList(searchResults);
}
public SearchPhrase getPhrase() {
return phrase;
}
public void sortSearchResults() {
Collections.sort(searchResults, new SearchResultComparator(phrase));
Collections.sort(searchResults, new SearchResultComparator(phrase));
}
public void filterSearchDuplicateResults() {
filterSearchDuplicateResults(searchResults);
}
private void filterSearchDuplicateResults(List<SearchResult> lst) {
ListIterator<SearchResult> it = lst.listIterator();
LinkedList<SearchResult> lstUnique = new LinkedList<SearchResult>();
while(it.hasNext()) {
while (it.hasNext()) {
SearchResult r = it.next();
boolean same = false;
for(SearchResult rs : lstUnique) {
for (SearchResult rs : lstUnique) {
same = sameSearchResult(rs, r);
if(same) {
if (same) {
break;
}
}
if(same) {
if (same) {
it.remove();
} else {
lstUnique.add(r);
if(lstUnique.size() > DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) {
if (lstUnique.size() > DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) {
lstUnique.remove(0);
}
}
}
}
}
public boolean sameSearchResult(SearchResult r1, SearchResult r2) {
if(r1.location != null && r2.location != null) {
if (r1.location != null && r2.location != null) {
Amenity a1 = null;
if(r1.object instanceof Amenity) {
if (r1.object instanceof Amenity) {
a1 = (Amenity) r1.object;
}
Amenity a2 = null;
if(r2.object instanceof Amenity) {
if (r2.object instanceof Amenity) {
a2 = (Amenity) r2.object;
}
if (r1.localeName.equals(r2.localeName)) {
@ -229,7 +209,7 @@ public class SearchUICore {
similarityRadius = 50000;
}
}
} else if(ObjectType.isAddress(r1.objectType) && ObjectType.isAddress(r2.objectType)) {
} else if (ObjectType.isAddress(r1.objectType) && ObjectType.isAddress(r2.objectType)) {
similarityRadius = 100;
}
return MapUtils.getDistance(r1.location, r2.location) < similarityRadius;
@ -240,44 +220,39 @@ public class SearchUICore {
return false;
}
}
public void setPoiTypes(MapPoiTypes poiTypes) {
this.poiTypes = poiTypes;
}
public int getTotalLimit() {
return totalLimit;
}
public void setTotalLimit(int totalLimit) {
this.totalLimit = totalLimit;
}
@SuppressWarnings("unchecked")
public <T> T getApiByClass(Class<T> cl) {
for (SearchCoreAPI a : apis) {
if(cl.isInstance(a)) {
if (cl.isInstance(a)) {
return (T) a;
}
}
if (cl.isInstance(searchCityByNameAPI)) {
return (T) searchCityByNameAPI;
} else if (cl.isInstance(searchPostcodeAPI)) {
return (T) searchPostcodeAPI;
}
return null;
}
public <T extends SearchCoreAPI> SearchResultCollection shallowSearch(Class<T> cl,
String text, final ResultMatcher<SearchResult> matcher) throws IOException {
String text, final ResultMatcher<SearchResult> matcher) throws IOException {
T api = getApiByClass(cl);
if(api != null) {
if (api != null) {
SearchPhrase sphrase = this.phrase.generateNewPhrase(text, searchSettings);
preparePhrase(sphrase);
AtomicInteger ai = new AtomicInteger();
SearchResultMatcher rm = new SearchResultMatcher(matcher, ai.get(), ai, totalLimit);
api.search(sphrase, rm);
SearchResultCollection collection = new SearchResultCollection(
sphrase);
collection.addSearchResults(rm.getRequestResults(), true, true);
@ -286,49 +261,45 @@ public class SearchUICore {
}
return null;
}
public void init() {
apis.add(new SearchCoreFactory.SearchLocationAndUrlAPI());
apis.add(new SearchCoreFactory.SearchAmenityTypesAPI(poiTypes));
apis.add(new SearchCoreFactory.SearchAmenityByTypeAPI(poiTypes));
apis.add(new SearchCoreFactory.SearchAmenityByNameAPI());
SearchBuildingAndIntersectionsByStreetAPI streetsApi =
SearchBuildingAndIntersectionsByStreetAPI streetsApi =
new SearchCoreFactory.SearchBuildingAndIntersectionsByStreetAPI();
apis.add(streetsApi);
SearchStreetByCityAPI cityApi = new SearchCoreFactory.SearchStreetByCityAPI(streetsApi);
apis.add(cityApi);
SearchCoreFactory.SearchAddressByNameAPI searchAddressByNameAPI
= new SearchCoreFactory.SearchAddressByNameAPI(streetsApi, cityApi);
apis.add(searchAddressByNameAPI);
searchCityByNameAPI = new SearchCoreFactory.SearchCityByNameAPI(searchAddressByNameAPI);
searchPostcodeAPI = new SearchCoreFactory.SearchPostcodeAPI();
apis.add(new SearchCoreFactory.SearchAddressByNameAPI(streetsApi, cityApi));
}
public void clearCustomSearchPoiFilters() {
for(SearchCoreAPI capi : apis) {
if(capi instanceof SearchAmenityTypesAPI) {
for (SearchCoreAPI capi : apis) {
if (capi instanceof SearchAmenityTypesAPI) {
((SearchAmenityTypesAPI) capi).clearCustomFilters();
}
}
}
public void addCustomSearchPoiFilter(CustomSearchPoiFilter poiFilter, int priority) {
for(SearchCoreAPI capi : apis) {
if(capi instanceof SearchAmenityTypesAPI) {
for (SearchCoreAPI capi : apis) {
if (capi instanceof SearchAmenityTypesAPI) {
((SearchAmenityTypesAPI) capi).addCustomFilter(poiFilter, priority);
}
}
}
public void registerAPI(SearchCoreAPI api) {
apis.add(api);
}
public SearchResultCollection getCurrentSearchResult() {
return currentSearchResult;
}
public SearchPhrase getPhrase() {
return phrase;
}
@ -348,27 +319,27 @@ public class SearchUICore {
public void updateSettings(SearchSettings settings) {
searchSettings = settings;
}
private List<SearchResult> filterCurrentResults(List<SearchResult> rr, SearchPhrase phrase) {
List<SearchResult> l = currentSearchResult.searchResults;
for(SearchResult r : l) {
if(filterOneResult(r, phrase)) {
for (SearchResult r : l) {
if (filterOneResult(r, phrase)) {
rr.add(r);
}
}
return rr;
}
private boolean filterOneResult(SearchResult object, SearchPhrase phrase) {
NameStringMatcher nameStringMatcher = phrase.getNameStringMatcher();
return nameStringMatcher.matches(object.localeName) || nameStringMatcher.matches(object.otherNames);
return nameStringMatcher.matches(object.localeName) || nameStringMatcher.matches(object.otherNames);
}
public boolean selectSearchResult(SearchResult r) {
this.phrase = this.phrase.selectWord(r);
return true;
}
public SearchPhrase resetPhrase() {
this.phrase = this.phrase.generateNewPhrase("", searchSettings);
return this.phrase;
@ -395,7 +366,7 @@ public class SearchUICore {
onSearchStart.run();
}
SearchResultMatcher rm = new SearchResultMatcher(matcher, request, requestNumber, totalLimit);
if(TIMEOUT_BETWEEN_CHARS > 0) {
if(TIMEOUT_BETWEEN_CHARS > 0) {
Thread.sleep(TIMEOUT_BETWEEN_CHARS);
}
if(rm.isCancelled()) {
@ -403,7 +374,7 @@ public class SearchUICore {
}
searchInBackground(phrase, rm);
if (!rm.isCancelled()) {
SearchResultCollection collection = new SearchResultCollection(
phrase);
collection.addSearchResults(rm.getRequestResults(), true, true);
@ -419,15 +390,15 @@ public class SearchUICore {
}
});
return quickRes;
}
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
for(SearchCoreAPI api : apis) {
if(api.getSearchPriority(phrase) >= 0 && api.isSearchMoreAvailable(phrase)) {
for (SearchCoreAPI api : apis) {
if (api.getSearchPriority(phrase) >= 0 && api.isSearchMoreAvailable(phrase)) {
return true;
}
}
@ -436,12 +407,7 @@ public class SearchUICore {
private void searchInBackground(final SearchPhrase phrase, SearchResultMatcher matcher) {
preparePhrase(phrase);
ArrayList<SearchCoreAPI> lst = new ArrayList<>();
if (searchApiClass != null) {
lst.add(getApiByClass(searchApiClass));
} else {
lst.addAll(apis);
}
ArrayList<SearchCoreAPI> lst = new ArrayList<>(apis);
Collections.sort(lst, new Comparator<SearchCoreAPI>() {
@Override
@ -459,7 +425,7 @@ public class SearchUICore {
}
try {
api.search(phrase, matcher);
matcher.apiSearchFinished(api, phrase);
} catch (Throwable e) {
e.printStackTrace();
@ -470,16 +436,16 @@ public class SearchUICore {
private void preparePhrase(final SearchPhrase phrase) {
for (SearchWord sw : phrase.getWords()) {
if(sw.getResult() != null && sw.getResult().file != null) {
if (sw.getResult() != null && sw.getResult().file != null) {
phrase.selectFile(sw.getResult().file);
}
}
phrase.sortFiles();
}
public static class SearchResultMatcher implements ResultMatcher<SearchResult>{
private final List<SearchResult> requestResults = new ArrayList<>();
private final ResultMatcher<SearchResult> matcher;
@ -488,32 +454,32 @@ public class SearchUICore {
private SearchResult parentSearchResult;
private final AtomicInteger requestNumber;
int count = 0;
public SearchResultMatcher(ResultMatcher<SearchResult> matcher, int request,
AtomicInteger requestNumber, int totalLimit) {
public SearchResultMatcher(ResultMatcher<SearchResult> matcher, int request,
AtomicInteger requestNumber, int totalLimit) {
this.matcher = matcher;
this.request = request;
this.requestNumber = requestNumber;
this.totalLimit = totalLimit;
}
public SearchResult setParentSearchResult(SearchResult parentSearchResult) {
SearchResult prev = this.parentSearchResult;
this.parentSearchResult = parentSearchResult;
return prev;
}
public List<SearchResult> getRequestResults() {
return requestResults;
}
public int getCount() {
return requestResults.size();
}
public void apiSearchFinished(SearchCoreAPI api, SearchPhrase phrase) {
if(matcher != null) {
if (matcher != null) {
SearchResult sr = new SearchResult(phrase);
sr.objectType = ObjectType.SEARCH_API_FINISHED;
sr.object = api;
@ -521,7 +487,7 @@ public class SearchUICore {
matcher.publish(sr);
}
}
public void apiSearchRegionFinished(SearchCoreAPI api, BinaryMapIndexReader region, SearchPhrase phrase) {
if(matcher != null) {
SearchResult sr = new SearchResult(phrase);
@ -532,14 +498,14 @@ public class SearchUICore {
matcher.publish(sr);
}
}
@Override
public boolean publish(SearchResult object) {
if(matcher == null || matcher.publish(object)) {
if (matcher == null || matcher.publish(object)) {
count++;
object.parentSearchResult = parentSearchResult;
if(totalLimit == -1 || count < totalLimit) {
requestResults.add(object);
if (totalLimit == -1 || count < totalLimit) {
requestResults.add(object);
}
return true;
}
@ -549,38 +515,42 @@ public class SearchUICore {
public boolean isCancelled() {
boolean cancelled = request != requestNumber.get();
return cancelled || (matcher != null && matcher.isCancelled());
}
}
}
public static class SearchResultComparator implements Comparator<SearchResult> {
private SearchPhrase sp;
private Collator collator;
private LatLon loc;
private boolean sortByName;
public SearchResultComparator(SearchPhrase sp) {
this.sp = sp;
this.collator = sp.getCollator();
loc = sp.getLastTokenLocation();
sortByName = sp.isSortByName();
}
@Override
public int compare(SearchResult o1, SearchResult o2) {
if(o1.getFoundWordCount() != o2.getFoundWordCount()) {
if (o1.getFoundWordCount() != o2.getFoundWordCount()) {
return -Algorithms.compare(o1.getFoundWordCount(), o2.getFoundWordCount());
}
double s1 = o1.getSearchDistance(loc);
double s2 = o2.getSearchDistance(loc);
int cmp = Double.compare(s1, s2);
if(cmp != 0) {
return cmp;
if (!sortByName || sp.isEmpty()) {
double s1 = o1.getSearchDistance(loc);
double s2 = o2.getSearchDistance(loc);
int cmp = Double.compare(s1, s2);
if (cmp != 0) {
return cmp;
}
}
int st1 = Algorithms.extractFirstIntegerNumber(o1.localeName);
int st2 = Algorithms.extractFirstIntegerNumber(o2.localeName);
if(st1 != st2) {
if (st1 != st2) {
return Algorithms.compare(st1, st2);
}
cmp = collator.compare(o1.localeName, o2.localeName);
if(cmp != 0) {
int cmp = collator.compare(o1.localeName, o2.localeName);
if (cmp != 0) {
return cmp;
}
if (o1.object instanceof Amenity && o2.object instanceof Amenity) {
@ -592,19 +562,19 @@ public class SearchUICore {
String subType1 = a1.getSubType();
String subType2 = a2.getSubType();
cmp = collator.compare(type1, type2);
if(cmp != 0) {
if (cmp != 0) {
return cmp;
}
cmp = collator.compare(subType1, subType2);
if(cmp != 0) {
if (cmp != 0) {
return cmp;
}
}
s1 = o1.getSearchDistance(loc, 1);
s2 = o2.getSearchDistance(loc, 1);
double s1 = o1.getSearchDistance(loc, 1);
double s2 = o2.getSearchDistance(loc, 1);
return Double.compare(s1, s2);
}
}
}

View file

@ -2,7 +2,7 @@ package net.osmand.search.core;
public enum ObjectType {
// ADDRESS
CITY(true), VILLAGE(true), POSTCODE(true), STREET(true), HOUSE(true),STREET_INTERSECTION(true),
CITY(true), VILLAGE(true), POSTCODE(true), STREET(true), HOUSE(true), STREET_INTERSECTION(true),
// POI
POI_TYPE(false), POI(true),
// LOCATION
@ -10,9 +10,12 @@ public enum ObjectType {
// UI OBJECTS
FAVORITE(true), WPT(true), RECENT_OBJ(true),
REGION(true),
SEARCH_API_FINISHED(false), SEARCH_API_REGION_FINISHED(false),
REGION(true),
SEARCH_API_FINISHED(false),
SEARCH_API_REGION_FINISHED(false),
UNKNOWN_NAME_FILTER(false);
private boolean hasLocation;
private ObjectType(boolean location) {
this.hasLocation = location;
@ -21,8 +24,7 @@ public enum ObjectType {
return hasLocation;
}
public static boolean isAddress(ObjectType t ) {
public static boolean isAddress(ObjectType t) {
return t == CITY || t == VILLAGE || t == POSTCODE || t == STREET || t == HOUSE || t == STREET_INTERSECTION;
}
}

View file

@ -20,4 +20,5 @@ public interface SearchCoreAPI {
*/
public boolean isSearchMoreAvailable(SearchPhrase phrase);
boolean isSearchAvailable(ObjectType[] typesToSearch);
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,16 @@
package net.osmand.search.core;
import net.osmand.Collator;
import net.osmand.CollatorStringMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.StringMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -12,18 +23,6 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import net.osmand.Collator;
import net.osmand.CollatorStringMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.OsmAndCollator;
import net.osmand.StringMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
//immutable object
public class SearchPhrase {
@ -288,7 +287,36 @@ public class SearchPhrase {
public int getRadiusLevel() {
return settings.getRadiusLevel();
}
public ObjectType[] getSearchTypes() {
return settings.getSearchTypes();
}
public boolean isCustomSearch() {
return getSearchTypes() != null;
}
public boolean isSearchTypeAllowed(ObjectType searchType) {
if (getSearchTypes() == null) {
return true;
} else {
for (ObjectType type : getSearchTypes()) {
if (type == searchType) {
return true;
}
}
return false;
}
}
public boolean isEmptyQueryAllowed() {
return settings.isEmptyQueryAllowed();
}
public boolean isSortByName() {
return settings.isSortByName();
}
public SearchPhrase selectWord(SearchResult res) {
return selectWord(res, null, false);
}
@ -405,15 +433,13 @@ public class SearchPhrase {
public boolean isEmpty() {
return words.isEmpty() && unknownSearchPhrase.isEmpty();
}
public SearchWord getLastSelectedWord() {
if(words.isEmpty()) {
return null;
}
return words.get(words.size() - 1);
}
public LatLon getWordLocation() {
for(int i = words.size() - 1; i >= 0; i--) {
@ -436,7 +462,6 @@ public class SearchPhrase {
return settings.getOriginalLocation();
}
public void selectFile(BinaryMapIndexReader object) {
if(indexes == null) {
indexes = new ArrayList<>();

View file

@ -1,22 +1,25 @@
package net.osmand.search.core;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.LatLon;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.LatLon;
// immutable object
public class SearchSettings {
private LatLon originalLocation;
private List<BinaryMapIndexReader> offlineIndexes = new ArrayList<>();
private int radiusLevel = 1;
private int totalLimit = -1;
private String lang;
private boolean transliterateIfMissing;
private ObjectType[] searchTypes;
private boolean emptyQueryAllowed;
private boolean sortByName;
public SearchSettings(SearchSettings s) {
if(s != null) {
this.radiusLevel = s.radiusLevel;
@ -24,6 +27,9 @@ public class SearchSettings {
this.totalLimit = s.totalLimit;
this.offlineIndexes = s.offlineIndexes;
this.originalLocation = s.originalLocation;
this.searchTypes = s.searchTypes;
this.emptyQueryAllowed = s.emptyQueryAllowed;
this.sortByName = s.sortByName;
}
}
@ -85,5 +91,43 @@ public class SearchSettings {
return transliterateIfMissing;
}
public ObjectType[] getSearchTypes() {
return searchTypes;
}
public boolean isCustomSearch() {
return searchTypes != null;
}
public SearchSettings setSearchTypes(ObjectType... searchTypes) {
SearchSettings s = new SearchSettings(this);
s.searchTypes = searchTypes;
return s;
}
public SearchSettings resetSearchTypes() {
SearchSettings s = new SearchSettings(this);
s.searchTypes = null;
return s;
}
public boolean isEmptyQueryAllowed() {
return emptyQueryAllowed;
}
public SearchSettings setEmptyQueryAllowed(boolean emptyQueryAllowed) {
SearchSettings s = new SearchSettings(this);
s.emptyQueryAllowed = emptyQueryAllowed;
return s;
}
public boolean isSortByName() {
return sortByName;
}
public SearchSettings setSortByName(boolean sortByName) {
SearchSettings s = new SearchSettings(this);
s.sortByName = sortByName;
return s;
}
}

View file

@ -74,9 +74,8 @@ import net.osmand.search.SearchUICore;
import net.osmand.search.SearchUICore.SearchResultCollection;
import net.osmand.search.core.ObjectType;
import net.osmand.search.core.SearchCoreAPI;
import net.osmand.search.core.SearchCoreFactory.SearchAddressByNameAPI;
import net.osmand.search.core.SearchCoreFactory.SearchAmenityTypesAPI;
import net.osmand.search.core.SearchCoreFactory.SearchCityByNameAPI;
import net.osmand.search.core.SearchCoreFactory.SearchPostcodeAPI;
import net.osmand.search.core.SearchPhrase;
import net.osmand.search.core.SearchResult;
import net.osmand.search.core.SearchSettings;
@ -468,6 +467,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
if (!searchQuery.equalsIgnoreCase(newQueryText)) {
searchQuery = newQueryText;
if (Algorithms.isEmpty(searchQuery)) {
stopCustomSearch();
searchUICore.resetPhrase();
} else {
runSearch();
@ -491,7 +491,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
String newText = searchUICore.getPhrase().getTextWithoutLastWord();
searchEditText.setText(newText);
searchEditText.setSelection(newText.length());
searchUICore.setSearchApiClass(null);
stopCustomSearch();
} else if (useMapCenter && location != null) {
useMapCenter = false;
centerLatLon = null;
@ -710,7 +710,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
boolean transliterate = app.getSettings().MAP_TRANSLITERATE_NAMES.get();
searchHelper = app.getSearchUICore();
searchUICore = searchHelper.getCore();
searchUICore.setSearchApiClass(null);
stopCustomSearch();
location = app.getLocationProvider().getLastKnownLocation();
@ -882,7 +882,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
searchView.setVisibility(View.GONE);
} else if (!show && tabsView.getVisibility() == View.VISIBLE) {
tabToolbarView.setVisibility(View.GONE);
buttonToolbarView.setVisibility(searchUICore.isInCustomSearch() ? View.GONE : View.VISIBLE);
buttonToolbarView.setVisibility(searchUICore.getSearchSettings().isCustomSearch() ? View.GONE : View.VISIBLE);
tabsView.setVisibility(View.GONE);
searchView.setVisibility(View.VISIBLE);
}
@ -1008,17 +1008,22 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
private void reloadCitiesInternal() {
try {
SearchResultCollection res = searchUICore.shallowSearch(SearchCityByNameAPI.class,
startSearchingCities(false, false);
SearchResultCollection res = searchUICore.shallowSearch(SearchAddressByNameAPI.class,
"", null);
stopCustomSearch();
List<QuickSearchListItem> rows = new ArrayList<>();
rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_building_number,
app.getString(R.string.select_city), new OnClickListener() {
@Override
public void onClick(View v) {
searchEditText.setHint(R.string.type_city_town);
searchUICore.setSearchApiClass(SearchCityByNameAPI.class);
startSearchingCities(true, true);
updateTabbarVisibility(false);
runSearch();
searchEditText.requestFocus();
AndroidUtils.softKeyboardDelayed(searchEditText);
}
}));
rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_postcode,
@ -1026,7 +1031,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
@Override
public void onClick(View v) {
searchEditText.setHint(R.string.type_postcode);
searchUICore.setSearchApiClass(SearchPostcodeAPI.class);
startSearchingPostcodes(true);
mainSearchFragment.getAdapter().clear();
updateTabbarVisibility(false);
searchEditText.requestFocus();
@ -1091,6 +1096,34 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
}
}
private void startSearchingCities(boolean sortByName, boolean searchVillages) {
SearchSettings settings = searchUICore.getSearchSettings().setEmptyQueryAllowed(true).setRadiusLevel(1);
if (sortByName) {
settings = settings.setSortByName(true);
}
if (searchVillages) {
settings = settings.setSearchTypes(ObjectType.CITY, ObjectType.VILLAGE);
} else {
settings = settings.setSearchTypes(ObjectType.CITY);
}
searchUICore.updateSettings(settings);
}
private void startSearchingPostcodes(boolean sortByName) {
SearchSettings settings = searchUICore.getSearchSettings().setSearchTypes(ObjectType.POSTCODE)
.setEmptyQueryAllowed(false).setRadiusLevel(1);
if (sortByName) {
settings = settings.setSortByName(true);
}
searchUICore.updateSettings(settings);
}
private void stopCustomSearch() {
SearchSettings settings = searchUICore.getSearchSettings().resetSearchTypes()
.setEmptyQueryAllowed(false).setSortByName(false).setRadiusLevel(1);
searchUICore.updateSettings(settings);
}
private void runSearch() {
runSearch(searchQuery);
}
@ -1268,7 +1301,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
}
}
searchUICore.selectSearchResult(sr);
searchUICore.setSearchApiClass(null);
stopCustomSearch();
String txt = searchUICore.getPhrase().getText(true);
searchQuery = txt;
searchEditText.setText(txt);

View file

@ -1,8 +1,5 @@
package net.osmand.plus.search;
import java.util.Arrays;
import java.util.List;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.FavouritePoint;
import net.osmand.data.LatLon;
@ -16,10 +13,14 @@ import net.osmand.search.SearchUICore;
import net.osmand.search.SearchUICore.SearchResultCollection;
import net.osmand.search.core.CustomSearchPoiFilter;
import net.osmand.search.core.ObjectType;
import net.osmand.search.core.SearchCoreFactory;
import net.osmand.search.core.SearchCoreFactory.SearchBaseAPI;
import net.osmand.search.core.SearchPhrase;
import net.osmand.search.core.SearchResult;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class QuickSearchHelper implements ResourceListener {
public static final int SEARCH_FAVORITE_API_PRIORITY = 50;
@ -63,76 +64,10 @@ public class QuickSearchHelper implements ResourceListener {
setRepositoriesForSearchUICore(app);
core.init();
// Register favorites search api
core.registerAPI(new SearchCoreFactory.SearchBaseAPI() {
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
@Override
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) {
List<FavouritePoint> favList = app.getFavorites().getFavouritePoints();
for (FavouritePoint point : favList) {
SearchResult sr = new SearchResult(phrase);
sr.localeName = point.getName();
sr.object = point;
sr.priority = SEARCH_FAVORITE_OBJECT_PRIORITY;
sr.objectType = ObjectType.FAVORITE;
sr.location = new LatLon(point.getLatitude(), point.getLongitude());
sr.preferredZoom = 17;
if (phrase.getUnknownSearchWordLength() <= 1 && phrase.isNoSelectedType()) {
resultMatcher.publish(sr);
} else if (phrase.getNameStringMatcher().matches(sr.localeName)) {
resultMatcher.publish(sr);
}
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) {
return -1;
}
return SEARCH_FAVORITE_API_PRIORITY;
}
});
core.registerAPI(new SearchFavoriteAPI(app));
// Register favorites by category search api
core.registerAPI(new SearchCoreFactory.SearchBaseAPI() {
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
@Override
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) {
List<FavouritePoint> favList = app.getFavorites().getFavouritePoints();
for (FavouritePoint point : favList) {
SearchResult sr = new SearchResult(phrase);
sr.localeName = point.getName();
sr.object = point;
sr.priority = SEARCH_FAVORITE_CATEGORY_PRIORITY;
sr.objectType = ObjectType.FAVORITE;
sr.location = new LatLon(point.getLatitude(), point.getLongitude());
sr.preferredZoom = 17;
if (point.getCategory() != null && phrase.getNameStringMatcher().matches(point.getCategory())) {
resultMatcher.publish(sr);
}
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) {
return -1;
}
return SEARCH_FAVORITE_API_CATEGORY_PRIORITY;
}
});
core.registerAPI(new SearchFavoriteByCategoryAPI(app));
// Register WptPt search api
core.registerAPI(new SearchWptAPI(app));
@ -156,11 +91,12 @@ public class QuickSearchHelper implements ResourceListener {
core.getSearchSettings().setOfflineIndexes(Arrays.asList(binaryMapIndexReaderArray));
}
public static class SearchWptAPI extends SearchCoreFactory.SearchBaseAPI {
public static class SearchWptAPI extends SearchBaseAPI {
private OsmandApplication app;
public SearchWptAPI(OsmandApplication app) {
super(ObjectType.WPT);
this.app = app;
}
@ -170,8 +106,10 @@ public class QuickSearchHelper implements ResourceListener {
}
@Override
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) {
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException {
if (!super.search(phrase, resultMatcher)) {
return false;
}
if (phrase.isEmpty()) {
return false;
}
@ -209,11 +147,102 @@ public class QuickSearchHelper implements ResourceListener {
}
}
public static class SearchHistoryAPI extends SearchCoreFactory.SearchBaseAPI {
public static class SearchFavoriteByCategoryAPI extends SearchBaseAPI {
private OsmandApplication app;
public SearchFavoriteByCategoryAPI(OsmandApplication app) {
super(ObjectType.FAVORITE);
this.app = app;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
@Override
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException {
if (!super.search(phrase, resultMatcher)) {
return false;
}
List<FavouritePoint> favList = app.getFavorites().getFavouritePoints();
for (FavouritePoint point : favList) {
SearchResult sr = new SearchResult(phrase);
sr.localeName = point.getName();
sr.object = point;
sr.priority = SEARCH_FAVORITE_CATEGORY_PRIORITY;
sr.objectType = ObjectType.FAVORITE;
sr.location = new LatLon(point.getLatitude(), point.getLongitude());
sr.preferredZoom = 17;
if (point.getCategory() != null && phrase.getNameStringMatcher().matches(point.getCategory())) {
resultMatcher.publish(sr);
}
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) {
return -1;
}
return SEARCH_FAVORITE_API_CATEGORY_PRIORITY;
}
}
public static class SearchFavoriteAPI extends SearchBaseAPI {
private OsmandApplication app;
public SearchFavoriteAPI(OsmandApplication app) {
super(ObjectType.FAVORITE);
this.app = app;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
@Override
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException {
if (!super.search(phrase, resultMatcher)) {
return false;
}
List<FavouritePoint> favList = app.getFavorites().getFavouritePoints();
for (FavouritePoint point : favList) {
SearchResult sr = new SearchResult(phrase);
sr.localeName = point.getName();
sr.object = point;
sr.priority = SEARCH_FAVORITE_OBJECT_PRIORITY;
sr.objectType = ObjectType.FAVORITE;
sr.location = new LatLon(point.getLatitude(), point.getLongitude());
sr.preferredZoom = 17;
if (phrase.getUnknownSearchWordLength() <= 1 && phrase.isNoSelectedType()) {
resultMatcher.publish(sr);
} else if (phrase.getNameStringMatcher().matches(sr.localeName)) {
resultMatcher.publish(sr);
}
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) {
return -1;
}
return SEARCH_FAVORITE_API_PRIORITY;
}
}
public static class SearchHistoryAPI extends SearchBaseAPI {
private OsmandApplication app;
public SearchHistoryAPI(OsmandApplication app) {
super(ObjectType.RECENT_OBJ);
this.app = app;
}
@ -223,7 +252,10 @@ public class QuickSearchHelper implements ResourceListener {
}
@Override
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) {
public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException {
if (!super.search(phrase, resultMatcher)) {
return false;
}
SearchHistoryHelper helper = SearchHistoryHelper.getInstance(app);
List<SearchHistoryHelper.HistoryEntry> points = helper.getHistoryEntries();
int p = 0;