package net.osmand.plus.activities.search; import android.os.AsyncTask; import android.os.Message; import android.view.Gravity; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.AdapterView; import android.widget.Button; import android.widget.FrameLayout; import net.osmand.CollatorStringMatcher; import net.osmand.CollatorStringMatcher.StringMatcherMode; import net.osmand.OsmAndCollator; import net.osmand.ResultMatcher; import net.osmand.data.City; import net.osmand.data.City.CityType; import net.osmand.data.LatLon; import net.osmand.data.Postcode; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.resources.RegionAddressRepository; import net.osmand.util.MapUtils; import java.util.Collection; import java.util.Comparator; import java.util.List; public class SearchCityByNameActivity extends SearchByNameAbstractActivity<City> { private RegionAddressRepository region; private int searchVillagesMode = -1; private Button searchVillages; private OsmandSettings osmandSettings; @Override protected void finishInitializing(List<City> list) { // Show villages if cities are not present in this region if (list != null && list.isEmpty()) { searchVillagesMode = 0; searchVillages.setVisibility(View.GONE); } super.finishInitializing(list); } @Override protected void reset() { //This is really only a "clear input text field", hence do not reset settings here //searchVillagesMode = -1; //osmandSettings.setLastSearchedCity(-1L, "", null); super.reset(); } @Override protected void addFooterViews() { final FrameLayout ll = new FrameLayout(this); searchVillages = new Button(this); android.widget.FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER_HORIZONTAL; searchVillages.setLayoutParams(lp); searchVillages.setText(R.string.search_villages_and_postcodes); ll.addView(searchVillages); searchVillages.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { searchVillagesMode = 1; research(); searchVillages.setVisibility(View.GONE); } }); getListView().addFooterView(ll); } @Override protected Comparator<? super City> createComparator() { final StringMatcherMode startsWith = CollatorStringMatcher.StringMatcherMode.CHECK_ONLY_STARTS_WITH; return new CityComparator(startsWith, getMyApplication().getSettings().MAP_PREFERRED_LOCALE.get(), getMyApplication().getSettings().MAP_TRANSLITERATE_NAMES.get()); } @Override public AsyncTask<Object, ?, ?> getInitializeTask() { return new AsyncTask<Object, City, List<City>>() { @Override protected void onPostExecute(List<City> result) { setLabelText(R.string.incremental_search_city); progress.setVisibility(View.INVISIBLE); finishInitializing(result); } @Override protected void onPreExecute() { setLabelText(R.string.loading_cities); progress.setVisibility(View.VISIBLE); } @Override protected List<City> doInBackground(Object... params) { region = ((OsmandApplication) getApplication()).getResourceManager().getRegionRepository(settings.getLastSearchedRegion()); if (region != null) { // preload cities region.preloadCities(new ResultMatcher<City>() { @Override public boolean publish(City object) { addObjectToInitialList(object); return true; } @Override public boolean isCancelled() { return false; } }); return region.getLoadedCities(); } return null; } }; } @Override protected boolean filterLoop(String query, Collection<City> list) { final boolean[] result = {false}; redefineSearchVillagesMode(query); if (!initializeTaskIsFinished() || !isVillagesSearchEnabled()) { result[0] = super.filterLoop(query, list); if (!result[0] && !isVillagesSearchEnabled()) { setVillagesSearchEnabled(true); } } if (!result[0]) { ResultMatcher<City> resultMatcher = new ResultMatcher<City>() { @Override public boolean isCancelled() { return namesFilter.isCancelled; } @Override public boolean publish(City object) { result[0] = true; Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, object); msg.sendToTarget(); return true; } }; region.fillWithSuggestedCities(query, resultMatcher, isVillagesSearchEnabled(), locationToSearch); } return result[0]; } private void setVillagesSearchEnabled(final boolean enable) { searchVillagesMode = enable ? 0 : -1; uiHandler.post(new Runnable() { @Override public void run() { searchVillages.setVisibility(enable ? View.GONE : View.VISIBLE); } }); } private boolean isVillagesSearchEnabled() { return searchVillagesMode >= 0; } private void redefineSearchVillagesMode(String query) { if (searchVillagesMode == 1) { searchVillagesMode = 0; } else if (searchVillagesMode == 0 && !initialListToFilter.isEmpty() && query.isEmpty()) { setVillagesSearchEnabled(false); } else if (searchVillagesMode == -1 && Postcode.looksLikePostcodeStart(query, region.getCountryName())) { setVillagesSearchEnabled(true); } } @Override public String getText(City obj) { LatLon l = obj.getLocation(); String name = getShortText(obj); if (isVillagesSearchEnabled()) { if (obj.getClosestCity() != null) { name += " - " + obj.getClosestCity().getName(region.getLang(), region.isTransliterateNames()); LatLon loc = obj.getClosestCity().getLocation(); if (loc != null && l != null) { name += " " + OsmAndFormatter.getFormattedDistance((int) MapUtils.getDistance(l, loc), getMyApplication()); } return name; } else { if (obj.getType() != null) { name += " - " + OsmAndFormatter.toPublicString(obj.getType(), getMyApplication()); } } } return name; } @Override public String getShortText(City obj) { String lName = obj.getName(region.getLang(), region.isTransliterateNames()); String name = obj.getName(); if (!lName.equals(name)) { return lName + " / " + name; } return lName; } @Override public void itemSelected(City obj) { settings.setLastSearchedCity(obj.getId(), obj.getName(region.getLang(), region.isTransliterateNames()), obj.getLocation()); if (region.getCityById(obj.getId(), obj.getName(region.getLang(), region.isTransliterateNames())) == null) { region.addCityToPreloadedList(obj); } quitActivity(SearchStreetByNameActivity.class); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } private final class CityComparator implements Comparator<City> { private final StringMatcherMode startsWith; private final net.osmand.Collator cs; private final String lang; private boolean transliterate; private CityComparator(StringMatcherMode startsWith, String lang, boolean transliterate) { this.startsWith = startsWith; this.transliterate = transliterate; this.cs = OsmAndCollator.primaryCollator(); this.lang = lang; } @Override public int compare(City lhs, City rhs) { final String part = getFilter().toString(); int compare = compareCityType(lhs, rhs); if (compare != 0) { return compare; } boolean st1 = CollatorStringMatcher.cmatches(cs, lhs.getName(lang, transliterate), part, startsWith); boolean st2 = CollatorStringMatcher.cmatches(cs, rhs.getName(lang, transliterate), part, startsWith); if (st1 != st2) { return st1 ? 1 : -1; } compare = cs.compare(getText(lhs), getText(rhs)); if (compare != 0) { return compare; } if (locationToSearch != null) { double d1 = MapUtils.getDistance(locationToSearch, lhs.getLocation()); double d2 = MapUtils.getDistance(locationToSearch, rhs.getLocation()); return -Double.compare(d1, d2); } return 0; } private int compareCityType(City lhs, City rhs) { boolean c1 = lhs.getType() == CityType.CITY || lhs.getType() == CityType.TOWN; boolean c2 = rhs.getType() == CityType.CITY || rhs.getType() == CityType.TOWN; if (c1 == c2) { return 0; } return c1 ? 1 : -1; } } }