diff --git a/OsmAnd/res/layout/arrange_poi_list_item.xml b/OsmAnd/res/layout/arrange_poi_list_item.xml new file mode 100644 index 0000000000..f1d493e094 --- /dev/null +++ b/OsmAnd/res/layout/arrange_poi_list_item.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/edit_profiles_list_fragment.xml b/OsmAnd/res/layout/edit_arrangement_list_fragment.xml similarity index 100% rename from OsmAnd/res/layout/edit_profiles_list_fragment.xml rename to OsmAnd/res/layout/edit_arrangement_list_fragment.xml diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 8635f82a6e..a72672bc93 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,12 @@ Thx - Hardy --> + Rearrange categories + You can add custom categories hide categories that you don’t find necessary and change the sort order of the list. The list can be imported and exported with profiles. + You can add a new custom category by selecting one or a few needed categories. + Reset to default will delete or custom categories and reset sort order to the default state after installation. + Available + Add custom category Permission is required to use this option. Check and share detailed logs of the application No routing rules in \'%1$s\'. Please choose another file. diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index bde5832a5c..df44ddeaa7 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -3056,6 +3056,10 @@ public class OsmandSettings { public void setSelectedPoiFilters(final Set poiFilters) { SELECTED_POI_FILTER_FOR_MAP.set(android.text.TextUtils.join(",", poiFilters)); } + + public final OsmandPreference ARRANGE_POI_FILTERS_BY_DEFAULT = new BooleanPreference("arrange_poi_filters_by_default", false).makeProfile().cache(); + + public final OsmandPreference POI_FILTERS_ARRANGEMENT = new StringPreference("poi_filters_arrangement", null).makeProfile().cache(); public static final String VOICE_PROVIDER_NOT_USE = "VOICE_PROVIDER_NOT_USE"; diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java index c20a4cc6bd..3dbee524b2 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java @@ -276,10 +276,7 @@ public class MapActivityLayers { final PoiFiltersHelper poiFilters = app.getPoiFilters(); final ContextMenuAdapter adapter = new ContextMenuAdapter(); final List list = new ArrayList<>(); - for (PoiUIFilter f : poiFilters.getTopDefinedPoiFilters()) { - addFilterToList(adapter, list, f, true); - } - for (PoiUIFilter f : poiFilters.getSearchPoiFilters()) { + for (PoiUIFilter f : poiFilters.getProfileDependentPoiUIFilters(true)) { addFilterToList(adapter, list, f, true); } list.add(poiFilters.getCustomPOIFilter()); @@ -357,10 +354,7 @@ public class MapActivityLayers { .setIcon(R.drawable.ic_action_search_dark).createItem()); final List list = new ArrayList<>(); list.add(poiFilters.getCustomPOIFilter()); - for (PoiUIFilter f : poiFilters.getTopDefinedPoiFilters()) { - addFilterToList(adapter, list, f, false); - } - for (PoiUIFilter f : poiFilters.getSearchPoiFilters()) { + for (PoiUIFilter f : poiFilters.getProfileDependentPoiUIFilters(true)) { addFilterToList(adapter, list, f, false); } diff --git a/OsmAnd/src/net/osmand/plus/poi/PoiFiltersHelper.java b/OsmAnd/src/net/osmand/plus/poi/PoiFiltersHelper.java index 5356fedfa2..fda6d03134 100644 --- a/OsmAnd/src/net/osmand/plus/poi/PoiFiltersHelper.java +++ b/OsmAnd/src/net/osmand/plus/poi/PoiFiltersHelper.java @@ -2,6 +2,11 @@ package net.osmand.plus.poi; import android.support.annotation.NonNull; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; +import com.google.gson.reflect.TypeToken; + import net.osmand.osm.AbstractPoiType; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; @@ -13,9 +18,12 @@ import net.osmand.plus.api.SQLiteAPI.SQLiteCursor; import net.osmand.plus.api.SQLiteAPI.SQLiteStatement; import net.osmand.util.Algorithms; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -25,6 +33,9 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import static net.osmand.plus.poi.PoiFiltersHelper.PoiFilterType.SEARCH; +import static net.osmand.plus.poi.PoiFiltersHelper.PoiFilterType.TOP_DEFINED; + public class PoiFiltersHelper { private final OsmandApplication application; @@ -54,6 +65,25 @@ public class PoiFiltersHelper { UDF_CAR_AID, UDF_FOR_TOURISTS, UDF_FOOD_SHOP, UDF_FUEL, UDF_SIGHTSEEING, UDF_EMERGENCY, UDF_PUBLIC_TRANSPORT, UDF_ACCOMMODATION, UDF_RESTAURANTS, UDF_PARKING }; + + public static final Comparator ORDER_COMPARATOR = new Comparator() { + @Override + public int compare(PoiUIFilter o1, PoiUIFilter o2) { + return (o1.getOrder() < o2.getOrder()) ? -1 : ((o1.getOrder() == o2.getOrder()) ? 0 : 1); + } + }; + + public static final Comparator ALPHABET_COMPARATOR = new Comparator() { + @Override + public int compare(PoiUIFilter o1, PoiUIFilter o2) { + return o1.compareTo(o2); + } + }; + + public enum PoiFilterType { + TOP_DEFINED, + SEARCH + } public PoiFiltersHelper(OsmandApplication application) { this.application = application; @@ -248,6 +278,149 @@ public class PoiFiltersHelper { return result; } + public List getProfileDependentPoiUIFilters(boolean onlyActive) { + return getProfileDependentPoiUIFilters(onlyActive, new PoiFilterType[]{TOP_DEFINED, SEARCH}); + } + + public List getProfileDependentPoiUIFilters(boolean onlyActive, PoiFilterType[] filterTypes) { + initPoiUIFiltersArrangementAndActivation(); + List result = new ArrayList<>(); + for (PoiFilterType type : filterTypes) { + List filterList = new ArrayList<>(); + switch (type) { + case TOP_DEFINED: + filterList = getTopDefinedPoiFilters(); + break; + case SEARCH: + filterList = getSearchPoiFilters(); + break; + } + if (onlyActive) { + for (PoiUIFilter f : filterList) { + if (f.isActive) { + result.add(f); + } + } + } else { + result.addAll(filterList); + } + } + Collections.sort(result, ORDER_COMPARATOR); + return result; + } + + private void initPoiUIFiltersArrangementAndActivation() { + List allFilters = new ArrayList<>(); + allFilters.addAll(getTopDefinedPoiFilters()); + allFilters.addAll(getSearchPoiFilters()); + + List poiBeans = getPoiFiltersBeans(); + Map activation = null; + Map arrangement = null; + if (poiBeans != null) { + activation = new HashMap<>(); + arrangement = new HashMap<>(); + for (PoiFilterBean filterBean : poiBeans) { + activation.put(filterBean.getFilterId(), filterBean.isActive); + arrangement.put(filterBean.getFilterId(), filterBean.getOrder()); + } + } + reorderPoiFilters(allFilters, arrangement); + refreshPoiFiltersActivation(allFilters, activation); + + //set up the biggest order to custom filter + PoiUIFilter customFilter = getCustomPOIFilter(); + customFilter.setActive(true); + customFilter.setOrder(allFilters.size()); + } + + private void reorderPoiFilters(List filters, Map arrangement) { + boolean defaultArrangement = application.getSettings().ARRANGE_POI_FILTERS_BY_DEFAULT.get(); + if (arrangement == null || defaultArrangement) { + //arrangement by default + Collections.sort(filters, ALPHABET_COMPARATOR); + for (int i = 0; i < filters.size(); i++) { + PoiUIFilter poiFilter = filters.get(i); + poiFilter.setOrder(i); + } + } else { + for (PoiUIFilter filter : filters) { + String filterId = filter.getFilterId(); + if (arrangement.containsKey(filterId)) { + filter.setOrder(arrangement.get(filterId)); + } + } + } + } + + private void refreshPoiFiltersActivation(List allFilters, Map activation) { + if (activation == null) { + //activation by default + for (PoiUIFilter filter : allFilters) { + filter.setActive(true); + } + } else { + for (PoiUIFilter filter : allFilters) { + String filterId = filter.getFilterId(); + if (activation.containsKey(filterId)) { + filter.setActive(activation.get(filterId)); + } + } + } + } + + public static class PoiFilterBean { + @Expose + String filterId; + @Expose + int order; + @Expose + boolean isActive; + + public PoiFilterBean(String filterId, int order, boolean isActive) { + this.filterId = filterId; + this.order = order; + this.isActive = isActive; + } + + public String getFilterId() { + return filterId; + } + + public int getOrder() { + return order; + } + + public boolean isActive() { + return isActive; + } + + public void setFilterId(String filterId) { + this.filterId = filterId; + } + + public void setOrder(int order) { + this.order = order; + } + + public void setActive(boolean active) { + isActive = active; + } + } + + public void savePoiFiltersBeans(List list) { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + String defaultProfiles = gson.toJson(list); + application.getSettings().POI_FILTERS_ARRANGEMENT.set(defaultProfiles); + } + + public List getPoiFiltersBeans() { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + Type t = new TypeToken>() { + }.getType(); + return gson.fromJson(application.getSettings().POI_FILTERS_ARRANGEMENT.get(), t); + } + private PoiFilterDbHelper openDbHelperNoPois() { return new PoiFilterDbHelper(null, application); } diff --git a/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java b/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java index 75bfa98d39..4c7a3a7f66 100644 --- a/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java +++ b/OsmAnd/src/net/osmand/plus/poi/PoiUIFilter.java @@ -52,6 +52,8 @@ public class PoiUIFilter implements SearchPoiTypeFilter, Comparable protected String standardIconId = ""; protected String name; protected boolean isStandardFilter; + protected int order; + protected boolean isActive = true; protected final OsmandApplication app; @@ -743,6 +745,22 @@ public class PoiUIFilter implements SearchPoiTypeFilter, Comparable this.isStandardFilter = isStandardFilter; } + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public boolean isActive() { + return isActive; + } + + public void setActive(boolean active) { + isActive = active; + } + public Context getApplication() { return app; } diff --git a/OsmAnd/src/net/osmand/plus/poi/RearrangePoiFiltersFragment.java b/OsmAnd/src/net/osmand/plus/poi/RearrangePoiFiltersFragment.java new file mode 100644 index 0000000000..1dcab35f4c --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/poi/RearrangePoiFiltersFragment.java @@ -0,0 +1,664 @@ +package net.osmand.plus.poi; + +import android.annotation.SuppressLint; +import android.content.Context; +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.MotionEventCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import net.osmand.AndroidUtils; +import net.osmand.PlatformUtil; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.render.RenderingIcons; +import net.osmand.plus.search.QuickSearchCustomPoiFragment; +import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback; + +import org.apache.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +import static net.osmand.plus.poi.PoiUIFilter.CUSTOM_FILTER_ID; +import static net.osmand.plus.poi.PoiFiltersHelper.PoiFilterBean; +import static net.osmand.plus.poi.RearrangePoiFiltersFragment.ItemType.DESCRIPTION; +import static net.osmand.plus.poi.RearrangePoiFiltersFragment.ItemType.POI; +import static net.osmand.plus.poi.RearrangePoiFiltersFragment.ItemType.SPACE; + +public class RearrangePoiFiltersFragment extends DialogFragment { + + public static final String TAG = "RearrangePoiFiltersFragment"; + + private static final Log LOG = PlatformUtil.getLog(RearrangePoiFiltersFragment.class); + + private boolean usedOnMap; + + private List items = new ArrayList<>(); + private EditPoiFiltersAdapter adapter; + private boolean isChanged = false; + private boolean orderModified = false; + private boolean isResetToDefault = false; + + private HashMap poiFiltersOrders = new HashMap<>(); + private List availableFiltersKeys = new ArrayList<>(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + boolean nightMode = isNightMode(requireMyApplication(), usedOnMap); + int themeId = nightMode ? R.style.OsmandDarkTheme : R.style.OsmandLightTheme; + setStyle(STYLE_NO_FRAME, themeId); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + final OsmandApplication app = requireMyApplication(); + + boolean nightMode = isNightMode(app, usedOnMap); + + View mainView = UiUtilities.getInflater(app, nightMode).inflate(R.layout.edit_arrangement_list_fragment, container, false); + ImageButton closeButton = mainView.findViewById(R.id.close_button); + closeButton.setImageResource(R.drawable.ic_action_remove_dark); + closeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + + TextView toolbarTitle = mainView.findViewById(R.id.toolbar_title); + toolbarTitle.setText(R.string.rearrange_categories); + + RecyclerView recyclerView = mainView.findViewById(R.id.profiles_list); + recyclerView.setLayoutManager(new LinearLayoutManager(app)); + + adapter = new EditPoiFiltersAdapter(app, nightMode); + initFiltersOrders(app, false); + + final ItemTouchHelper touchHelper = new ItemTouchHelper(new ReorderItemTouchHelperCallback(adapter)); + touchHelper.attachToRecyclerView(recyclerView); + + orderModified = !app.getSettings().ARRANGE_POI_FILTERS_BY_DEFAULT.get(); + + adapter.setListener(new PoiAdapterListener() { + + private int fromPosition; + private int toPosition; + + @Override + public void onDragStarted(RecyclerView.ViewHolder holder) { + fromPosition = holder.getAdapterPosition(); + touchHelper.startDrag(holder); + } + + @Override + public void onDragOrSwipeEnded(RecyclerView.ViewHolder holder) { + toPosition = holder.getAdapterPosition(); + if (toPosition >= 0 && fromPosition >= 0 && toPosition != fromPosition) { + adapter.notifyDataSetChanged(); + } + } + + @Override + public void onButtonClicked(int pos) { + ListItem item = items.get(pos); + if (item.value instanceof PoiUIFilterDataObject) { + isChanged = true; + isResetToDefault = false; + PoiUIFilterDataObject poiInfo = (PoiUIFilterDataObject) item.value; + poiInfo.toggleActive(); + if (!poiInfo.isActive) { + availableFiltersKeys.add(poiInfo.filterId); + } else { + availableFiltersKeys.remove(poiInfo.filterId); + } + updateItems(); + } + } + }); + recyclerView.setAdapter(adapter); + + View cancelButton = mainView.findViewById(R.id.dismiss_button); + UiUtilities.setupDialogButton(nightMode, cancelButton, UiUtilities.DialogButtonType.SECONDARY, R.string.shared_string_cancel); + cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + + mainView.findViewById(R.id.buttons_divider).setVisibility(View.VISIBLE); + + View applyButton = mainView.findViewById(R.id.right_bottom_button); + UiUtilities.setupDialogButton(nightMode, applyButton, UiUtilities.DialogButtonType.PRIMARY, R.string.shared_string_apply); + applyButton.setVisibility(View.VISIBLE); + applyButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (isChanged) { + List savingData = new ArrayList<>(); + for (PoiUIFilter filter : getProfileDependentPoiUiFilters(app)) { + String filterId = filter.getFilterId(); + Integer order = poiFiltersOrders.get(filterId); + if (order == null) { + order = filter.getOrder(); + } + boolean isActive = !availableFiltersKeys.contains(filterId); + filter.setActive(isActive); + filter.setOrder(order); + savingData.add(new PoiFilterBean(filterId, order, isActive)); + } + app.getPoiFilters().savePoiFiltersBeans(savingData); + app.getSettings().ARRANGE_POI_FILTERS_BY_DEFAULT.set(!orderModified); + } else if (isResetToDefault) { + app.getPoiFilters().savePoiFiltersBeans(null); + app.getSettings().ARRANGE_POI_FILTERS_BY_DEFAULT.set(true); + } + dismiss(); + } + }); + + return mainView; + } + + private void initFiltersOrders(OsmandApplication app, boolean arrangementByDefault) { + poiFiltersOrders.clear(); + availableFiltersKeys.clear(); + List filters = getProfileDependentPoiUiFilters(app); + if (arrangementByDefault) { + Collections.sort(filters); + for (int i = 0; i < filters.size(); i++) { + PoiUIFilter filter = filters.get(i); + poiFiltersOrders.put(filter.getFilterId(), i); + } + } else { + for (PoiUIFilter filter : filters) { + poiFiltersOrders.put(filter.getFilterId(), filter.getOrder()); + if (!filter.isActive) { + availableFiltersKeys.add(filter.getFilterId()); + } + } + } + updateItems(); + } + + private void updateItems() { + final OsmandApplication app = requireMyApplication(); + List active = getPoiFilters(true); + List available = getPoiFilters(false); + items.clear(); + items.add(new ListItem(DESCRIPTION, app.getString(R.string.create_custom_categories_list_promo))); + items.add(new ListItem(ItemType.SPACE, app.getResources().getDimension(R.dimen.content_padding))); + items.addAll(active); + items.add(new ListItem(ItemType.DIVIDER, 0)); + if (availableFiltersKeys != null && availableFiltersKeys.size() > 0) { + items.add(new ListItem(ItemType.HEADER, app.getString(R.string.shared_string_available))); + items.addAll(available); + items.add(new ListItem(ItemType.DIVIDER, 1)); + } + items.add(new ListItem(ItemType.BUTTON, new ControlButton(app.getString(R.string.add_custom_category), + R.drawable.ic_action_plus, new View.OnClickListener() { + @Override + public void onClick(View v) { + QuickSearchCustomPoiFragment.showDialog(RearrangePoiFiltersFragment.this, app.getPoiFilters().getCustomPOIFilter().getFilterId()); + } + }))); + items.add(new ListItem(ItemType.BUTTON, new ControlButton(app.getString(R.string.reset_to_default), + R.drawable.ic_action_reset_to_default_dark, new View.OnClickListener() { + @Override + public void onClick(View v) { + isChanged = false; + orderModified = false; + isResetToDefault = true; + initFiltersOrders(app, true); + } + }))); + items.add(new ListItem(DESCRIPTION, app.getString(R.string.add_new_custom_category_button_promo) + + '\n' + app.getString(R.string.reset_to_default_category_button_promo))); + + adapter.setItems(items); + } + + public static void showInstance(@NonNull DialogFragment parentFragment, boolean usedOnMap) { + try { + RearrangePoiFiltersFragment fragment = new RearrangePoiFiltersFragment(); + fragment.setUsedOnMap(usedOnMap); + fragment.show(parentFragment.getChildFragmentManager(), RearrangePoiFiltersFragment.TAG); + } catch (RuntimeException e) { + LOG.error("showInstance", e); + } + } + + public List getPoiFilters(boolean isActive) { + OsmandApplication app = requireMyApplication(); + List result = new ArrayList<>(); + for (PoiUIFilter f : getProfileDependentPoiUiFilters(app)) { + addFilterToList(result, f, isActive); + } + Collections.sort(result, new Comparator() { + @Override + public int compare(ListItem o1, ListItem o2) { + int order1 = ((PoiUIFilterDataObject) o1.value).order; + int order2 = ((PoiUIFilterDataObject) o2.value).order; + return (order1 < order2) ? -1 : ((order1 == order2) ? 0 : 1); + } + }); + return result; + } + + private void addFilterToList(List list, PoiUIFilter f, boolean isActive) { + String filterId = f.getFilterId(); + if (!isActive && availableFiltersKeys.contains(filterId) || isActive && !availableFiltersKeys.contains(filterId)) { + Integer order = poiFiltersOrders.get(filterId); + if (order == null) { + order = f.getOrder(); + } + PoiUIFilterDataObject poiInfo = new PoiUIFilterDataObject(); + poiInfo.filterId = filterId; + poiInfo.name = f.getName(); + poiInfo.order = order; + String iconRes = f.getIconId(); + if (iconRes != null && RenderingIcons.containsBigIcon(iconRes)) { + poiInfo.iconRes = RenderingIcons.getBigIconResourceId(iconRes); + } else { + poiInfo.iconRes = R.drawable.mx_user_defined; + } + poiInfo.isActive = !availableFiltersKeys.contains(filterId); + list.add(new ListItem(POI, poiInfo)); + } + } + + private static List getProfileDependentPoiUiFilters(@NonNull OsmandApplication app) { + List filters = app.getPoiFilters().getProfileDependentPoiUIFilters(false); + //remove custom filter + for (int i = filters.size() - 1; i >= 0; i--) { + PoiUIFilter filter = filters.get(i); + if (filter.getFilterId().equals(CUSTOM_FILTER_ID)) { + filters.remove(filter); + break; + } + } + return filters; + } + + public void setUsedOnMap(boolean usedOnMap) { + this.usedOnMap = usedOnMap; + } + + @NonNull + protected OsmandApplication requireMyApplication() { + FragmentActivity activity = requireActivity(); + return (OsmandApplication) activity.getApplication(); + } + + public static boolean isNightMode(OsmandApplication app, boolean usedOnMap) { + if (app != null) { + return usedOnMap ? app.getDaynightHelper().isNightModeForMapControls() : !app.getSettings().isLightContent(); + } + return false; + } + + public class PoiUIFilterDataObject { + String filterId; + String name; + int iconRes; + int order; + boolean isActive; + + public void toggleActive() { + isActive = !isActive; + } + } + + protected class ControlButton { + private String title; + private int iconRes; + private View.OnClickListener listener; + + public ControlButton(String title, int iconRes, View.OnClickListener listener) { + this.title = title; + this.iconRes = iconRes; + this.listener = listener; + } + } + + protected enum ItemType { + DESCRIPTION, + POI, + HEADER, + DIVIDER, + SPACE, + BUTTON + } + + private class ListItem { + ItemType type; + Object value; + + public ListItem(ItemType type, Object value) { + this.type = type; + this.value = value; + } + + public ListItem(ItemType type) { + this.type = type; + } + } + + private class EditPoiFiltersAdapter extends RecyclerView.Adapter + implements ReorderItemTouchHelperCallback.OnItemMoveCallback { + + private OsmandApplication app; + private UiUtilities uiUtilities; + + private List items = new ArrayList<>(); + private boolean nightMode; + private PoiAdapterListener listener; + + public EditPoiFiltersAdapter(OsmandApplication app, boolean nightMode) { + setHasStableIds(true); + this.app = app; + this.uiUtilities = app.getUIUtilities(); + this.nightMode = nightMode; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewTypeId) { + Context ctx = parent.getContext(); + LayoutInflater inflater = UiUtilities.getInflater(ctx, nightMode); + ItemType type = viewTypeId < ItemType.values().length ? ItemType.values()[viewTypeId] : SPACE; + View itemView; + switch (type) { + case POI: + itemView = inflater.inflate(R.layout.arrange_poi_list_item, parent, false); + return new PoiViewHolder(itemView); + case SPACE: + itemView = new View(ctx); + return new SpaceViewHolder(itemView); + case BUTTON: + itemView = inflater.inflate(R.layout.preference_button, parent, false); + return new ButtonViewHolder(itemView); + case HEADER: + itemView = inflater.inflate(R.layout.preference_category_with_descr, parent, false); + return new HeaderViewHolder(itemView); + case DIVIDER: + itemView = inflater.inflate(R.layout.divider, parent, false); + return new DividerViewHolder(itemView); + case DESCRIPTION: + itemView = inflater.inflate(R.layout.bottom_sheet_item_description_long, parent, false); + return new DescriptionViewHolder(itemView); + default: + throw new IllegalArgumentException("Unsupported view type"); + } + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public void onBindViewHolder(final @NonNull RecyclerView.ViewHolder holder, int position) { + ListItem item = items.get(position); + boolean nightMode = isNightMode(app, usedOnMap); + int activeColorResId = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light; + if (holder instanceof PoiViewHolder) { + PoiViewHolder h = (PoiViewHolder) holder; + PoiUIFilterDataObject poiInfo = (PoiUIFilterDataObject) item.value; + int osmandOrangeColorResId = nightMode ? R.color.osmand_orange_dark : R.color.osmand_orange; + h.title.setText(poiInfo.name); + h.icon.setImageDrawable(uiUtilities.getIcon(poiInfo.iconRes, osmandOrangeColorResId)); + h.moveIcon.setVisibility(poiInfo.isActive ? View.VISIBLE : View.GONE); + h.actionIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + int pos = holder.getAdapterPosition(); + if (listener != null && pos != RecyclerView.NO_POSITION) { + listener.onButtonClicked(pos); + } + } + }); + if (poiInfo.isActive) { + h.actionIcon.setImageDrawable(uiUtilities.getIcon(R.drawable.ic_action_remove, R.color.color_osm_edit_delete)); + h.moveIcon.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent event) { + if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { + listener.onDragStarted(holder); + } + return false; + } + }); + } else { + h.actionIcon.setImageDrawable(uiUtilities.getIcon(R.drawable.ic_action_add, R.color.color_osm_edit_create)); + } + } else if (holder instanceof SpaceViewHolder) { + float space = (float) item.value; + ((SpaceViewHolder) holder).setSpace((int) space); + } else if (holder instanceof ButtonViewHolder) { + ControlButton buttonInfo = (ControlButton) item.value; + ButtonViewHolder h = (ButtonViewHolder) holder; + h.buttonView.setOnClickListener(buttonInfo.listener); + h.icon.setImageDrawable(uiUtilities.getIcon(buttonInfo.iconRes, activeColorResId)); + h.title.setText(buttonInfo.title); + Drawable drawable = UiUtilities.getColoredSelectableDrawable(app, ContextCompat.getColor(app, activeColorResId), 0.3f); + AndroidUtils.setBackground(h.buttonView, drawable); + } else if (holder instanceof HeaderViewHolder) { + String header = (String) item.value; + ((HeaderViewHolder) holder).tvTitle.setText(header); + } else if (holder instanceof DescriptionViewHolder) { + String description = (String) item.value; + ((DescriptionViewHolder) holder).tvDescription.setText(description); + } + } + + public void setListener(PoiAdapterListener listener) { + this.listener = listener; + } + + public void setItems(List items) { + this.items = items; + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public int getItemViewType(int position) { + ListItem item = items.get(position); + return item.type.ordinal(); + } + + @Override + public boolean onItemMove(int from, int to) { + Object itemFrom = items.get(from).value; + Object itemTo = items.get(to).value; + if (itemFrom instanceof PoiUIFilterDataObject && itemTo instanceof PoiUIFilterDataObject) { + isChanged = true; + orderModified = true; + isResetToDefault = false; + PoiUIFilterDataObject poiFrom = (PoiUIFilterDataObject) itemFrom; + PoiUIFilterDataObject poiTo = (PoiUIFilterDataObject) itemTo; + + int orderFrom = poiFrom.order; + int orderTo = poiTo.order; + + poiFrom.order = orderTo; + poiTo.order = orderFrom; + + poiFiltersOrders.put(poiFrom.filterId, orderTo); + poiFiltersOrders.put(poiTo.filterId, orderFrom); + + Collections.swap(items, from, to); + notifyItemMoved(from, to); + return true; + } + return false; + } + + @Override + public long getItemId(int position) { + ListItem item = items.get(position); + if (item.value instanceof PoiUIFilterDataObject) { + return ((PoiUIFilterDataObject) item.value).filterId.hashCode(); + } else if (item.value instanceof ControlButton) { + return ((ControlButton) item.value).title.hashCode(); + } else if (item.value != null) { + return item.value.hashCode(); + } + return item.hashCode(); + } + + @Override + public void onItemDismiss(RecyclerView.ViewHolder holder) { + listener.onDragOrSwipeEnded(holder); + } + + private class DividerViewHolder extends RecyclerView.ViewHolder implements ReorderItemTouchHelperCallback.UnmovableItem { + View divider; + + public DividerViewHolder(View itemView) { + super(itemView); + divider = itemView.findViewById(R.id.divider); + } + + @Override + public boolean isMovingDisabled() { + return true; + } + } + + private class HeaderViewHolder extends RecyclerView.ViewHolder implements ReorderItemTouchHelperCallback.UnmovableItem { + private TextView tvTitle; + private TextView tvDescription; + + public HeaderViewHolder(View itemView) { + super(itemView); + tvTitle = itemView.findViewById(android.R.id.title); + tvDescription = itemView.findViewById(android.R.id.summary); + tvDescription.setVisibility(View.GONE); + } + + @Override + public boolean isMovingDisabled() { + return true; + } + } + + private class ButtonViewHolder extends RecyclerView.ViewHolder implements ReorderItemTouchHelperCallback.UnmovableItem { + + private View buttonView; + private ImageView icon; + private TextView title; + + public ButtonViewHolder(View itemView) { + super(itemView); + buttonView = itemView; + icon = itemView.findViewById(android.R.id.icon); + title = itemView.findViewById(android.R.id.title); + } + + @Override + public boolean isMovingDisabled() { + return true; + } + } + + private class SpaceViewHolder extends RecyclerView.ViewHolder implements ReorderItemTouchHelperCallback.UnmovableItem { + + View space; + + public SpaceViewHolder(View itemView) { + super(itemView); + space = itemView; + } + + public void setSpace(int hSpace) { + ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, hSpace); + space.setLayoutParams(lp); + } + + @Override + public boolean isMovingDisabled() { + return true; + } + } + + private class PoiViewHolder extends RecyclerView.ViewHolder implements ReorderItemTouchHelperCallback.UnmovableItem { + + private TextView title; + private TextView description; + private ImageView icon; + private ImageButton actionIcon; + private ImageView moveIcon; + private View itemsContainer; + + public PoiViewHolder(View itemView) { + super(itemView); + title = itemView.findViewById(R.id.title); + actionIcon = itemView.findViewById(R.id.action_icon); + icon = itemView.findViewById(R.id.icon); + moveIcon = itemView.findViewById(R.id.move_icon); + itemsContainer = itemView.findViewById(R.id.selectable_list_item); + } + + @Override + public boolean isMovingDisabled() { + int position = getAdapterPosition(); + if (position != RecyclerView.NO_POSITION) { + ListItem item = items.get(position); + if (item.value instanceof PoiUIFilterDataObject) { + PoiUIFilterDataObject pdo = (PoiUIFilterDataObject) item.value; + return !pdo.isActive; + } + } + return false; + } + } + + private class DescriptionViewHolder extends RecyclerView.ViewHolder implements ReorderItemTouchHelperCallback.UnmovableItem { + + private TextView tvDescription; + + public DescriptionViewHolder(View itemView) { + super(itemView); + tvDescription = itemView.findViewById(R.id.description); + } + + @Override + public boolean isMovingDisabled() { + return true; + } + } + } + + public interface PoiAdapterListener { + + void onDragStarted(RecyclerView.ViewHolder holder); + + void onDragOrSwipeEnded(RecyclerView.ViewHolder holder); + + void onButtonClicked(int view); + } +} diff --git a/OsmAnd/src/net/osmand/plus/profiles/EditProfilesFragment.java b/OsmAnd/src/net/osmand/plus/profiles/EditProfilesFragment.java index a49f174653..51680d99b0 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/EditProfilesFragment.java +++ b/OsmAnd/src/net/osmand/plus/profiles/EditProfilesFragment.java @@ -65,7 +65,7 @@ public class EditProfilesFragment extends BaseOsmAndFragment { } nightMode = !app.getSettings().isLightContent(); - View mainView = UiUtilities.getInflater(getContext(), nightMode).inflate(R.layout.edit_profiles_list_fragment, container, false); + View mainView = UiUtilities.getInflater(getContext(), nightMode).inflate(R.layout.edit_arrangement_list_fragment, container, false); ImageButton closeButton = mainView.findViewById(R.id.close_button); closeButton.setImageResource(R.drawable.ic_action_remove_dark); closeButton.setOnClickListener(new View.OnClickListener() { diff --git a/OsmAnd/src/net/osmand/plus/quickaction/actions/ShowHidePoiAction.java b/OsmAnd/src/net/osmand/plus/quickaction/actions/ShowHidePoiAction.java index 389c3a099c..9ec10fbf68 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/actions/ShowHidePoiAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/actions/ShowHidePoiAction.java @@ -283,10 +283,7 @@ public class ShowHidePoiAction extends QuickAction { final List list = new ArrayList<>(); - for (PoiUIFilter f : poiFilters.getTopDefinedPoiFilters()) { - addFilterToList(adapter, list, f); - } - for (PoiUIFilter f : poiFilters.getSearchPoiFilters()) { + for (PoiUIFilter f : poiFilters.getProfileDependentPoiUIFilters(true)) { addFilterToList(adapter, list, f); } diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java index 7262a9fa2f..4f95f50054 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java @@ -76,6 +76,7 @@ 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.poi.RearrangePoiFiltersFragment; import net.osmand.plus.resources.RegionAddressRepository; import net.osmand.plus.search.QuickSearchHelper.SearchHistoryAPI; import net.osmand.plus.search.listitems.QuickSearchButtonListItem; @@ -1224,6 +1225,13 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC QuickSearchDialogFragment.this, filter.getFilterId()); } })); + rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_item_move, + app.getString(R.string.rearrange_categories), new OnClickListener() { + @Override + public void onClick(View v) { + RearrangePoiFiltersFragment.showInstance(QuickSearchDialogFragment.this, false); + } + })); if (categoriesSearchFragment != null) { categoriesSearchFragment.updateListAdapter(rows, false); }