New address search in progress

This commit is contained in:
Alexey Kulish 2017-03-23 22:23:40 +03:00
parent 82a682dd58
commit a491e854ae
14 changed files with 532 additions and 150 deletions

View file

@ -7,7 +7,6 @@ import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.Amenity;
import net.osmand.data.LatLon;
import net.osmand.osm.AbstractPoiType;
import net.osmand.osm.MapPoiTypes;
import net.osmand.search.core.CustomSearchPoiFilter;
import net.osmand.search.core.ObjectType;
@ -30,7 +29,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@ -44,7 +42,7 @@ public class SearchUICore {
private static final int TIMEOUT_BETWEEN_CHARS = 200;
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;
@ -56,8 +54,11 @@ public class SearchUICore {
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;
taskQueue = new LinkedBlockingQueue<Runnable>();
@ -67,7 +68,19 @@ public class SearchUICore {
currentSearchResult = new SearchResultCollection(phrase);
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;
@ -242,11 +255,16 @@ public class SearchUICore {
@SuppressWarnings("unchecked")
public <T> T getApiByClass(Class<T> cl) {
for(SearchCoreAPI a : apis) {
for (SearchCoreAPI a : apis) {
if(cl.isInstance(a)) {
return (T) a;
}
}
if (cl.isInstance(searchCityByNameAPI)) {
return (T) searchCityByNameAPI;
} else if (cl.isInstance(searchPostcodeAPI)) {
return (T) searchPostcodeAPI;
}
return null;
}
@ -279,7 +297,11 @@ public class SearchUICore {
apis.add(streetsApi);
SearchStreetByCityAPI cityApi = new SearchCoreFactory.SearchStreetByCityAPI(streetsApi);
apis.add(cityApi);
apis.add(new SearchCoreFactory.SearchAddressByNameAPI(streetsApi, cityApi));
SearchCoreFactory.SearchAddressByNameAPI searchAddressByNameAPI
= new SearchCoreFactory.SearchAddressByNameAPI(streetsApi, cityApi);
apis.add(searchAddressByNameAPI);
searchCityByNameAPI = new SearchCoreFactory.SearchCityByNameAPI(searchAddressByNameAPI);
searchPostcodeAPI = new SearchCoreFactory.SearchPostcodeAPI();
}
public void clearCustomSearchPoiFilters() {
@ -414,7 +436,12 @@ public class SearchUICore {
private void searchInBackground(final SearchPhrase phrase, SearchResultMatcher matcher) {
preparePhrase(phrase);
ArrayList<SearchCoreAPI> lst = new ArrayList<>(apis);
ArrayList<SearchCoreAPI> lst = new ArrayList<>();
if (searchApiClass != null) {
lst.add(getApiByClass(searchApiClass));
} else {
lst.addAll(apis);
}
Collections.sort(lst, new Comparator<SearchCoreAPI>() {
@Override
@ -423,11 +450,11 @@ public class SearchUICore {
o2.getSearchPriority(phrase));
}
});
for(SearchCoreAPI api : lst) {
if(matcher.isCancelled()) {
for (SearchCoreAPI api : lst) {
if (matcher.isCancelled()) {
break;
}
if(api.getSearchPriority(phrase) == -1) {
if (api.getSearchPriority(phrase) == -1) {
continue;
}
try {

View file

@ -16,6 +16,7 @@ import net.osmand.data.City;
import net.osmand.data.City.CityType;
import net.osmand.data.LatLon;
import net.osmand.data.MapObject;
import net.osmand.data.Postcode;
import net.osmand.data.QuadRect;
import net.osmand.data.QuadTree;
import net.osmand.data.Street;
@ -43,7 +44,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import gnu.trove.list.array.TIntArrayList;
@ -167,10 +167,116 @@ public class SearchCoreFactory {
}
return retName;
}
public static class SearchPostcodeAPI extends SearchBaseAPI {
@Override
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
if (!phrase.isUnknownSearchWordPresent()) {
return false;
}
if (phrase.isNoSelectedType()) {
final NameStringMatcher nm = phrase.getNameStringMatcher();
final QuadRect bbox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 20);
final QuadRect postcodeBbox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 5);
Iterator<BinaryMapIndexReader> offlineIndexes = phrase.getOfflineIndexes(bbox, SearchPhraseDataType.ADDRESS);
while (offlineIndexes.hasNext()) {
BinaryMapIndexReader r = offlineIndexes.next();
SearchRequest<City> req = BinaryMapIndexReader.buildAddressRequest(new ResultMatcher<City>() {
@Override
public boolean publish(City object) {
return object.isPostcode() && nm.matches(object.getPostcode());
}
@Override
public boolean isCancelled() {
return resultMatcher.isCancelled();
}
});
List<City> l = r.getCities(req, BinaryMapAddressReaderAdapter.POSTCODES_TYPE);
for (City c : l) {
LatLon cl = c.getLocation();
int y = MapUtils.get31TileNumberY(cl.getLatitude());
int x = MapUtils.get31TileNumberX(cl.getLongitude());
if (postcodeBbox.contains(x, y, x, y)) {
SearchResult res = new SearchResult(phrase);
res.object = c;
res.file = (BinaryMapIndexReader) c.getReferenceFile();
res.localeName = c.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.otherNames = c.getAllNames(true);
res.localeRelatedObjectName = res.file.getRegionName();
res.relatedObject = res.file;
res.location = c.getLocation();
res.priority = SEARCH_ADDRESS_BY_NAME_PRIORITY;
res.priorityDistance = 0.1;
res.objectType = ObjectType.POSTCODE;
resultMatcher.publish(res);
}
}
}
}
return true;
}
}
public static class SearchCityByNameAPI extends SearchBaseAPI {
private SearchAddressByNameAPI searchAddressByNameAPI;
private static final int LIMIT = 10000;
public SearchCityByNameAPI(SearchAddressByNameAPI searchAddressByNameAPI) {
this.searchAddressByNameAPI = searchAddressByNameAPI;
}
@Override
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
QuadRect bbox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 20);
QuadRect closestBBox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 3);
if (phrase.isNoSelectedType()) {
searchAddressByNameAPI.initCities(phrase, bbox);
}
NameStringMatcher nm = null;
List<City> cities;
if (phrase.isUnknownSearchWordPresent()) {
nm = phrase.getNameStringMatcher();
cities = searchAddressByNameAPI.townCitiesQR.queryInBox(bbox, new ArrayList<City>());
} else {
cities = searchAddressByNameAPI.townCitiesQR.queryInBox(closestBBox, new ArrayList<City>());
}
int limit = 0;
for (City c : cities) {
SearchResult res = new SearchResult(phrase);
res.object = c;
res.file = (BinaryMapIndexReader) c.getReferenceFile();
res.localeName = c.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.otherNames = c.getAllNames(true);
res.localeRelatedObjectName = res.file.getRegionName();
res.relatedObject = res.file;
res.location = c.getLocation();
res.priority = SEARCH_ADDRESS_BY_NAME_PRIORITY;
res.priorityDistance = 0.1;
res.objectType = ObjectType.CITY;
if (nm == null) {
resultMatcher.publish(res);
} else if (nm.matches(res.localeName) || nm.matches(res.otherNames)) {
resultMatcher.publish(res);
}
if (limit++ > LIMIT * phrase.getRadiusLevel()) {
break;
}
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
return searchAddressByNameAPI.getSearchPriority(p);
}
}
public static class SearchAddressByNameAPI extends SearchBaseAPI {
private static final int DEFAULT_ADDRESS_BBOX_RADIUS = 100*1000;
private static final int DEFAULT_ADDRESS_BBOX_RADIUS = 100 * 1000;
private static final int LIMIT = 10000;
@ -222,17 +328,15 @@ public class SearchCoreFactory {
return true;
}
private void initAndSearchCities(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
QuadRect bbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 20);
protected void initCities(final SearchPhrase phrase, QuadRect bbox) throws IOException {
Iterator<BinaryMapIndexReader> offlineIndexes = phrase.getOfflineIndexes(bbox, SearchPhraseDataType.ADDRESS);
while(offlineIndexes.hasNext()) {
while (offlineIndexes.hasNext()) {
BinaryMapIndexReader r = offlineIndexes.next();
if(!townCities.containsKey(r)) {
if (!townCities.containsKey(r)) {
BinaryMapIndexReader.buildAddressRequest(null);
List<City> l = r.getCities(null, BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE);
townCities.put(r, l);
for(City c : l) {
for (City c : l) {
LatLon cl = c.getLocation();
c.setReferenceFile(r);
int y = MapUtils.get31TileNumberY(cl.getLatitude());
@ -242,6 +346,12 @@ public class SearchCoreFactory {
}
}
}
}
private void initAndSearchCities(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
QuadRect bbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 20);
initCities(phrase, bbox);
if (phrase.isNoSelectedType() && bbox != null && phrase.isUnknownSearchWordPresent()) {
NameStringMatcher nm = phrase.getNameStringMatcher();
resArray.clear();
@ -259,20 +369,19 @@ public class SearchCoreFactory {
res.priority = SEARCH_ADDRESS_BY_NAME_PRIORITY;
res.priorityDistance = 0.1;
res.objectType = ObjectType.CITY;
if(nm.matches(res.localeName) || nm.matches(res.otherNames)) {
if (nm.matches(res.localeName) || nm.matches(res.otherNames)) {
subSearchApiOrPublish(phrase, resultMatcher, res, cityApi);
}
if(limit++ > LIMIT * phrase.getRadiusLevel()) {
if (limit++ > LIMIT * phrase.getRadiusLevel()) {
break;
}
}
}
}
private void searchByName(final SearchPhrase phrase, final SearchResultMatcher resultMatcher)
throws IOException {
if(phrase.getRadiusLevel() > 1 || phrase.getUnknownSearchWordLength() > 3 || phrase.getUnknownSearchWords().size() > 0) {
if (phrase.getRadiusLevel() > 1 || phrase.getUnknownSearchWordLength() > 3 || phrase.getUnknownSearchWords().size() > 0) {
final boolean locSpecified = phrase.getLastTokenLocation() != null;
LatLon loc = phrase.getLastTokenLocation();
final List<SearchResult> immediateResults = new ArrayList<>();
@ -287,7 +396,7 @@ public class SearchCoreFactory {
int limit = 0;
@Override
public boolean publish(MapObject object) {
if(isCancelled()) {
if (isCancelled()) {
return false;
}
SearchResult sr = new SearchResult(phrase);
@ -339,7 +448,7 @@ public class SearchCoreFactory {
return false;
}
City c = null;
if(closestCities == null) {
if (closestCities == null) {
closestCities = townCitiesQR.queryInBox(villagesBbox, new ArrayList<City>());
}
double minDist = -1;
@ -417,7 +526,7 @@ public class SearchCoreFactory {
@Override
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
if(!phrase.isUnknownSearchWordPresent()) {
if (!phrase.isUnknownSearchWordPresent()) {
return false;
}
final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1];
@ -426,10 +535,10 @@ public class SearchCoreFactory {
final NameStringMatcher nm = phrase.getNameStringMatcher();
QuadRect bbox = phrase.getRadiusBBoxToSearch(BBOX_RADIUS_INSIDE);
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
(int)bbox.centerX(), (int)bbox.centerY(),
(int) bbox.centerX(), (int) bbox.centerY(),
phrase.getUnknownSearchWord(),
(int)bbox.left, (int)bbox.right,
(int)bbox.top, (int)bbox.bottom,
(int) bbox.left, (int) bbox.right,
(int) bbox.top, (int) bbox.bottom,
new ResultMatcher<Amenity>() {
int limit = 0;
@Override
@ -759,9 +868,7 @@ public class SearchCoreFactory {
}
}
public static class SearchStreetByCityAPI extends SearchBaseAPI {
private SearchBaseAPI streetsAPI;
@ -955,10 +1062,10 @@ public class SearchCoreFactory {
@Override
public int getSearchPriority(SearchPhrase p) {
if(isLastWordCityGroup(p)) {
if (isLastWordCityGroup(p)) {
return SEARCH_BUILDING_BY_CITY_PRIORITY;
}
if(!p.isLastWord(ObjectType.STREET)) {
if (!p.isLastWord(ObjectType.STREET)) {
return -1;
}
return SEARCH_BUILDING_BY_STREET_PRIORITY;

View file

@ -3,32 +3,45 @@
xmlns:osmand="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="60dp"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp">
android:orientation="vertical">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/imageView"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="16dp"
android:scaleType="centerInside"
android:tint="?attr/color_dialog_buttons"
android:src="@drawable/ic_action_search_dark"/>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="?attr/color_dialog_buttons"
android:textSize="@dimen/default_sub_text_size"
osmand:textAllCapsCompat="true"
osmand:typeface="@string/font_roboto_medium"
android:text="@string/custom_search"/>
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp"
android:minHeight="60dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="16dp"
android:scaleType="centerInside"
android:src="@drawable/ic_action_search_dark"/>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="?attr/color_dialog_buttons"
android:textSize="@dimen/default_sub_text_size"
osmand:textAllCapsCompat="true"
osmand:typeface="@string/font_roboto_medium"/>
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="56dp"
android:background="?attr/dashboard_divider"
android:visibility="gone"/>
</LinearLayout>

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/top_divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/ctx_menu_info_view_bg"
android:orientation="vertical"
android:visibility="gone">
<include layout="@layout/card_bottom_divider"/>
<View
android:layout_width="match_parent"
android:layout_height="8dp"/>
<include layout="@layout/card_top_divider"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:minHeight="48dp"
android:paddingLeft="56dp"
android:paddingRight="16dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_desc_text_size"/>
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="56dp"
android:background="?attr/dashboard_divider"/>
</LinearLayout>

View file

@ -9,6 +9,11 @@
3. All your modified/created strings are in the top of the file (to make easier find what\'s translated).
PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy
-->
<string name="type_city_town">Type city or town</string>
<string name="type_postcode">Type postcode</string>
<string name="nearest_cities">Nearest cities</string>
<string name="select_city">Select city</string>
<string name="select_postcode">Select postcode</string>
<string name="quick_action_auto_zoom">Auto zoom map on/off</string>
<string name="quick_action_auto_zoom_desc">Tapping the action button will turn on/off auto zoom map according to your speed.</string>
<string name="quick_action_auto_zoom_on">Auto zoom map on</string>

View file

@ -5,7 +5,6 @@ import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
@ -65,6 +64,10 @@ import net.osmand.plus.helpers.SearchHistoryHelper;
import net.osmand.plus.helpers.SearchHistoryHelper.HistoryEntry;
import net.osmand.plus.poi.PoiUIFilter;
import net.osmand.plus.search.QuickSearchHelper.SearchHistoryAPI;
import net.osmand.plus.search.listitems.QuickSearchButtonListItem;
import net.osmand.plus.search.listitems.QuickSearchHeaderListItem;
import net.osmand.plus.search.listitems.QuickSearchListItem;
import net.osmand.plus.search.listitems.QuickSearchMoreListItem;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
import net.osmand.search.SearchUICore;
@ -72,6 +75,8 @@ import net.osmand.search.SearchUICore.SearchResultCollection;
import net.osmand.search.core.ObjectType;
import net.osmand.search.core.SearchCoreAPI;
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;
@ -118,6 +123,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
private QuickSearchMainListFragment mainSearchFragment;
private QuickSearchHistoryListFragment historySearchFragment;
private QuickSearchCategoriesListFragment categoriesSearchFragment;
private QuickSearchAddressListFragment addrSearchFragment;
private QuickSearchToolbarController toolbarController;
private Toolbar toolbarEdit;
@ -406,6 +412,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
);
viewPager = (LockableViewPager) view.findViewById(R.id.pager);
viewPager.setOffscreenPageLimit(2);
pagerAdapter = new SearchFragmentPagerAdapter(getChildFragmentManager(), getResources());
viewPager.setAdapter(pagerAdapter);
if (centerLatLon != null || showCategories) {
@ -484,6 +491,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
String newText = searchUICore.getPhrase().getTextWithoutLastWord();
searchEditText.setText(newText);
searchEditText.setSelection(newText.length());
searchUICore.setSearchApiClass(null);
} else if (useMapCenter && location != null) {
useMapCenter = false;
centerLatLon = null;
@ -702,6 +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);
location = app.getLocationProvider().getLastKnownLocation();
@ -873,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(View.VISIBLE);
buttonToolbarView.setVisibility(searchUICore.isInCustomSearch() ? View.GONE : View.VISIBLE);
tabsView.setVisibility(View.GONE);
searchView.setVisibility(View.VISIBLE);
}
@ -899,6 +908,11 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
reloadCategories();
break;
case ADDRESS:
addrSearchFragment = (QuickSearchAddressListFragment) searchListFragment;
reloadCities();
break;
case MAIN:
if (!Algorithms.isEmpty(searchQuery)) {
searchEditText.setText(searchQuery);
@ -951,7 +965,8 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
for (SearchResult sr : res.getCurrentSearchResults()) {
rows.add(new QuickSearchListItem(app, sr));
}
rows.add(new CustomSearchButton(app, new OnClickListener() {
rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_search_dark,
app.getString(R.string.custom_search), new OnClickListener() {
@Override
public void onClick(View v) {
PoiUIFilter filter = app.getPoiFilters().getCustomPOIFilter();
@ -966,7 +981,74 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
e.printStackTrace();
app.showToastMessage(e.getMessage());
}
}
public void reloadCities() {
if (app.isApplicationInitializing()) {
showProgressBar();
app.getAppInitializer().addListener(new AppInitializeListener() {
@Override
public void onProgress(AppInitializer init, AppInitializer.InitEvents event) {
}
@Override
public void onFinish(AppInitializer init) {
if (!paused) {
reloadCitiesInternal();
if (!searching) {
hideProgressBar();
}
}
}
});
} else {
reloadCitiesInternal();
}
}
private void reloadCitiesInternal() {
try {
SearchResultCollection res = searchUICore.shallowSearch(SearchCityByNameAPI.class,
"", null);
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);
updateTabbarVisibility(false);
runSearch();
}
}));
rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_postcode,
app.getString(R.string.select_postcode), new OnClickListener() {
@Override
public void onClick(View v) {
searchEditText.setHint(R.string.type_postcode);
searchUICore.setSearchApiClass(SearchPostcodeAPI.class);
mainSearchFragment.getAdapter().clear();
updateTabbarVisibility(false);
searchEditText.requestFocus();
AndroidUtils.softKeyboardDelayed(searchEditText);
}
}));
if (res != null) {
rows.add(new QuickSearchHeaderListItem(app, app.getString(R.string.nearest_cities), true));
int limit = 15;
for (SearchResult sr : res.getCurrentSearchResults()) {
if (limit > 0) {
rows.add(new QuickSearchListItem(app, sr));
}
limit--;
}
}
addrSearchFragment.updateListAdapter(rows, false);
} catch (IOException e) {
e.printStackTrace();
app.showToastMessage(e.getMessage());
}
}
public void reloadHistory() {
@ -1186,10 +1268,12 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
}
}
searchUICore.selectSearchResult(sr);
searchUICore.setSearchApiClass(null);
String txt = searchUICore.getPhrase().getText(true);
searchQuery = txt;
searchEditText.setText(txt);
searchEditText.setSelection(txt.length());
buttonToolbarView.setVisibility(View.VISIBLE);
updateToolbarButton();
SearchSettings settings = searchUICore.getPhrase().getSettings();
if (settings.getRadiusLevel() != 1) {
@ -1486,10 +1570,16 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
}
public class SearchFragmentPagerAdapter extends FragmentPagerAdapter {
private final String[] fragments = new String[]{QuickSearchHistoryListFragment.class.getName(),
QuickSearchCategoriesListFragment.class.getName()};
private final int[] titleIds = new int[]{QuickSearchHistoryListFragment.TITLE,
QuickSearchCategoriesListFragment.TITLE};
private final String[] fragments = new String[] {
QuickSearchHistoryListFragment.class.getName(),
QuickSearchCategoriesListFragment.class.getName(),
QuickSearchAddressListFragment.class.getName()
};
private final int[] titleIds = new int[]{
QuickSearchHistoryListFragment.TITLE,
QuickSearchCategoriesListFragment.TITLE,
QuickSearchAddressListFragment.TITLE
};
private final String[] titles;
public SearchFragmentPagerAdapter(FragmentManager fm, Resources res) {
@ -1582,6 +1672,15 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
}
}
public static class QuickSearchAddressListFragment extends QuickSearchListFragment {
public static final int TITLE = R.string.address;
@Override
public SearchListFragmentType getType() {
return SearchListFragmentType.ADDRESS;
}
}
public static class QuickSearchMainListFragment extends QuickSearchListFragment {
@Override
@ -1597,27 +1696,4 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC
}
}
public static class CustomSearchButton extends QuickSearchListItem {
private OnClickListener onClickListener;
public CustomSearchButton(OsmandApplication app, OnClickListener onClickListener) {
super(app, null);
this.onClickListener = onClickListener;
}
@Override
public Drawable getIcon() {
return app.getIconsCache().getIcon(R.drawable.ic_action_search_dark);
}
@Override
public String getName() {
return app.getString(R.string.custom_search);
}
public OnClickListener getOnClickListener() {
return onClickListener;
}
}
}

View file

@ -3,6 +3,7 @@ package net.osmand.plus.search;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.view.LayoutInflater;
import android.view.View;
@ -21,7 +22,12 @@ import net.osmand.data.LatLon;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.dashboard.DashLocationFragment;
import net.osmand.plus.search.QuickSearchDialogFragment.CustomSearchButton;
import net.osmand.plus.search.listitems.QuickSearchButtonListItem;
import net.osmand.plus.search.listitems.QuickSearchHeaderListItem;
import net.osmand.plus.search.listitems.QuickSearchListItem;
import net.osmand.plus.search.listitems.QuickSearchListItemType;
import net.osmand.plus.search.listitems.QuickSearchMoreListItem;
import net.osmand.plus.search.listitems.QuickSearchSelectAllListItem;
import net.osmand.search.core.SearchPhrase;
import net.osmand.util.Algorithms;
import net.osmand.util.OpeningHoursParser;
@ -40,24 +46,17 @@ public class QuickSearchListAdapter extends ArrayAdapter<QuickSearchListItem> {
private Float heading;
private boolean useMapCenter;
private int searchMoreItemPosition;
private int selectAllItemPosition;
private int customSearchItemPosition;
private int screenOrientation;
private int dp56;
private int dp1;
private boolean hasSearchMoreItem;
private OnSelectionListener selectionListener;
private boolean selectionMode;
private boolean selectAll;
private List<QuickSearchListItem> selectedItems = new ArrayList<>();
private static final int ITEM_TYPE_REGULAR = 0;
private static final int ITEM_TYPE_SEARCH_MORE = 1;
private static final int ITEM_TYPE_SELECT_ALL = 2;
private static final int ITEM_TYPE_CUSTOM_SEARCH = 3;
public interface OnSelectionListener {
void onUpdateSelectionMode(List<QuickSearchListItem> selectedItems);
@ -150,75 +149,62 @@ public class QuickSearchListAdapter extends ArrayAdapter<QuickSearchListItem> {
public void setListItems(List<QuickSearchListItem> items) {
setNotifyOnChange(false);
clear();
hasSearchMoreItem = false;
for (QuickSearchListItem item : items) {
add(item);
if (!hasSearchMoreItem && item.getType() == QuickSearchListItemType.SEARCH_MORE) {
hasSearchMoreItem = true;
}
}
acquireAdditionalItemsPositions();
setNotifyOnChange(true);
notifyDataSetChanged();
}
public void addListItem(QuickSearchListItem item) {
if (searchMoreItemPosition != -1 && item instanceof QuickSearchMoreListItem) {
public void addListItem(@NonNull QuickSearchListItem item) {
if (hasSearchMoreItem && item.getType() == QuickSearchListItemType.SEARCH_MORE) {
return;
}
setNotifyOnChange(false);
add(item);
acquireAdditionalItemsPositions();
if (item.getType() == QuickSearchListItemType.SEARCH_MORE) {
hasSearchMoreItem = true;
}
setNotifyOnChange(true);
notifyDataSetChanged();
}
public void insertListItem(QuickSearchListItem item, int index) {
public void insertListItem(@NonNull QuickSearchListItem item, int index) {
setNotifyOnChange(false);
insert(item, index);
acquireAdditionalItemsPositions();
if (item.getType() == QuickSearchListItemType.SEARCH_MORE) {
hasSearchMoreItem = true;
}
setNotifyOnChange(true);
notifyDataSetChanged();
}
private void acquireAdditionalItemsPositions() {
selectAllItemPosition = -1;
searchMoreItemPosition = -1;
customSearchItemPosition = -1;
if (getCount() > 0) {
QuickSearchListItem first = getItem(0);
QuickSearchListItem last = getItem(getCount() - 1);
selectAllItemPosition = first instanceof QuickSearchSelectAllListItem ? 0 : -1;
searchMoreItemPosition = last instanceof QuickSearchMoreListItem ? getCount() - 1 : -1;
customSearchItemPosition = last instanceof CustomSearchButton ? getCount() - 1 : -1;
}
}
@Override
public QuickSearchListItem getItem(int position) {
return super.getItem(position);
public boolean isEnabled(int position) {
return getItem(position).getType() != QuickSearchListItemType.HEADER;
}
@Override
public int getItemViewType(int position) {
if (position == searchMoreItemPosition) {
return ITEM_TYPE_SEARCH_MORE;
} else if (position == customSearchItemPosition) {
return ITEM_TYPE_CUSTOM_SEARCH;
} else if (position == selectAllItemPosition) {
return ITEM_TYPE_SELECT_ALL;
} else {
return ITEM_TYPE_REGULAR;
}
return getItem(position).getType().ordinal();
}
@Override
public int getViewTypeCount() {
return 4;
return QuickSearchListItemType.values().length;
}
@NonNull
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
final QuickSearchListItem listItem = getItem(position);
int viewType = getItemViewType(position);
QuickSearchListItemType type = listItem.getType();
LinearLayout view;
if (viewType == ITEM_TYPE_SEARCH_MORE) {
if (type == QuickSearchListItemType.SEARCH_MORE) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) app
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@ -229,7 +215,7 @@ public class QuickSearchListAdapter extends ArrayAdapter<QuickSearchListItem> {
}
((TextView) view.findViewById(R.id.title)).setText(listItem.getName());
} else if (viewType == ITEM_TYPE_CUSTOM_SEARCH) {
} else if (type == QuickSearchListItemType.BUTTON) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) app
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@ -238,7 +224,9 @@ public class QuickSearchListAdapter extends ArrayAdapter<QuickSearchListItem> {
} else {
view = (LinearLayout) convertView;
}
} else if (viewType == ITEM_TYPE_SELECT_ALL) {
((ImageView) view.findViewById(R.id.imageView)).setImageDrawable(listItem.getIcon());
((TextView) view.findViewById(R.id.title)).setText(listItem.getName());
} else if (type == QuickSearchListItemType.SELECT_ALL) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) app
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@ -257,6 +245,18 @@ public class QuickSearchListAdapter extends ArrayAdapter<QuickSearchListItem> {
toggleCheckbox(position, ch);
}
});
} else if (type == QuickSearchListItemType.HEADER) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) app
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = (LinearLayout) inflater.inflate(
R.layout.search_header_list_item, null);
} else {
view = (LinearLayout) convertView;
}
view.findViewById(R.id.top_divider)
.setVisibility(((QuickSearchHeaderListItem)listItem).isShowTopDivider() ? View.VISIBLE : View.GONE);
((TextView) view.findViewById(R.id.title)).setText(listItem.getName());
} else {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) app
@ -344,11 +344,12 @@ public class QuickSearchListAdapter extends ArrayAdapter<QuickSearchListItem> {
app.getSettings().isLightContent() ? R.color.bg_color_light : R.color.bg_color_dark));
View divider = view.findViewById(R.id.divider);
if (divider != null) {
if (position == getCount() - 1) {
if (position == getCount() - 1 || getItem(position + 1).getType() == QuickSearchListItemType.HEADER) {
divider.setVisibility(View.GONE);
} else {
divider.setVisibility(View.VISIBLE);
if (position + 1 == searchMoreItemPosition || position == selectAllItemPosition) {
if (getItem(position + 1).getType() == QuickSearchListItemType.SEARCH_MORE
|| type == QuickSearchListItemType.SELECT_ALL) {
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp1);
p.setMargins(0, 0, 0 ,0);
divider.setLayoutParams(p);
@ -364,13 +365,14 @@ public class QuickSearchListAdapter extends ArrayAdapter<QuickSearchListItem> {
}
public void toggleCheckbox(int position, CheckBox ch) {
int viewType = getItemViewType(position);
if (viewType == ITEM_TYPE_SELECT_ALL) {
QuickSearchListItemType type = getItem(position).getType();
if (type == QuickSearchListItemType.SELECT_ALL) {
selectAll = ch.isChecked();
if (ch.isChecked()) {
selectedItems.clear();
for (int i = 0; i < getCount(); i++) {
if (getItemViewType(i) == ITEM_TYPE_REGULAR) {
QuickSearchListItemType t = getItem(i).getType();
if (t == QuickSearchListItemType.SEARCH_RESULT) {
selectedItems.add(getItem(i));
}
}

View file

@ -29,7 +29,10 @@ import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.OsmAndListFragment;
import net.osmand.plus.dashboard.DashLocationFragment;
import net.osmand.plus.helpers.SearchHistoryHelper.HistoryEntry;
import net.osmand.plus.search.QuickSearchDialogFragment.CustomSearchButton;
import net.osmand.plus.search.listitems.QuickSearchButtonListItem;
import net.osmand.plus.search.listitems.QuickSearchListItem;
import net.osmand.plus.search.listitems.QuickSearchListItemType;
import net.osmand.plus.search.listitems.QuickSearchMoreListItem;
import net.osmand.search.core.ObjectType;
import net.osmand.search.core.SearchResult;
import net.osmand.util.Algorithms;
@ -47,6 +50,7 @@ public abstract class QuickSearchListFragment extends OsmAndListFragment {
enum SearchListFragmentType {
HISTORY,
CATEGORIES,
ADDRESS,
MAIN
}
@ -86,11 +90,11 @@ public abstract class QuickSearchListFragment extends OsmAndListFragment {
if (index < listAdapter.getCount()) {
QuickSearchListItem item = listAdapter.getItem(index);
if (item != null) {
if (item instanceof QuickSearchMoreListItem) {
if (item.getType() == QuickSearchListItemType.SEARCH_MORE) {
((QuickSearchMoreListItem) item).getOnClickListener().onClick(view);
} else if (item instanceof CustomSearchButton) {
((CustomSearchButton) item).getOnClickListener().onClick(view);
} else {
} else if (item.getType() == QuickSearchListItemType.BUTTON) {
((QuickSearchButtonListItem) item).getOnClickListener().onClick(view);
} else if (item.getType() == QuickSearchListItemType.SEARCH_RESULT) {
SearchResult sr = item.getSearchResult();
if (sr.objectType == ObjectType.POI

View file

@ -0,0 +1,45 @@
package net.osmand.plus.search.listitems;
import android.graphics.drawable.Drawable;
import android.view.View;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
public class QuickSearchButtonListItem extends QuickSearchListItem {
private int iconId;
private String title;
private View.OnClickListener onClickListener;
private int colorId;
public QuickSearchButtonListItem(OsmandApplication app, int iconId, String title, View.OnClickListener onClickListener) {
super(app, null);
this.iconId = iconId;
this.title = title;
this.onClickListener = onClickListener;
this.colorId = app.getSettings().isLightContent() ? R.color.color_dialog_buttons_light : R.color.color_dialog_buttons_dark;
}
public QuickSearchListItemType getType() {
return QuickSearchListItemType.BUTTON;
}
@Override
public Drawable getIcon() {
if (iconId != 0) {
return app.getIconsCache().getIcon(iconId, colorId);
} else {
return null;
}
}
@Override
public String getName() {
return title;
}
public View.OnClickListener getOnClickListener() {
return onClickListener;
}
}

View file

@ -0,0 +1,32 @@
package net.osmand.plus.search.listitems;
import net.osmand.plus.OsmandApplication;
public class QuickSearchHeaderListItem extends QuickSearchListItem {
private String title;
private boolean showTopDivider;
public QuickSearchHeaderListItem(OsmandApplication app, String title, boolean showTopDivider) {
super(app, null);
this.title = title;
this.showTopDivider = showTopDivider;
}
public QuickSearchListItemType getType() {
return QuickSearchListItemType.HEADER;
}
public String getTitle() {
return title;
}
public boolean isShowTopDivider() {
return showTopDivider;
}
@Override
public String getName() {
return title;
}
}

View file

@ -1,4 +1,4 @@
package net.osmand.plus.search;
package net.osmand.plus.search.listitems;
import android.content.Context;
import android.graphics.drawable.Drawable;
@ -41,6 +41,10 @@ public class QuickSearchListItem {
this.searchResult = searchResult;
}
public QuickSearchListItemType getType() {
return QuickSearchListItemType.SEARCH_RESULT;
}
public SearchResult getSearchResult() {
return searchResult;
}

View file

@ -0,0 +1,9 @@
package net.osmand.plus.search.listitems;
public enum QuickSearchListItemType {
SEARCH_RESULT,
HEADER,
BUTTON,
SEARCH_MORE,
SELECT_ALL
}

View file

@ -1,4 +1,4 @@
package net.osmand.plus.search;
package net.osmand.plus.search.listitems;
import android.view.View.OnClickListener;
@ -15,6 +15,10 @@ public class QuickSearchMoreListItem extends QuickSearchListItem {
this.onClickListener = onClickListener;
}
public QuickSearchListItemType getType() {
return QuickSearchListItemType.SEARCH_MORE;
}
@Override
public String getName() {
return name;

View file

@ -1,4 +1,4 @@
package net.osmand.plus.search;
package net.osmand.plus.search.listitems;
import android.view.View;
@ -15,6 +15,10 @@ public class QuickSearchSelectAllListItem extends QuickSearchListItem {
this.onClickListener = onClickListener;
}
public QuickSearchListItemType getType() {
return QuickSearchListItemType.SELECT_ALL;
}
@Override
public String getName() {
return name;