diff --git a/OsmAnd/res/layout/search_fav_list_item.xml b/OsmAnd/res/layout/search_fav_list_item.xml new file mode 100644 index 0000000000..098d444749 --- /dev/null +++ b/OsmAnd/res/layout/search_fav_list_item.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/search_favs_fragment.xml b/OsmAnd/res/layout/search_favs_fragment.xml new file mode 100644 index 0000000000..e054e25f22 --- /dev/null +++ b/OsmAnd/res/layout/search_favs_fragment.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/search_favs_list_header.xml b/OsmAnd/res/layout/search_favs_list_header.xml new file mode 100644 index 0000000000..17b421abb9 --- /dev/null +++ b/OsmAnd/res/layout/search_favs_list_header.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/search_favs_list_item.xml b/OsmAnd/res/layout/search_favs_list_item.xml new file mode 100644 index 0000000000..e9b973b393 --- /dev/null +++ b/OsmAnd/res/layout/search_favs_list_item.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index a83bd3c258..fdcf4e53b1 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -9,6 +9,8 @@ 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 --> + Sorted by distance + Search favorites For view hillshade on the map, you need to download special map of this region. To see hillshade on the map, you need to buy and install plugin Hide from zoom level diff --git a/OsmAnd/src/net/osmand/plus/activities/FavoritesSearchFragment.java b/OsmAnd/src/net/osmand/plus/activities/FavoritesSearchFragment.java new file mode 100644 index 0000000000..229b1cdb53 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/activities/FavoritesSearchFragment.java @@ -0,0 +1,550 @@ +package net.osmand.plus.activities; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.Toolbar; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Filter; +import android.widget.Filterable; +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 net.osmand.AndroidUtils; +import net.osmand.access.AccessibilityAssistant; +import net.osmand.data.FavouritePoint; +import net.osmand.data.LatLon; +import net.osmand.data.PointDescription; +import net.osmand.plus.FavouritesDbHelper; +import net.osmand.plus.FavouritesDbHelper.FavoriteGroup; +import net.osmand.plus.OsmAndFormatter; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.base.FavoriteImageDrawable; +import net.osmand.plus.myplaces.FavoritesActivity; +import net.osmand.util.Algorithms; +import net.osmand.util.MapUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class FavoritesSearchFragment extends DialogFragment { + + public static final String TAG = "FavoritesSearchFragment"; + + private OsmandApplication app; + private AccessibilityAssistant accessibilityAssistant; + + private static final String FAV_SEARCH_QUERY_KEY = "fav_search_query_key"; + + private Toolbar toolbar; + private EditText searchEditText; + private ProgressBar progressBar; + private ImageButton clearButton; + + private ListView listView; + private FavoritesSearchListAdapter listAdapter; + + private String searchQuery; + private boolean paused; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + app = getMyApplication(); + 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 Activity activity = getActivity(); + final View view = inflater.inflate(R.layout.search_favs_fragment, container, false); + + Bundle arguments = getArguments(); + if (savedInstanceState != null) { + searchQuery = savedInstanceState.getString(FAV_SEARCH_QUERY_KEY); + } + if (searchQuery == null && arguments != null) { + searchQuery = arguments.getString(FAV_SEARCH_QUERY_KEY); + } + if (searchQuery == null) { + searchQuery = ""; + } + + 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 View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + } + ); + + searchEditText = (EditText) view.findViewById(R.id.searchEditText); + searchEditText.setHint(R.string.search_favorites); + 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(); + if (!searchQuery.equalsIgnoreCase(newQueryText)) { + searchQuery = newQueryText; + listAdapter.getFilter().filter(newQueryText); + } + } + } + ); + + 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 View.OnClickListener() { + @Override + public void onClick(View v) { + if (searchEditText.getText().length() > 0) { + searchEditText.setText(""); + searchEditText.setSelection(0); + } + } + } + ); + + listView = (ListView) view.findViewById(android.R.id.list); + + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + if (listView != null) { + listView.setOnScrollListener(new AbsListView.OnScrollListener() { + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + } + + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { + hideKeyboard(); + } + } + }); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + FavouritePoint point = listAdapter.getItem(position); + if (point != null) { + showOnMap(point); + dismiss(); + } + } + }); + listAdapter = new FavoritesSearchListAdapter(getMyApplication(), getActivity()); + listAdapter.setAccessibilityAssistant(accessibilityAssistant); + listAdapter.synchronizePoints(); + listView.setAdapter(listAdapter); + } + openKeyboard(); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new Dialog(getActivity(), getTheme()){ + @Override + public void onBackPressed() { + cancel(); + } + }; + } + + @Override + public void onResume() { + super.onResume(); + paused = false; + } + + @Override + public void onPause() { + super.onPause(); + paused = true; + hideProgressBar(); + } + + @Override + public void onDismiss(DialogInterface dialog) { + Activity activity = getActivity(); + if (activity != null) { + getChildFragmentManager().popBackStack(); + } + super.onDismiss(dialog); + } + + public void showOnMap(final FavouritePoint point) { + getMyApplication().getSettings().FAVORITES_TAB.set(FavoritesActivity.FAV_TAB); + + final OsmandSettings settings = getMyApplication().getSettings(); + LatLon location = new LatLon(point.getLatitude(), point.getLongitude()); + settings.setMapLocationToShow(location.getLatitude(), location.getLongitude(), + settings.getLastKnownMapZoom(), + new PointDescription(PointDescription.POINT_TYPE_FAVORITE, point.getName()), + true, + point); //$NON-NLS-1$ + MapActivity.launchMapActivityMoveToTop(getActivity()); + } + + private OsmandApplication getMyApplication() { + return (OsmandApplication) getActivity().getApplication(); + } + + private void openKeyboard() { + searchEditText.requestFocus(); + AndroidUtils.softKeyboardDelayed(searchEditText); + } + + public void hideKeyboard() { + if (searchEditText.hasFocus()) { + AndroidUtils.hideSoftKeyboard(getActivity(), searchEditText); + } + } + + private void showProgressBar() { + updateClearButtonVisibility(false); + progressBar.setVisibility(View.VISIBLE); + } + + private void hideProgressBar() { + updateClearButtonVisibility(true); + progressBar.setVisibility(View.GONE); + } + + private void updateClearButtonVisibility(boolean show) { + if (show) { + clearButton.setVisibility(View.VISIBLE); + } else { + clearButton.setVisibility(View.GONE); + } + } + + public static boolean showInstance(@NonNull FragmentActivity activity, @NonNull String searchQuery) { + try { + + Bundle bundle = new Bundle(); + if (!Algorithms.isEmpty(searchQuery)) { + bundle.putString(FAV_SEARCH_QUERY_KEY, searchQuery); + } + + FavoritesSearchFragment fragment = new FavoritesSearchFragment(); + fragment.setArguments(bundle); + fragment.show(activity.getSupportFragmentManager(), TAG); + return true; + + } catch (RuntimeException e) { + return false; + } + } + + class FavoritesSearchListAdapter extends ArrayAdapter implements Filterable { + + private static final int FAVORITE_TYPE = 0; + private static final int HEADER_SHADOW_TYPE = 1; + private static final int HEADER_TYPE = 2; + private static final int FOOTER_SHADOW_TYPE = 3; + + private OsmandApplication app; + private Activity activity; + private AccessibilityAssistant accessibilityAssistant; + private FavouritesDbHelper helper; + + private LatLon location; + private Drawable arrowImage; + + List points = new ArrayList<>(); + Filter myFilter; + private Set filter; + + public FavoritesSearchListAdapter(OsmandApplication app, Activity activity) { + super(app, R.layout.search_list_item); + this.app = app; + this.activity = activity; + this.helper = app.getFavorites(); + location = app.getSettings().getLastKnownMapLocation(); + arrowImage = ContextCompat.getDrawable(activity, R.drawable.ic_direction_arrow); + arrowImage.mutate(); + arrowImage.setColorFilter(ContextCompat.getColor(activity, R.color.color_distance), PorterDuff.Mode.MULTIPLY); + } + + public void setAccessibilityAssistant(AccessibilityAssistant accessibilityAssistant) { + this.accessibilityAssistant = accessibilityAssistant; + } + + public void synchronizePoints() { + points.clear(); + List gs = helper.getFavoriteGroups(); + Set flt = filter; + for (FavoriteGroup key : gs) { + if (flt == null || flt.contains(key)) { + for (FavouritePoint p : key.points) { + if (p.isVisible()) { + points.add(p); + } + } + } else { + ArrayList list = new ArrayList<>(); + for (FavouritePoint p : key.points) { + if (p.isVisible() && flt.contains(p)) { + list.add(p); + } + } + points.addAll(list); + } + } + Collections.sort(points, new Comparator() { + @Override + public int compare(FavouritePoint p1, FavouritePoint p2) { + int d1 = (int) (MapUtils.getDistance(p1.getLatitude(), p1.getLongitude(), + location.getLatitude(), location.getLongitude())); + int d2 = (int) (MapUtils.getDistance(p2.getLatitude(), p2.getLongitude(), + location.getLatitude(), location.getLongitude())); + return d1 < d2 ? -1 : (d1 == d2 ? 0 : 1); + } + }); + notifyDataSetChanged(); + } + + public LatLon getLocation() { + return location; + } + + public void setLocation(LatLon location) { + this.location = location; + } + + @Nullable + @Override + public FavouritePoint getItem(int position) { + if (position > 1 && position < points.size() + 2) { + return points.get(position - 2); + } else { + return null; + } + } + + @Override + public int getCount() { + if (points.size() > 0) { + return points.size() + 3; + } else { + return 0; + } + } + + @Override + public boolean isEnabled(int position) { + return getItemViewType(position) == FAVORITE_TYPE; + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { + return HEADER_SHADOW_TYPE; + } else if (position == 1) { + return HEADER_TYPE; + } else if (position == getCount() - 1) { + return FOOTER_SHADOW_TYPE; + } else { + return FAVORITE_TYPE; + } + } + + @Override + public int getViewTypeCount() { + return 4; + } + + @NonNull + @Override + public View getView(final int position, View convertView, @NonNull ViewGroup parent) { + final FavouritePoint point = getItem(position); + int type = getItemViewType(position); + LinearLayout view; + if (type == HEADER_TYPE) { + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) app + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = (LinearLayout) inflater.inflate( + R.layout.search_favs_list_header, null); + } else { + view = (LinearLayout) convertView; + } + + ((TextView) view.findViewById(R.id.title)).setText(app.getString(R.string.sorted_by_distance)); + } else if (type == HEADER_SHADOW_TYPE) { + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) app + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = (LinearLayout) inflater.inflate( + R.layout.list_shadow_header, null); + } else { + view = (LinearLayout) convertView; + } + } else if (type == FOOTER_SHADOW_TYPE) { + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) app + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = (LinearLayout) inflater.inflate( + R.layout.list_shadow_footer, null); + } else { + view = (LinearLayout) convertView; + } + } else { + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) app + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = (LinearLayout) inflater.inflate( + R.layout.search_favs_list_item, null); + } else { + view = (LinearLayout) convertView; + } + + if (point != null) { + ImageView imageView = (ImageView) view.findViewById(R.id.imageView); + TextView title = (TextView) view.findViewById(R.id.title); + TextView subtitle = (TextView) view.findViewById(R.id.subtitle); + + imageView.setImageDrawable(FavoriteImageDrawable.getOrCreate(activity, point.getColor(), false)); + title.setText(point.getName()); + + int dist = (int) (MapUtils.getDistance(point.getLatitude(), point.getLongitude(), + location.getLatitude(), location.getLongitude())); + String distance = OsmAndFormatter.getFormattedDistance(dist, app) + " "; + ImageView direction = (ImageView) view.findViewById(R.id.direction); + direction.setImageDrawable(arrowImage); + TextView distanceText = (TextView) view.findViewById(R.id.distance); + distanceText.setText(distance); + distanceText.setTextColor(app.getResources().getColor(R.color.color_distance)); + + subtitle.setText(point.getCategory().length() == 0 ? app.getString(R.string.shared_string_favorites) : point.getCategory()); + } + } + View divider = view.findViewById(R.id.divider); + if (divider != null) { + if (position > getCount() - 3) { + divider.setVisibility(View.GONE); + } else { + divider.setVisibility(View.VISIBLE); + } + } + ViewCompat.setAccessibilityDelegate(view, accessibilityAssistant); + return view; + } + + @NonNull + @Override + public Filter getFilter() { + if (myFilter == null) { + myFilter = new FavoritesFilter(); + } + return myFilter; + } + + void setFilterResults(Set values) { + this.filter = values; + } + } + + class FavoritesFilter extends Filter { + + FavouritesDbHelper helper; + + FavoritesFilter() { + helper = app.getFavorites(); + } + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults results = new FilterResults(); + String favorites = app.getString(R.string.shared_string_favorites).toLowerCase(); + if (constraint == null || constraint.length() == 0) { + results.values = null; + results.count = 1; + } else { + Set filter = new HashSet<>(); + String cs = constraint.toString().toLowerCase(); + for (FavoriteGroup g : helper.getFavoriteGroups()) { + String gName; + if (Algorithms.isEmpty(g.name)) { + gName = favorites; + } else { + gName = g.name.toLowerCase(); + } + if (g.visible && gName.contains(cs)) { + filter.add(g); + } else { + for (FavouritePoint fp : g.points) { + if (fp.isVisible() && fp.getName().toLowerCase().contains(cs)) { + filter.add(fp); + } + } + } + } + results.values = filter; + results.count = filter.size(); + } + return results; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + synchronized (listAdapter) { + listAdapter.setFilterResults((Set) results.values); + listAdapter.synchronizePoints(); + } + listAdapter.notifyDataSetChanged(); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java b/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java index 361205acd4..19dcd512a1 100644 --- a/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java @@ -9,13 +9,11 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Handler; import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.AlertDialog; import android.support.v7.view.ActionMode; -import android.support.v7.widget.SearchView; import android.text.Html; import android.view.LayoutInflater; import android.view.Menu; @@ -82,7 +80,6 @@ public class FavoritesTreeFragment extends OsmandExpandableListFragment { private Set favoritesSelected = new LinkedHashSet<>(); private Set groupsToDelete = new LinkedHashSet<>(); private ActionMode actionMode; - private SearchView searchView; Drawable arrowImage; Drawable arrowImageDisabled; private HashMap> preferenceCache = new HashMap<>(); @@ -142,7 +139,20 @@ public class FavoritesTreeFragment extends OsmandExpandableListFragment { ExpandableListView listView = (ExpandableListView) view.findViewById(android.R.id.list); favouritesAdapter.synchronizeGroups(); if (!favouritesAdapter.isEmpty()) { - listView.addHeaderView(inflater.inflate(R.layout.list_shadow_header, null, false)); + boolean light = getMyApplication().getSettings().isLightContent(); + View searchView = inflater.inflate(R.layout.search_fav_list_item, null); + searchView.setBackgroundResource(light ? R.color.bg_color_light : R.color.bg_color_dark); + TextView title = (TextView) searchView.findViewById(R.id.title); + title.setCompoundDrawablesWithIntrinsicBounds(getMyApplication().getIconsCache().getThemedIcon(R.drawable.ic_action_search_dark), null, null, null); + title.setHint(R.string.shared_string_search); + searchView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FavoritesSearchFragment.showInstance(getActivity(), ""); + } + }); + listView.addHeaderView(searchView); + listView.addHeaderView(inflater.inflate(R.layout.list_item_divider, null, false)); footerView = inflater.inflate(R.layout.list_shadow_footer, null, false); listView.addFooterView(footerView); } @@ -241,39 +251,10 @@ public class FavoritesTreeFragment extends OsmandExpandableListFragment { menu.clear(); MenuItem mi = createMenuItem(menu, SEARCH_ID, R.string.search_poi_filter, R.drawable.ic_action_search_dark, R.drawable.ic_action_search_dark, MenuItemCompat.SHOW_AS_ACTION_ALWAYS | MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); - searchView = new SearchView(getActivity()); - FavoritesActivity.updateSearchView(getActivity(), searchView); - MenuItemCompat.setActionView(mi, searchView); - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + mi.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override - public boolean onQueryTextSubmit(String query) { - favouritesAdapter.getFilter().filter(query); - return true; - } - - @Override - public boolean onQueryTextChange(String newText) { - favouritesAdapter.getFilter().filter(newText); - return true; - } - }); - MenuItemCompat.setOnActionExpandListener(mi, new MenuItemCompat.OnActionExpandListener() { - @Override - public boolean onMenuItemActionExpand(MenuItem item) { - return true; - } - - @Override - public boolean onMenuItemActionCollapse(MenuItem item) { - favouritesAdapter.setFilterResults(null); - favouritesAdapter.synchronizeGroups(); - favouritesAdapter.notifyDataSetChanged(); - // Needed to hide intermediate progress bar after closing action mode - new Handler().postDelayed(new Runnable() { - public void run() { - hideProgressBar(); - } - }, 100); + public boolean onMenuItemClick(MenuItem item) { + FavoritesSearchFragment.showInstance(getActivity(), ""); return true; } });