package net.osmand.plus.search; import android.annotation.SuppressLint; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentTransaction; import android.support.v4.content.FileProvider; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.Spannable; import android.text.SpannableString; import android.text.TextWatcher; import android.text.style.ForegroundColorSpan; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import net.osmand.AndroidUtils; import net.osmand.Location; import net.osmand.PlatformUtil; import net.osmand.ResultMatcher; import net.osmand.access.AccessibilityAssistant; import net.osmand.access.NavigationInfo; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.data.City; import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.osm.AbstractPoiType; import net.osmand.osm.PoiCategory; import net.osmand.osm.PoiType; import net.osmand.plus.AppInitializer; import net.osmand.plus.AppInitializer.AppInitializeListener; import net.osmand.plus.FavouritesDbHelper; import net.osmand.plus.GPXUtilities; import net.osmand.plus.GPXUtilities.GPXFile; import net.osmand.plus.GPXUtilities.WptPt; import net.osmand.plus.LockableViewPager; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndLocationProvider.OsmAndCompassListener; import net.osmand.plus.OsmAndLocationProvider.OsmAndLocationListener; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity.ShowQuickSearchMode; import net.osmand.plus.helpers.SearchHistoryHelper; import net.osmand.plus.helpers.SearchHistoryHelper.HistoryEntry; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.resources.RegionAddressRepository; 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; 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.SearchPhrase; import net.osmand.search.core.SearchResult; import net.osmand.search.core.SearchSettings; import net.osmand.search.core.SearchWord; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import static net.osmand.search.core.ObjectType.POI_TYPE; import static net.osmand.search.core.ObjectType.SEARCH_STARTED; import static net.osmand.search.core.SearchCoreFactory.SEARCH_AMENITY_TYPE_PRIORITY; public class QuickSearchDialogFragment extends DialogFragment implements OsmAndCompassListener, OsmAndLocationListener { private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(QuickSearchDialogFragment.class); public static final String TAG = "QuickSearchDialogFragment"; private static final String QUICK_SEARCH_QUERY_KEY = "quick_search_query_key"; private static final String QUICK_SEARCH_LAT_KEY = "quick_search_lat_key"; private static final String QUICK_SEARCH_LON_KEY = "quick_search_lon_key"; private static final String QUICK_SEARCH_INTERRUPTED_SEARCH_KEY = "quick_search_interrupted_search_key"; private static final String QUICK_SEARCH_HIDDEN_KEY = "quick_search_hidden_key"; private static final String QUICK_SEARCH_TOOLBAR_TITLE_KEY = "quick_search_toolbar_title_key"; private static final String QUICK_SEARCH_TOOLBAR_VISIBLE_KEY = "quick_search_toolbar_visible_key"; private static final String QUICK_SEARCH_FAB_VISIBLE_KEY = "quick_search_fab_visible_key"; private static final String QUICK_SEARCH_RUN_SEARCH_FIRST_TIME_KEY = "quick_search_run_search_first_time_key"; private static final String QUICK_SEARCH_PHRASE_DEFINED_KEY = "quick_search_phrase_defined_key"; private static final String QUICK_SEARCH_SHOW_TAB_KEY = "quick_search_show_tab_key"; private static final String QUICK_SEARCH_TYPE_KEY = "quick_search_type_key"; private Toolbar toolbar; private LockableViewPager viewPager; private SearchFragmentPagerAdapter pagerAdapter; private TabLayout tabLayout; private View tabToolbarView; private View tabsView; private View searchView; private View buttonToolbarView; private ImageView buttonToolbarImage; private ImageButton buttonToolbarFilter; private TextView buttonToolbarText; private QuickSearchMainListFragment mainSearchFragment; private QuickSearchHistoryListFragment historySearchFragment; private QuickSearchCategoriesListFragment categoriesSearchFragment; private QuickSearchAddressListFragment addressSearchFragment; private QuickSearchToolbarController toolbarController; private Toolbar toolbarEdit; private TextView titleEdit; private View fab; private EditText searchEditText; private ProgressBar progressBar; private ImageButton clearButton; private AccessibilityAssistant accessibilityAssistant; private NavigationInfo navigationInfo; private OsmandApplication app; private QuickSearchHelper searchHelper; private SearchUICore searchUICore; private SearchResultListener defaultResultListener; private String searchQuery; private LatLon centerLatLon; private net.osmand.Location location = null; private Float heading = null; private boolean useMapCenter; private boolean paused; private boolean cancelPrev; private boolean searching; private boolean hidden; private boolean foundPartialLocation; private String toolbarTitle; private boolean toolbarVisible; private boolean newSearch; private boolean interruptedSearch; private long hideTimeMs; private boolean expired; private boolean poiFilterApplied; private boolean fabVisible; private boolean runSearchFirstTime; private boolean phraseDefined; private boolean addressSearch; private boolean citiesLoaded; private LatLon storedOriginalLocation; private QuickSearchType searchType = QuickSearchType.REGULAR; private static final double DISTANCE_THRESHOLD = 70000; // 70km private static final int EXPIRATION_TIME_MIN = 10; // 10 minutes public enum QuickSearchTab { HISTORY, CATEGORIES, ADDRESS, } public enum QuickSearchType { REGULAR, START_POINT, DESTINATION, } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); app = getMyApplication(); navigationInfo = new NavigationInfo(app); accessibilityAssistant = new AccessibilityAssistant(getActivity()); boolean isLightTheme = app.getSettings().OSMAND_THEME.get() == OsmandSettings.OSMAND_LIGHT_THEME; int themeId = isLightTheme ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme; setStyle(STYLE_NO_FRAME, themeId); } @Override @SuppressLint("PrivateResource, ValidFragment") public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final MapActivity mapActivity = getMapActivity(); final View view = inflater.inflate(R.layout.search_dialog_fragment, container, false); toolbarController = new QuickSearchToolbarController(); toolbarController.setOnBackButtonClickListener(new OnClickListener() { @Override public void onClick(View v) { mapActivity.showQuickSearch(ShowQuickSearchMode.CURRENT, false); } }); toolbarController.setOnTitleClickListener(new OnClickListener() { @Override public void onClick(View v) { mapActivity.showQuickSearch(ShowQuickSearchMode.CURRENT, false); } }); toolbarController.setOnCloseButtonClickListener(new OnClickListener() { @Override public void onClick(View v) { mapActivity.closeQuickSearch(); } }); Bundle arguments = getArguments(); if (savedInstanceState != null) { searchType = QuickSearchType.valueOf(savedInstanceState.getString(QUICK_SEARCH_TYPE_KEY, QuickSearchType.REGULAR.name())); searchQuery = savedInstanceState.getString(QUICK_SEARCH_QUERY_KEY); double lat = savedInstanceState.getDouble(QUICK_SEARCH_LAT_KEY, Double.NaN); double lon = savedInstanceState.getDouble(QUICK_SEARCH_LON_KEY, Double.NaN); if (!Double.isNaN(lat) && !Double.isNaN(lon)) { centerLatLon = new LatLon(lat, lon); } interruptedSearch = savedInstanceState.getBoolean(QUICK_SEARCH_INTERRUPTED_SEARCH_KEY, false); hidden = savedInstanceState.getBoolean(QUICK_SEARCH_HIDDEN_KEY, false); toolbarTitle = savedInstanceState.getString(QUICK_SEARCH_TOOLBAR_TITLE_KEY); toolbarVisible = savedInstanceState.getBoolean(QUICK_SEARCH_TOOLBAR_VISIBLE_KEY, false); fabVisible = savedInstanceState.getBoolean(QUICK_SEARCH_FAB_VISIBLE_KEY, false); } if (searchQuery == null && arguments != null) { searchType = QuickSearchType.valueOf(arguments.getString(QUICK_SEARCH_TYPE_KEY, QuickSearchType.REGULAR.name())); searchQuery = arguments.getString(QUICK_SEARCH_QUERY_KEY); runSearchFirstTime = arguments.getBoolean(QUICK_SEARCH_RUN_SEARCH_FIRST_TIME_KEY, false); phraseDefined = arguments.getBoolean(QUICK_SEARCH_PHRASE_DEFINED_KEY, false); double lat = arguments.getDouble(QUICK_SEARCH_LAT_KEY, Double.NaN); double lon = arguments.getDouble(QUICK_SEARCH_LON_KEY, Double.NaN); if (!Double.isNaN(lat) && !Double.isNaN(lon)) { centerLatLon = new LatLon(lat, lon); } newSearch = true; } if (searchQuery == null) searchQuery = ""; QuickSearchTab showSearchTab = QuickSearchTab.HISTORY; if (arguments != null) { showSearchTab = QuickSearchTab.valueOf(arguments.getString(QUICK_SEARCH_SHOW_TAB_KEY, QuickSearchTab.HISTORY.name())); } if (showSearchTab == QuickSearchTab.ADDRESS) { addressSearch = true; } tabToolbarView = view.findViewById(R.id.tab_toolbar_layout); tabsView = view.findViewById(R.id.tabs_view); searchView = view.findViewById(R.id.search_view); buttonToolbarView = view.findViewById(R.id.button_toolbar_layout); buttonToolbarImage = (ImageView) view.findViewById(R.id.buttonToolbarImage); buttonToolbarImage.setImageDrawable(app.getIconsCache().getThemedIcon(R.drawable.ic_action_marker_dark)); buttonToolbarFilter = (ImageButton) view.findViewById(R.id.filterButton); buttonToolbarFilter.setImageDrawable(app.getIconsCache().getThemedIcon(R.drawable.ic_action_filter)); buttonToolbarFilter.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SearchPhrase searchPhrase = searchUICore.getPhrase(); if (searchPhrase.isLastWord(POI_TYPE)) { String filterId = null; String filterByName = searchPhrase.getUnknownSearchPhrase().trim(); Object object = searchPhrase.getLastSelectedWord().getResult().object; if (object instanceof PoiUIFilter) { PoiUIFilter model = (PoiUIFilter) object; if (!Algorithms.isEmpty(model.getSavedFilterByName())) { model.setFilterByName(model.getSavedFilterByName()); } filterId = model.getFilterId(); } else if (object instanceof AbstractPoiType) { AbstractPoiType abstractPoiType = (AbstractPoiType) object; PoiUIFilter custom = app.getPoiFilters().getFilterById(PoiUIFilter.STD_PREFIX + abstractPoiType.getKeyName()); if (custom != null) { custom.setFilterByName(null); custom.clearFilter(); custom.updateTypesToAccept(abstractPoiType); filterId = custom.getFilterId(); } } if (filterId != null) { QuickSearchPoiFilterFragment.showDialog( QuickSearchDialogFragment.this, filterByName, filterId); } } } }); buttonToolbarText = (TextView) view.findViewById(R.id.buttonToolbarTitle); view.findViewById(R.id.buttonToolbar).setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { SearchPhrase searchPhrase = searchUICore.getPhrase(); if (foundPartialLocation) { QuickSearchCoordinatesFragment.showDialog(QuickSearchDialogFragment.this, searchPhrase.getUnknownSearchWord()); } else if (searchPhrase.isNoSelectedType() || searchPhrase.isLastWord(POI_TYPE)) { PoiUIFilter filter; if (searchPhrase.isNoSelectedType()) { filter = app.getPoiFilters().getSearchByNamePOIFilter(); if (!Algorithms.isEmpty(searchPhrase.getUnknownSearchWord())) { filter.setFilterByName(searchPhrase.getUnknownSearchWord()); filter.clearCurrentResults(); } } else if (searchPhrase.getLastSelectedWord().getResult().object instanceof AbstractPoiType) { if (searchPhrase.isNoSelectedType()) { filter = new PoiUIFilter(null, app, ""); } else { AbstractPoiType abstractPoiType = (AbstractPoiType) searchPhrase.getLastSelectedWord() .getResult().object; filter = new PoiUIFilter(abstractPoiType, app, ""); } if (!Algorithms.isEmpty(searchPhrase.getUnknownSearchWord())) { filter.setFilterByName(searchPhrase.getUnknownSearchWord()); } } else { filter = (PoiUIFilter) searchPhrase.getLastSelectedWord().getResult().object; } app.getPoiFilters().clearSelectedPoiFilters(); app.getPoiFilters().addSelectedPoiFilter(filter); mapActivity.getContextMenu().closeActiveToolbar(); showToolbar(); getMapActivity().refreshMap(); hide(); } else { SearchWord word = searchPhrase.getLastSelectedWord(); if (word != null) { if ((searchType == QuickSearchType.START_POINT || searchType == QuickSearchType.DESTINATION) && word.getLocation() != null) { if (mainSearchFragment != null) { mainSearchFragment.showResult(word.getResult()); } } else if (word.getLocation() != null) { SearchResult searchResult = word.getResult(); String name = QuickSearchListItem.getName(app, searchResult); String typeName = QuickSearchListItem.getTypeName(app, searchResult); PointDescription pointDescription = new PointDescription(PointDescription.POINT_TYPE_ADDRESS, typeName, name); app.getSettings().setMapLocationToShow( searchResult.location.getLatitude(), searchResult.location.getLongitude(), searchResult.preferredZoom, pointDescription, true, searchResult.object); hideToolbar(); MapActivity.launchMapActivityMoveToTop(getActivity()); reloadHistory(); hide(); } else if (word.getType() == ObjectType.FAVORITE_GROUP) { FavouritesDbHelper.FavoriteGroup group = (FavouritesDbHelper.FavoriteGroup) word.getResult().object; if (group.points.size() > 1) { double left = 0, right = 0; double top = 0, bottom = 0; for (FavouritePoint p : group.points) { if (left == 0) { left = p.getLongitude(); right = p.getLongitude(); top = p.getLatitude(); bottom = p.getLatitude(); } else { left = Math.min(left, p.getLongitude()); right = Math.max(right, p.getLongitude()); top = Math.max(top, p.getLatitude()); bottom = Math.min(bottom, p.getLatitude()); } } getMapActivity().getMapView().fitRectToMap(left, right, top, bottom, 0, 0, 0); hideToolbar(); MapActivity.launchMapActivityMoveToTop(getActivity()); hide(); } else if (group.points.size() == 1) { FavouritePoint p = group.points.get(0); app.getSettings().setMapLocationToShow(p.getLatitude(), p.getLongitude(), word.getResult().preferredZoom); hideToolbar(); MapActivity.launchMapActivityMoveToTop(getActivity()); hide(); } } } } } } ); toolbar = (Toolbar) view.findViewById(R.id.toolbar); toolbar.setNavigationIcon(app.getIconsCache().getThemedIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha)); toolbar.setNavigationContentDescription(R.string.access_shared_string_navigate_up); toolbar.setNavigationOnClickListener( new OnClickListener() { @Override public void onClick(View v) { if (!processBackAction()) { dismiss(); } } } ); toolbarEdit = (Toolbar) view.findViewById(R.id.toolbar_edit); toolbarEdit.setNavigationIcon(app.getIconsCache().getIcon(R.drawable.ic_action_remove_dark)); toolbarEdit.setNavigationContentDescription(R.string.shared_string_cancel); toolbarEdit.setNavigationOnClickListener( new OnClickListener() { @Override public void onClick(View v) { enableSelectionMode(false, -1); } } ); titleEdit = (TextView) view.findViewById(R.id.titleEdit); view.findViewById(R.id.shareButton).setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { List<HistoryEntry> historyEntries = new ArrayList<HistoryEntry>(); List<QuickSearchListItem> selectedItems = historySearchFragment.getListAdapter().getSelectedItems(); for (QuickSearchListItem searchListItem : selectedItems) { HistoryEntry historyEntry = (HistoryEntry) searchListItem.getSearchResult().object; historyEntries.add(historyEntry); } if (historyEntries.size() > 0) { shareHistory(historyEntries); enableSelectionMode(false, -1); } } } ); view.findViewById(R.id.deleteButton).setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { new DialogFragment() { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.confirmation_to_delete_history_items) .setPositiveButton(R.string.shared_string_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { SearchHistoryHelper helper = SearchHistoryHelper.getInstance(app); List<QuickSearchListItem> selectedItems = historySearchFragment.getListAdapter().getSelectedItems(); for (QuickSearchListItem searchListItem : selectedItems) { HistoryEntry historyEntry = (HistoryEntry) searchListItem.getSearchResult().object; helper.remove(historyEntry); } reloadHistory(); enableSelectionMode(false, -1); } }) .setNegativeButton(R.string.shared_string_no, null); return builder.create(); } }.show(getChildFragmentManager(), "DeleteHistoryConfirmationFragment"); } } ); viewPager = (LockableViewPager) view.findViewById(R.id.pager); viewPager.setOffscreenPageLimit(2); pagerAdapter = new SearchFragmentPagerAdapter(getChildFragmentManager(), getResources()); viewPager.setAdapter(pagerAdapter); switch (showSearchTab) { case HISTORY: viewPager.setCurrentItem(0); break; case CATEGORIES: viewPager.setCurrentItem(1); break; case ADDRESS: viewPager.setCurrentItem(2); break; } tabLayout = (TabLayout) view.findViewById(R.id.tab_layout); tabLayout.setupWithViewPager(viewPager); viewPager.addOnPageChangeListener( new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { hideKeyboard(); addressSearch = position == 2; updateClearButtonAndHint(); if (addressSearch && !citiesLoaded) { reloadCities(); } else { restoreSearch(); } } @Override public void onPageScrollStateChanged(int state) { } } ); searchEditText = (EditText) view.findViewById(R.id.searchEditText); searchEditText.addTextChangedListener( new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { String newQueryText = s.toString(); updateClearButtonAndHint(); updateClearButtonVisibility(true); boolean textEmpty = newQueryText.length() == 0; updateTabbarVisibility(textEmpty); if (textEmpty && addressSearch) { startAddressSearch(); } if (textEmpty && poiFilterApplied) { poiFilterApplied = false; reloadCategories(); if (fabVisible) { fabVisible = false; updateFab(); } } if (!searchQuery.equalsIgnoreCase(newQueryText)) { searchQuery = newQueryText; if (Algorithms.isEmpty(searchQuery)) { searchUICore.resetPhrase(); } else { runSearch(); } } else if (runSearchFirstTime) { runSearchFirstTime = false; runSearch(); } } } ); progressBar = (ProgressBar) view.findViewById(R.id.searchProgressBar); clearButton = (ImageButton) view.findViewById(R.id.clearButton); clearButton.setImageDrawable(app.getIconsCache().getThemedIcon(R.drawable.ic_action_remove_dark)); clearButton.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { if (searchEditText.getText().length() > 0) { String newText = searchUICore.getPhrase().getTextWithoutLastWord(); searchEditText.setText(newText); searchEditText.setSelection(newText.length()); } else if (useMapCenter && location != null) { useMapCenter = false; centerLatLon = null; updateUseMapCenterUI(); LatLon centerLatLon = new LatLon(location.getLatitude(), location.getLongitude()); SearchSettings ss = searchUICore.getSearchSettings().setOriginalLocation( new LatLon(centerLatLon.getLatitude(), centerLatLon.getLongitude())); searchUICore.updateSettings(ss); updateClearButtonAndHint(); updateClearButtonVisibility(true); startLocationUpdate(); } updateToolbarButton(); } } ); fab = view.findViewById(R.id.fab); fab.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { saveCustomFilter(); } }); updateFab(); setupSearch(mapActivity); return view; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); updateToolbarButton(); updateClearButtonAndHint(); updateClearButtonVisibility(true); addMainSearchFragment(); if (centerLatLon == null) { openKeyboard(); } } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new Dialog(getActivity(), getTheme()){ @Override public void onBackPressed() { if (!processBackAction()) { cancel(); } } }; } public void saveCustomFilter() { final OsmandApplication app = getMyApplication(); final PoiUIFilter filter = app.getPoiFilters().getCustomPOIFilter(); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setTitle(R.string.access_hint_enter_name); final EditText editText = new EditText(getContext()); editText.setHint(R.string.new_filter); final TextView textView = new TextView(getContext()); textView.setText(app.getString(R.string.new_filter_desc)); textView.setTextAppearance(getContext(), R.style.TextAppearance_ContextMenuSubtitle); LinearLayout ll = new LinearLayout(getContext()); ll.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); ll.setOrientation(LinearLayout.VERTICAL); ll.setPadding(AndroidUtils.dpToPx(getContext(), 20f), AndroidUtils.dpToPx(getContext(), 12f), AndroidUtils.dpToPx(getContext(), 20f), AndroidUtils.dpToPx(getContext(), 12f)); ll.addView(editText, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); textView.setPadding(AndroidUtils.dpToPx(getContext(), 4f), AndroidUtils.dpToPx(getContext(), 6f), AndroidUtils.dpToPx(getContext(), 4f), AndroidUtils.dpToPx(getContext(), 4f)); ll.addView(textView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); builder.setView(ll); builder.setNegativeButton(R.string.shared_string_cancel, null); builder.setPositiveButton(R.string.shared_string_save, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { PoiUIFilter nFilter = new PoiUIFilter(editText.getText().toString(), null, filter.getAcceptedTypes(), app); if (!Algorithms.isEmpty(filter.getFilterByName())) { nFilter.setSavedFilterByName(filter.getFilterByName()); } if (app.getPoiFilters().createPoiFilter(nFilter)) { Toast.makeText(getContext(), MessageFormat.format(getContext().getText(R.string.edit_filter_create_message).toString(), editText.getText().toString()), Toast.LENGTH_SHORT).show(); app.getSearchUICore().refreshCustomPoiFilters(); replaceQueryWithUiFilter(nFilter, ""); reloadCategories(); fabVisible = false; updateFab(); } } }); builder.create().show(); } public void restoreToolbar() { if (toolbarVisible) { if (toolbarTitle != null) { showToolbar(toolbarTitle); } else { showToolbar(); } } } public void showToolbar() { showToolbar(getText()); } public void showToolbar(String title) { toolbarVisible = true; toolbarTitle = title; toolbarController.setTitle(toolbarTitle); getMapActivity().showTopToolbar(toolbarController); } public void hideToolbar() { toolbarVisible = false; getMapActivity().hideTopToolbar(toolbarController); } public QuickSearchType getSearchType() { return searchType; } public String getText() { return searchEditText.getText().toString(); } public AccessibilityAssistant getAccessibilityAssistant() { return accessibilityAssistant; } public NavigationInfo getNavigationInfo() { return navigationInfo; } public void hideKeyboard() { if (searchEditText.hasFocus()) { AndroidUtils.hideSoftKeyboard(getActivity(), searchEditText); } } public boolean isSearchHidden() { return hidden; } public boolean isExpired() { return expired || (hideTimeMs > 0 && System.currentTimeMillis() - hideTimeMs > EXPIRATION_TIME_MIN * 60 * 1000); } public void show() { if (useMapCenter) { LatLon mapCenter = getMapActivity().getMapView().getCurrentRotatedTileBox().getCenterLatLon(); SearchSettings ss = searchUICore.getSearchSettings().setOriginalLocation( new LatLon(mapCenter.getLatitude(), mapCenter.getLongitude())); searchUICore.updateSettings(ss); updateUseMapCenterUI(); updateLocationUI(mapCenter, null); } app.getLocationProvider().removeCompassListener(app.getLocationProvider().getNavigationInfo()); getDialog().show(); paused = false; hidden = false; if (interruptedSearch) { addMoreButton(); interruptedSearch = false; } } public void hide() { paused = true; hidden = true; expired = searchType != QuickSearchType.REGULAR; hideTimeMs = System.currentTimeMillis(); interruptedSearch = searching; searching = false; hideProgressBar(); updateClearButtonVisibility(true); getDialog().hide(); app.getLocationProvider().addCompassListener(app.getLocationProvider().getNavigationInfo()); } public void closeSearch() { app.getPoiFilters().clearSelectedPoiFilters(); dismiss(); } public void addMainSearchFragment() { mainSearchFragment = (QuickSearchMainListFragment) Fragment.instantiate(this.getContext(), QuickSearchMainListFragment.class.getName()); FragmentManager childFragMan = getChildFragmentManager(); FragmentTransaction childFragTrans = childFragMan.beginTransaction(); childFragTrans.replace(R.id.search_view, mainSearchFragment); childFragTrans.commit(); } private void updateToolbarButton() { SearchWord word = searchUICore.getPhrase().getLastSelectedWord(); if (foundPartialLocation) { buttonToolbarText.setText(app.getString(R.string.advanced_coords_search).toUpperCase()); } else if (searchEditText.getText().length() > 0) { if (searchType == QuickSearchType.START_POINT || searchType == QuickSearchType.DESTINATION) { if (word != null && word.getResult() != null) { buttonToolbarText.setText(app.getString(R.string.shared_string_select).toUpperCase() + " " + word.getResult().localeName.toUpperCase()); } else { buttonToolbarText.setText(app.getString(R.string.shared_string_select).toUpperCase()); } } else { if (word != null && word.getResult() != null) { buttonToolbarText.setText(app.getString(R.string.show_something_on_map, word.getResult().localeName).toUpperCase()); } else { buttonToolbarText.setText(app.getString(R.string.shared_string_show_on_map).toUpperCase()); } } } else { buttonToolbarText.setText(app.getString(R.string.shared_string_show_on_map).toUpperCase()); } boolean filterButtonVisible = word != null && word.getType() != null && word.getType().equals(POI_TYPE); buttonToolbarFilter.setVisibility(filterButtonVisible ? View.VISIBLE : View.GONE); if (filterButtonVisible) { if (word.getResult().object instanceof PoiUIFilter) { buttonToolbarFilter.setImageDrawable(app.getIconsCache().getIcon(R.drawable.ic_action_filter, app.getSettings().isLightContent() ? R.color.color_dialog_buttons_light : R.color.color_dialog_buttons_dark)); } else{ buttonToolbarFilter.setImageDrawable(app.getIconsCache().getThemedIcon(R.drawable.ic_action_filter)); } } } private void setupSearch(final MapActivity mapActivity) { // Setup search core String locale = app.getSettings().MAP_PREFERRED_LOCALE.get(); boolean transliterate = app.getSettings().MAP_TRANSLITERATE_NAMES.get(); searchHelper = app.getSearchUICore(); searchUICore = searchHelper.getCore(); defaultResultListener = new SearchResultListener() { @Override public void publish(SearchResultCollection res, boolean append) { updateSearchResult(res, append); } @Override public void searchStarted(SearchPhrase phrase) { } @Override public boolean searchFinished(SearchPhrase phrase) { return true; } }; stopAddressSearch(); location = app.getLocationProvider().getLastKnownLocation(); LatLon searchLatLon; if (centerLatLon == null) { LatLon clt = mapActivity.getMapView().getCurrentRotatedTileBox().getCenterLatLon(); searchLatLon = clt; searchEditText.setHint(R.string.search_poi_category_hint); if (location != null) { double d = MapUtils.getDistance(clt, location.getLatitude(), location.getLongitude()); if (d < DISTANCE_THRESHOLD) { searchLatLon = new LatLon(location.getLatitude(), location.getLongitude()); } else { useMapCenter = true; } } else { useMapCenter = true; } } else { searchLatLon = centerLatLon; useMapCenter = true; } SearchSettings settings = searchUICore.getSearchSettings().setOriginalLocation( new LatLon(searchLatLon.getLatitude(), searchLatLon.getLongitude())); settings = settings.setLang(locale, transliterate); searchUICore.updateSettings(settings); if (newSearch) { setResultCollection(null); if (!phraseDefined) { searchUICore.resetPhrase(); } phraseDefined = false; } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setShowsDialog(true); } @Override public void onSaveInstanceState(Bundle outState) { outState.putString(QUICK_SEARCH_TYPE_KEY, searchType.name()); outState.putString(QUICK_SEARCH_QUERY_KEY, searchQuery); outState.putBoolean(QUICK_SEARCH_INTERRUPTED_SEARCH_KEY, interruptedSearch = searching); outState.putBoolean(QUICK_SEARCH_HIDDEN_KEY, hidden); if (toolbarTitle != null) { outState.putString(QUICK_SEARCH_TOOLBAR_TITLE_KEY, toolbarTitle); } outState.putBoolean(QUICK_SEARCH_TOOLBAR_VISIBLE_KEY, toolbarVisible); if (centerLatLon != null) { outState.putDouble(QUICK_SEARCH_LAT_KEY, centerLatLon.getLatitude()); outState.putDouble(QUICK_SEARCH_LON_KEY, centerLatLon.getLongitude()); } outState.putBoolean(QUICK_SEARCH_FAB_VISIBLE_KEY, fabVisible); } @Override public void onResume() { super.onResume(); if (!useMapCenter) { startLocationUpdate(); } expired = false; paused = false; } @Override public void onPause() { super.onPause(); paused = true; hideTimeMs = System.currentTimeMillis(); stopLocationUpdate(); hideProgressBar(); } @Override public void onDismiss(DialogInterface dialog) { MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { hideToolbar(); getChildFragmentManager().popBackStack(); } super.onDismiss(dialog); } private boolean processBackAction() { if (addressSearch && isSearchViewVisible()) { searchEditText.setText(""); return true; } return false; } public Toolbar getToolbar() { return toolbar; } public boolean isUseMapCenter() { return useMapCenter; } private void startLocationUpdate() { app.getLocationProvider().removeCompassListener(app.getLocationProvider().getNavigationInfo()); app.getLocationProvider().addCompassListener(this); app.getLocationProvider().addLocationListener(this); location = app.getLocationProvider().getLastKnownLocation(); updateLocation(location); } private void stopLocationUpdate() { app.getLocationProvider().removeLocationListener(this); app.getLocationProvider().removeCompassListener(this); app.getLocationProvider().addCompassListener(app.getLocationProvider().getNavigationInfo()); } private void showProgressBar() { updateClearButtonVisibility(false); progressBar.setVisibility(View.VISIBLE); } private void hideProgressBar() { updateClearButtonVisibility(true); progressBar.setVisibility(View.GONE); } private void updateClearButtonAndHint() { if (useMapCenter && location != null && searchEditText.length() == 0) { LatLon latLon = searchUICore.getSearchSettings().getOriginalLocation(); double d = MapUtils.getDistance(latLon, location.getLatitude(), location.getLongitude()); String dist = OsmAndFormatter.getFormattedDistance((float) d, app); searchEditText.setHint(getString(R.string.dist_away_from_my_location, dist)); clearButton.setImageDrawable(app.getIconsCache().getIcon(R.drawable.ic_action_get_my_location, R.color.color_myloc_distance)); } else { if (addressSearch) { searchEditText.setHint(R.string.type_address); } else { searchEditText.setHint(R.string.search_poi_category_hint); } clearButton.setImageDrawable(app.getIconsCache().getThemedIcon(R.drawable.ic_action_remove_dark)); } } private void updateClearButtonVisibility(boolean show) { if (show) { clearButton.setVisibility(searchEditText.length() > 0 || (useMapCenter && location != null) ? View.VISIBLE : View.GONE); } else { clearButton.setVisibility(View.GONE); } } private void updateTabbarVisibility(boolean show) { if (show && tabsView.getVisibility() == View.GONE) { tabToolbarView.setVisibility(View.VISIBLE); buttonToolbarView.setVisibility(View.GONE); tabsView.setVisibility(View.VISIBLE); searchView.setVisibility(View.GONE); } else if (!show && tabsView.getVisibility() == View.VISIBLE) { tabToolbarView.setVisibility(View.GONE); buttonToolbarView.setVisibility(searchUICore.getSearchSettings().isCustomSearch() ? View.GONE : View.VISIBLE); tabsView.setVisibility(View.GONE); searchView.setVisibility(View.VISIBLE); } } private boolean isSearchViewVisible() { return searchView.getVisibility() == View.VISIBLE; } public void setResultCollection(SearchResultCollection resultCollection) { searchHelper.setResultCollection(resultCollection); } public SearchResultCollection getResultCollection() { return searchHelper.getResultCollection(); } public boolean isResultEmpty() { SearchResultCollection res = getResultCollection(); return res == null || res.getCurrentSearchResults().size() == 0; } public void onSearchListFragmentResume(QuickSearchListFragment searchListFragment) { switch (searchListFragment.getType()) { case HISTORY: historySearchFragment = (QuickSearchHistoryListFragment) searchListFragment; reloadHistory(); break; case CATEGORIES: categoriesSearchFragment = (QuickSearchCategoriesListFragment) searchListFragment; reloadCategories(); break; case ADDRESS: addressSearchFragment = (QuickSearchAddressListFragment) searchListFragment; if (addressSearch && !citiesLoaded) { reloadCities(); } break; case MAIN: if (!Algorithms.isEmpty(searchQuery)) { searchEditText.setText(searchQuery); searchEditText.setSelection(searchQuery.length()); } if (getResultCollection() != null) { updateSearchResult(getResultCollection(), false); if (interruptedSearch || searchUICore.isSearchMoreAvailable(searchUICore.getPhrase())) { addMoreButton(); } } break; } LatLon mapCenter = getMapActivity().getMapView().getCurrentRotatedTileBox().getCenterLatLon(); if (useMapCenter) { updateUseMapCenterUI(); searchListFragment.updateLocation(mapCenter, null); } } public void reloadCategories() { 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) { reloadCategoriesInternal(); if (!searching) { hideProgressBar(); } } } }); } else { reloadCategoriesInternal(); } } private void reloadCategoriesInternal() { try { LOG.info("+++ start loading categories"); SearchResultCollection res = searchUICore.shallowSearch(SearchAmenityTypesAPI.class, "", null); if (res != null) { List<QuickSearchListItem> rows = new ArrayList<>(); for (SearchResult sr : res.getCurrentSearchResults()) { rows.add(new QuickSearchListItem(app, sr)); } 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(); filter.clearFilter(); QuickSearchCustomPoiFragment.showDialog( QuickSearchDialogFragment.this, filter.getFilterId()); } })); categoriesSearchFragment.updateListAdapter(rows, false); } LOG.info("--- categories loaded"); } catch (IOException e) { 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() { LOG.info("+++ start loading nearest cities"); startNearestCitySearch(); runCoreSearch("", false, false, new SearchResultListener() { @Override public void searchStarted(SearchPhrase phrase) { } @Override public void publish(SearchResultCollection res, boolean append) { } @Override public boolean searchFinished(SearchPhrase phrase) { SearchResultCollection res = getResultCollection(); LOG.info("--- nearest cities found: " + (res != null ? res.getCurrentSearchResults().size() : 0)); final OsmandSettings settings = app.getSettings(); List<QuickSearchListItem> rows = new ArrayList<>(); LOG.info("+++ start last city searching (within nearests)"); SearchResult lastCity = null; if (res != null) { citiesLoaded = res.getCurrentSearchResults().size() > 0; final long lastCityId = settings.getLastSearchedCity(); for (SearchResult sr : res.getCurrentSearchResults()) { if (sr.objectType == ObjectType.CITY && ((City) sr.object).getId() == lastCityId) { lastCity = sr; break; } } } LOG.info("--- last city found: " + (lastCity != null ? lastCity.localeName : "-")); final String lastCityName = lastCity == null ? settings.getLastSearchedCityName() : lastCity.localeName; if (!Algorithms.isEmpty(lastCityName)) { String selectStreets = app.getString(R.string.select_street); String inCityName = app.getString(R.string.shared_string_in_name, lastCityName); Spannable spannable = new SpannableString(selectStreets + " " + inCityName); boolean light = settings.isLightContent(); spannable.setSpan(new ForegroundColorSpan(getResources().getColor(light ? R.color.icon_color : R.color.color_white)), selectStreets.length() + 1, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); final SearchResult lastCityFinal = lastCity; rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_street_name, spannable, new OnClickListener() { @Override public void onClick(View v) { if (lastCityFinal == null) { final long lastCityId = settings.getLastSearchedCity(); final LatLon lastCityPoint = settings.getLastSearchedPoint(); if (lastCityId != -1 && lastCityPoint != null) { startLastCitySearch(lastCityPoint); LOG.info("+++ start last city searching (standalone)"); runCoreSearch("", false, false, new SearchResultListener() { boolean cityFound = false; @Override public void publish(SearchResultCollection res, boolean append) { if (res != null) { for (SearchResult sr : res.getCurrentSearchResults()) { if (sr.objectType == ObjectType.CITY && ((City) sr.object).getId() == lastCityId) { LOG.info("--- last city found: " + sr.localeName); cityFound = true; completeQueryWithObject(sr); break; } } } } @Override public void searchStarted(SearchPhrase phrase) { } @Override public boolean searchFinished(SearchPhrase phrase) { if (!cityFound) { replaceQueryWithText(lastCityName + " "); } return false; } }); restoreSearch(); } else { replaceQueryWithText(lastCityName + " "); } } else { completeQueryWithObject(lastCityFinal); } openKeyboard(); } })); } 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); startCitySearch(); updateTabbarVisibility(false); runCoreSearch("", false, false); openKeyboard(); } })); 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); startPostcodeSearch(); mainSearchFragment.getAdapter().clear(); updateTabbarVisibility(false); openKeyboard(); } })); rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_marker_dark, app.getString(R.string.coords_search), new OnClickListener() { @Override public void onClick(View v) { LatLon latLon = searchUICore.getSearchSettings().getOriginalLocation(); QuickSearchCoordinatesFragment.showDialog(QuickSearchDialogFragment.this, latLon.getLatitude(), latLon.getLongitude()); } })); 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--; } } addressSearchFragment.updateListAdapter(rows, false); LOG.info("--- nearest cities loaded"); return true; } }); restoreSearch(); } public void reloadHistory() { 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) { reloadHistoryInternal(); if (!searching) { hideProgressBar(); } } } }); } else { reloadHistoryInternal(); } } private void reloadHistoryInternal() { if (historySearchFragment != null) { try { SearchResultCollection res = searchUICore.shallowSearch(SearchHistoryAPI.class, "", null); List<QuickSearchListItem> rows = new ArrayList<>(); if (res != null) { for (SearchResult sr : res.getCurrentSearchResults()) { rows.add(new QuickSearchListItem(app, sr)); } } historySearchFragment.updateListAdapter(rows, false); } catch (Exception e) { e.printStackTrace(); app.showToastMessage(e.getMessage()); } } } private void restoreSearch() { if (addressSearch) { startAddressSearch(); } else { stopAddressSearch(); } if (storedOriginalLocation != null) { // Restore previous search location searchUICore.updateSettings(searchUICore.getSearchSettings().setOriginalLocation(storedOriginalLocation)); storedOriginalLocation = null; } } private void startAddressSearch() { SearchSettings settings = searchUICore.getSearchSettings() .setEmptyQueryAllowed(true) .setAddressSearch(true) .setSortByName(false) .setSearchTypes(ObjectType.CITY, ObjectType.VILLAGE, ObjectType.POSTCODE, ObjectType.HOUSE, ObjectType.STREET_INTERSECTION, ObjectType.STREET, ObjectType.LOCATION, ObjectType.PARTIAL_LOCATION) .setRadiusLevel(1); searchUICore.updateSettings(settings); } private void startCitySearch() { SearchSettings settings = searchUICore.getSearchSettings() .setEmptyQueryAllowed(true) .setAddressSearch(true) .setSortByName(true) .setSearchTypes(ObjectType.CITY, ObjectType.VILLAGE) .setRadiusLevel(1); searchUICore.updateSettings(settings); } private void startNearestCitySearch() { SearchSettings settings = searchUICore.getSearchSettings() .setEmptyQueryAllowed(true) .setAddressSearch(true) .setSortByName(false) .setSearchTypes(ObjectType.CITY) .setRadiusLevel(1); searchUICore.updateSettings(settings); } private void startLastCitySearch(LatLon latLon) { SearchSettings settings = searchUICore.getSearchSettings(); storedOriginalLocation = settings.getOriginalLocation(); settings = settings.setEmptyQueryAllowed(true) .setAddressSearch(true) .setSortByName(false) .setSearchTypes(ObjectType.CITY) .setOriginalLocation(latLon) .setRadiusLevel(1); searchUICore.updateSettings(settings); } private void startPostcodeSearch() { SearchSettings settings = searchUICore.getSearchSettings() .setSearchTypes(ObjectType.POSTCODE) .setEmptyQueryAllowed(false) .setAddressSearch(true) .setSortByName(true) .setRadiusLevel(1); searchUICore.updateSettings(settings); } private void stopAddressSearch() { SearchSettings settings = searchUICore.getSearchSettings() .resetSearchTypes() .setEmptyQueryAllowed(false) .setSortByName(false) .setAddressSearch(false) .setRadiusLevel(1); searchUICore.updateSettings(settings); } private void runSearch() { runSearch(searchQuery); } private void runSearch(String text) { showProgressBar(); SearchSettings settings = searchUICore.getSearchSettings(); if (settings.getRadiusLevel() != 1) { searchUICore.updateSettings(settings.setRadiusLevel(1)); } runCoreSearch(text, true, false); } private void runCoreSearch(final String text, final boolean showQuickResult, final boolean searchMore) { runCoreSearch(text, showQuickResult, searchMore, defaultResultListener); } private void runCoreSearch(final String text, final boolean showQuickResult, final boolean searchMore, final SearchResultListener resultListener) { showProgressBar(); foundPartialLocation = false; updateToolbarButton(); interruptedSearch = false; searching = true; cancelPrev = true; if (app.isApplicationInitializing() && text.length() > 0) { app.getAppInitializer().addListener(new AppInitializeListener() { @Override public void onProgress(AppInitializer init, AppInitializer.InitEvents event) { } @Override public void onFinish(AppInitializer init) { if (!paused) { runCoreSearchInternal(text, showQuickResult, searchMore, resultListener); } } }); } else { runCoreSearchInternal(text, showQuickResult, searchMore, resultListener); } } private void runCoreSearchInternal(String text, boolean showQuickResult, boolean searchMore, final SearchResultListener resultListener) { SearchResultCollection c = searchUICore.search(text, showQuickResult, new ResultMatcher<SearchResult>() { SearchResultCollection regionResultCollection = null; SearchCoreAPI regionResultApi = null; List<SearchResult> results = new ArrayList<>(); @Override public boolean publish(final SearchResult object) { if (object.objectType == SEARCH_STARTED) { cancelPrev = false; } if (paused || cancelPrev) { if (results.size() > 0) { app.runInUIThread(new Runnable() { @Override public void run() { SearchResultCollection collection = getResultCollection(); if (collection != null) { collection.addSearchResults(results, true, true); } } }); } return false; } switch (object.objectType) { case SEARCH_STARTED: if (resultListener != null) { app.runInUIThread(new Runnable() { @Override public void run() { resultListener.searchStarted(object.requiredSearchPhrase); } }); } break; case SEARCH_FINISHED: app.runInUIThread(new Runnable() { @Override public void run() { if(paused) { return; } searching = false; if (resultListener == null || resultListener.searchFinished(object.requiredSearchPhrase)) { hideProgressBar(); if (searchUICore.isSearchMoreAvailable(object.requiredSearchPhrase)) { addMoreButton(); } } } }); break; case SEARCH_API_FINISHED: final SearchCoreAPI searchApi = (SearchCoreAPI) object.object; final List<SearchResult> apiResults; final SearchPhrase phrase = object.requiredSearchPhrase; final SearchCoreAPI regionApi = regionResultApi; final SearchResultCollection regionCollection = regionResultCollection; final boolean hasRegionCollection = (searchApi == regionApi && regionCollection != null); if (hasRegionCollection) { apiResults = regionCollection.getCurrentSearchResults(); } else { apiResults = results; } regionResultApi = null; regionResultCollection = null; results = new ArrayList<>(); showApiResults(apiResults, phrase, hasRegionCollection, resultListener); break; case SEARCH_API_REGION_FINISHED: regionResultApi = (SearchCoreAPI) object.object; final SearchPhrase regionPhrase = object.requiredSearchPhrase; regionResultCollection = new SearchResultCollection(regionPhrase).addSearchResults(results, true, true); showRegionResults(regionResultCollection, resultListener); break; case PARTIAL_LOCATION: showLocationToolbar(); break; default: results.add(object); } return false; } @Override public boolean isCancelled() { return paused || cancelPrev; } }); if (!searchMore) { setResultCollection(null); if (!showQuickResult) { updateSearchResult(null, false); } } if (showQuickResult) { updateSearchResult(c, false); } } private void showLocationToolbar() { app.runInUIThread(new Runnable() { @Override public void run() { foundPartialLocation = true; updateToolbarButton(); } }); } private void showApiResults(final List<SearchResult> apiResults, final SearchPhrase phrase, final boolean hasRegionCollection, final SearchResultListener resultListener) { app.runInUIThread(new Runnable() { @Override public void run() { if (!paused && !cancelPrev) { boolean append = getResultCollection() != null; if (append) { getResultCollection().addSearchResults(apiResults, true, true); } else { SearchResultCollection resCollection = new SearchResultCollection(phrase); resCollection.addSearchResults(apiResults, true, true); setResultCollection(resCollection); } if (!hasRegionCollection && resultListener != null) { resultListener.publish(getResultCollection(), append); } } } }); } private void showRegionResults(final SearchResultCollection regionResultCollection, final SearchResultListener resultListener) { app.runInUIThread(new Runnable() { @Override public void run() { if (!paused && !cancelPrev) { if (getResultCollection() != null) { SearchResultCollection resCollection = getResultCollection().combineWithCollection(regionResultCollection, true, true); if (resultListener != null) { resultListener.publish(resCollection, true); } } else if (resultListener != null) { resultListener.publish(regionResultCollection, false); } } } }); } public void completeQueryWithObject(SearchResult sr) { if (sr.object instanceof PoiType && ((PoiType) sr.object).isAdditional()) { PoiType additional = (PoiType) sr.object; AbstractPoiType parent = additional.getParentType(); if (parent != null) { PoiUIFilter custom = app.getPoiFilters().getFilterById(PoiUIFilter.STD_PREFIX + parent.getKeyName()); if (custom != null) { custom.clearFilter(); custom.updateTypesToAccept(parent); custom.setFilterByName(additional.getKeyName().replace('_', ':').toLowerCase()); SearchPhrase phrase = searchUICore.getPhrase(); sr = new SearchResult(phrase); sr.localeName = custom.getName(); sr.object = custom; sr.priority = SEARCH_AMENITY_TYPE_PRIORITY; sr.priorityDistance = 0; sr.objectType = ObjectType.POI_TYPE; } } } searchUICore.selectSearchResult(sr); if (addressSearch) { startAddressSearch(); if (sr.objectType == ObjectType.CITY) { if (sr.relatedObject != null && sr.relatedObject instanceof BinaryMapIndexReader) { File f = ((BinaryMapIndexReader) sr.relatedObject).getFile(); if (f != null) { RegionAddressRepository region = app.getResourceManager().getRegionRepository(f.getName()); if (region != null) { app.getSettings().setLastSearchedRegion(region.getFileName(), region.getEstimatedRegionCenter()); City city = (City) sr.object; app.getSettings().setLastSearchedCity(city.getId(), sr.localeName, city.getLocation()); } } } } } String txt = searchUICore.getPhrase().getText(true); replaceQueryWithText(txt); if (sr.objectType == ObjectType.CITY) { openKeyboard(); } } private void openKeyboard() { searchEditText.requestFocus(); AndroidUtils.softKeyboardDelayed(searchEditText); } public void replaceQueryWithText(String txt) { searchQuery = txt; searchEditText.setText(txt); searchEditText.setSelection(txt.length()); SearchWord lastWord = searchUICore.getPhrase().getLastSelectedWord(); boolean buttonToolbarVisible = lastWord == null || searchType == QuickSearchType.REGULAR || ((searchType == QuickSearchType.START_POINT || searchType == QuickSearchType.DESTINATION) && ObjectType.isAddress(lastWord.getType())); buttonToolbarView.setVisibility(buttonToolbarVisible ? View.VISIBLE : View.GONE); updateToolbarButton(); SearchSettings settings = searchUICore.getSearchSettings(); if (settings.getRadiusLevel() != 1) { searchUICore.updateSettings(settings.setRadiusLevel(1)); } runCoreSearch(txt, false, false); } public void replaceQueryWithUiFilter(PoiUIFilter filter, String nameFilter) { SearchPhrase searchPhrase = searchUICore.getPhrase(); if (searchPhrase.isLastWord(POI_TYPE)) { poiFilterApplied = true; SearchResult sr = searchPhrase.getLastSelectedWord().getResult(); sr.object = filter; sr.localeName = filter.getName(); searchUICore.getPhrase().syncWordsWithResults(); String txt = filter.getName() + (!Algorithms.isEmpty(nameFilter) && filter.isStandardFilter() ? " " + nameFilter : " "); searchQuery = txt; searchEditText.setText(txt); searchEditText.setSelection(txt.length()); updateToolbarButton(); runCoreSearch(txt, false, false); } } public void clearLastWord() { if (searchEditText.getText().length() > 0) { String newText = searchUICore.getPhrase().getTextWithoutLastWord(); searchEditText.setText(newText); searchEditText.setSelection(newText.length()); } } private void addMoreButton() { if (!paused && !cancelPrev && mainSearchFragment != null) { QuickSearchMoreListItem moreListItem = new QuickSearchMoreListItem(app, null, new OnClickListener() { @Override public void onClick(View v) { if (!interruptedSearch) { SearchSettings settings = searchUICore.getSearchSettings(); searchUICore.updateSettings(settings.setRadiusLevel(settings.getRadiusLevel() + 1)); } runCoreSearch(searchQuery, false, true); } }); moreListItem.setInterruptedSearch(interruptedSearch); moreListItem.setEmptySearch(isResultEmpty()); mainSearchFragment.addListItem(moreListItem); } } private void updateSearchResult(SearchResultCollection res, boolean append) { if (!paused && mainSearchFragment != null) { List<QuickSearchListItem> rows = new ArrayList<>(); if (res != null && res.getCurrentSearchResults().size() > 0) { for (final SearchResult sr : res.getCurrentSearchResults()) { rows.add(new QuickSearchListItem(app, sr)); } } mainSearchFragment.updateListAdapter(rows, append); } } public static boolean showInstance(@NonNull MapActivity mapActivity, @NonNull String searchQuery, @Nullable Object object, QuickSearchType searchType, QuickSearchTab showSearchTab, @Nullable LatLon latLon) { try { if (mapActivity.isActivityDestroyed()) { return false; } mapActivity.getMyApplication().logEvent(mapActivity, "search_open"); Bundle bundle = new Bundle(); if (object != null) { bundle.putBoolean(QUICK_SEARCH_RUN_SEARCH_FIRST_TIME_KEY, true); String objectLocalizedName = searchQuery; if (object instanceof PoiCategory) { PoiCategory c = (PoiCategory) object; objectLocalizedName = c.getTranslation(); SearchUICore searchUICore = mapActivity.getMyApplication().getSearchUICore().getCore(); SearchPhrase phrase = searchUICore.resetPhrase(objectLocalizedName + " "); SearchResult sr = new SearchResult(phrase); sr.localeName = objectLocalizedName; sr.object = c; sr.priority = SEARCH_AMENITY_TYPE_PRIORITY; sr.priorityDistance = 0; sr.objectType = ObjectType.POI_TYPE; searchUICore.selectSearchResult(sr); bundle.putBoolean(QUICK_SEARCH_PHRASE_DEFINED_KEY, true); } searchQuery = objectLocalizedName.trim() + " "; } else if (!Algorithms.isEmpty(searchQuery)) { bundle.putBoolean(QUICK_SEARCH_RUN_SEARCH_FIRST_TIME_KEY, true); } bundle.putString(QUICK_SEARCH_QUERY_KEY, searchQuery); bundle.putString(QUICK_SEARCH_SHOW_TAB_KEY, showSearchTab.name()); bundle.putString(QUICK_SEARCH_TYPE_KEY, searchType.name()); if (latLon != null) { bundle.putDouble(QUICK_SEARCH_LAT_KEY, latLon.getLatitude()); bundle.putDouble(QUICK_SEARCH_LON_KEY, latLon.getLongitude()); } QuickSearchDialogFragment fragment = new QuickSearchDialogFragment(); fragment.setArguments(bundle); fragment.show(mapActivity.getSupportFragmentManager(), TAG); return true; } catch (RuntimeException e) { return false; } } private MapActivity getMapActivity() { return (MapActivity) getActivity(); } private OsmandApplication getMyApplication() { return (OsmandApplication) getActivity().getApplication(); } @Override public void updateCompassValue(final float value) { // 99 in next line used to one-time initialize arrows (with reference vs. fixed-north direction) // on non-compass devices float lastHeading = heading != null ? heading : 99; heading = value; if (Math.abs(MapUtils.degreesDiff(lastHeading, heading)) > 5) { final Location location = this.location; app.runInUIThread(new Runnable() { @Override public void run() { updateLocationUI(location, value); } }); } else { heading = lastHeading; } } @Override public void updateLocation(final Location location) { final Float heading = this.heading; app.runInUIThread(new Runnable() { @Override public void run() { updateLocationUI(location, heading); } }); } private void updateLocationUI(Location location, Float heading) { this.location = location; LatLon latLon = null; if (location != null) { latLon = new LatLon(location.getLatitude(), location.getLongitude()); navigationInfo.updateLocation(location); } updateLocationUI(latLon, heading); } private void updateLocationUI(LatLon latLon, Float heading) { if (latLon != null && !paused && !cancelPrev) { if (mainSearchFragment != null && searchView.getVisibility() == View.VISIBLE) { mainSearchFragment.updateLocation(latLon, heading); } else if (historySearchFragment != null && viewPager.getCurrentItem() == 0) { historySearchFragment.updateLocation(latLon, heading); } else if (categoriesSearchFragment != null && viewPager.getCurrentItem() == 1) { categoriesSearchFragment.updateLocation(latLon, heading); } else if (addressSearchFragment != null && viewPager.getCurrentItem() == 2) { addressSearchFragment.updateLocation(latLon, heading); } } } private void updateUseMapCenterUI() { if (!paused && !cancelPrev) { if (mainSearchFragment != null) { mainSearchFragment.getListAdapter().setUseMapCenter(useMapCenter); } if (historySearchFragment != null) { historySearchFragment.getListAdapter().setUseMapCenter(useMapCenter); } if (categoriesSearchFragment != null) { categoriesSearchFragment.getListAdapter().setUseMapCenter(useMapCenter); } if (addressSearchFragment != null) { addressSearchFragment.getListAdapter().setUseMapCenter(useMapCenter); } } } public void enableSelectionMode(boolean selectionMode, int position) { historySearchFragment.setSelectionMode(selectionMode, position); tabToolbarView.setVisibility(selectionMode ? View.GONE : View.VISIBLE); buttonToolbarView.setVisibility(View.GONE); toolbar.setVisibility(selectionMode ? View.GONE : View.VISIBLE); toolbarEdit.setVisibility(selectionMode ? View.VISIBLE : View.GONE); viewPager.setSwipeLocked(selectionMode); } public void updateSelectionMode(List<QuickSearchListItem> selectedItems) { if (selectedItems.size() > 0) { String text = selectedItems.size() + " " + app.getString(R.string.shared_string_selected_lowercase); titleEdit.setText(text); } else { titleEdit.setText(""); } } private void shareHistory(final List<HistoryEntry> historyEntries) { if (!historyEntries.isEmpty()) { final AsyncTask<Void, Void, GPXFile> exportTask = new AsyncTask<Void, Void, GPXFile>() { @Override protected GPXFile doInBackground(Void... params) { GPXFile gpx = new GPXFile(); for (HistoryEntry h : historyEntries) { WptPt pt = new WptPt(); pt.lat = h.getLat(); pt.lon = h.getLon(); pt.name = h.getName().getName(); boolean hasTypeInDescription = !Algorithms.isEmpty(h.getName().getTypeName()); if (hasTypeInDescription) { pt.desc = h.getName().getTypeName(); } gpx.points.add(pt); } return gpx; } @Override protected void onPreExecute() { showProgressBar(); } @Override protected void onPostExecute(GPXFile gpxFile) { hideProgressBar(); File dir = new File(getActivity().getCacheDir(), "share"); if (!dir.exists()) { dir.mkdir(); } File dst = new File(dir, "History.gpx"); GPXUtilities.writeGpxFile(dst, gpxFile, app); final Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, "History.gpx:\n\n\n" + GPXUtilities.asString(gpxFile, app)); sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_history_subject)); sendIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".fileprovider", dst)); sendIntent.setType("text/plain"); startActivity(sendIntent); } }; exportTask.execute(); } } public void showFilter(@NonNull String filterId) { PoiUIFilter filter = app.getPoiFilters().getFilterById(filterId); boolean isCustomFilter = filterId.equals(app.getPoiFilters().getCustomPOIFilter().getFilterId()); if (isCustomFilter) { fabVisible = true; poiFilterApplied = true; updateFab(); } SearchResult sr = new SearchResult(searchUICore.getPhrase()); sr.localeName = filter.getName(); sr.object = filter; sr.priority = 0; sr.objectType = ObjectType.POI_TYPE; searchUICore.selectSearchResult(sr); String txt = filter.getName() + " "; searchQuery = txt; searchEditText.setText(txt); searchEditText.setSelection(txt.length()); updateToolbarButton(); SearchSettings settings = searchUICore.getSearchSettings(); if (settings.getRadiusLevel() != 1) { searchUICore.updateSettings(settings.setRadiusLevel(1)); } runCoreSearch(txt, false, false); } private void updateFab() { fab.setVisibility(fabVisible ? View.VISIBLE : View.GONE); } public interface SearchResultListener { void searchStarted(SearchPhrase phrase); void publish(SearchResultCollection res, boolean append); // return true if search done, false if next search will be ran immediately boolean searchFinished(SearchPhrase phrase); } public class SearchFragmentPagerAdapter extends FragmentPagerAdapter { 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) { super(fm); titles = new String[titleIds.length]; for (int i = 0; i < titleIds.length; i++) { titles[i] = res.getString(titleIds[i]); } } @Override public int getCount() { return fragments.length; } @Override public Fragment getItem(int position) { return Fragment.instantiate(QuickSearchDialogFragment.this.getContext(), fragments[position]); } @Override public CharSequence getPageTitle(int position) { return titles[position]; } } public static class QuickSearchHistoryListFragment extends QuickSearchListFragment { public static final int TITLE = R.string.shared_string_history; private boolean selectionMode; @Override public SearchListFragmentType getType() { return SearchListFragmentType.HISTORY; } public boolean isSelectionMode() { return selectionMode; } public void setSelectionMode(boolean selectionMode, int position) { this.selectionMode = selectionMode; getListAdapter().setSelectionMode(selectionMode, position); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { if (selectionMode) { return false; } else { getDialogFragment().enableSelectionMode(true, position - getListView().getHeaderViewsCount()); return true; } } }); getListAdapter().setSelectionListener(new QuickSearchListAdapter.OnSelectionListener() { @Override public void onUpdateSelectionMode(List<QuickSearchListItem> selectedItems) { getDialogFragment().updateSelectionMode(selectedItems); } @Override public void reloadData() { getDialogFragment().reloadHistory(); } }); } @Override public void onListItemClick(ListView l, View view, int position, long id) { if (selectionMode) { CheckBox ch = (CheckBox) view.findViewById(R.id.toggle_item); ch.setChecked(!ch.isChecked()); getListAdapter().toggleCheckbox(position - l.getHeaderViewsCount(), ch); } else { super.onListItemClick(l, view, position, id); } } } public static class QuickSearchCategoriesListFragment extends QuickSearchListFragment { public static final int TITLE = R.string.search_categories; @Override public SearchListFragmentType getType() { return SearchListFragmentType.CATEGORIES; } } 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 public SearchListFragmentType getType() { return SearchListFragmentType.MAIN; } } public static class QuickSearchToolbarController extends TopToolbarController { public QuickSearchToolbarController() { super(TopToolbarControllerType.QUICK_SEARCH); } } }