diff --git a/OsmAnd/res/layout/edit_poi_add_item.xml b/OsmAnd/res/layout/edit_poi_add_item.xml new file mode 100644 index 00000000000..a0b537a4bed --- /dev/null +++ b/OsmAnd/res/layout/edit_poi_add_item.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/edit_poi_add_opening_hours_item.xml b/OsmAnd/res/layout/edit_poi_add_opening_hours_item.xml new file mode 100644 index 00000000000..30a5103451a --- /dev/null +++ b/OsmAnd/res/layout/edit_poi_add_opening_hours_item.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/edit_poi_basic_info_item.xml b/OsmAnd/res/layout/edit_poi_basic_info_item.xml new file mode 100644 index 00000000000..7f0097c30dd --- /dev/null +++ b/OsmAnd/res/layout/edit_poi_basic_info_item.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/edit_poi_description_item.xml b/OsmAnd/res/layout/edit_poi_description_item.xml new file mode 100644 index 00000000000..4c4b4bd10df --- /dev/null +++ b/OsmAnd/res/layout/edit_poi_description_item.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd/res/layout/fragment_edit_poi.xml b/OsmAnd/res/layout/fragment_edit_poi.xml index 5e9f3a7bf5e..fd71191e90d 100644 --- a/OsmAnd/res/layout/fragment_edit_poi.xml +++ b/OsmAnd/res/layout/fragment_edit_poi.xml @@ -21,7 +21,7 @@ app:contentInsetRight="0dp" app:contentInsetStart="@dimen/divider_color_light_margin_start" /> - - + android:layout_height="match_parent"/> - + + + + + diff --git a/OsmAnd/res/layout/list_item_poi_tag.xml b/OsmAnd/res/layout/list_item_poi_tag.xml index b5e5fc0118b..d509f4c5162 100644 --- a/OsmAnd/res/layout/list_item_poi_tag.xml +++ b/OsmAnd/res/layout/list_item_poi_tag.xml @@ -2,7 +2,7 @@ { + scrollView.scrollTo(0, 0); + scrollView.requestLayout(); + }); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { updateNightMode(); view = themedInflater.inflate(R.layout.fragment_edit_poi, container, false); + scrollView = view.findViewById(R.id.scroll_view); + scrollView.setNestedScrollingEnabled(false); if (savedInstanceState != null) { Map map = (Map) AndroidUtils.getSerializable(savedInstanceState, TAGS_LIST, LinkedHashMap.class); editPoiData.updateTags(map); @@ -151,60 +160,51 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa viewPager = view.findViewById(R.id.viewpager); String basicTitle = getResources().getString(R.string.tab_title_basic); String extendedTitle = getResources().getString(R.string.tab_title_advanced); - PoiInfoPagerAdapter pagerAdapter = new PoiInfoPagerAdapter(getChildFragmentManager(), basicTitle, extendedTitle); + TabLayout tabLayout = view.findViewById(R.id.tab_layout); + PoiInfoPagerAdapter pagerAdapter = new PoiInfoPagerAdapter(this, basicTitle, extendedTitle); viewPager.setAdapter(pagerAdapter); - viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override - public void onPageScrolled(int i, float v, int i1) { - + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels); } @Override - public void onPageSelected(int i) { - Fragment pageFragment = pagerAdapter.getItem(i); + public void onPageSelected(int position) { + Fragment pageFragment = pagerAdapter.createFragment(position); ((OnFragmentActivatedListener) pageFragment).onFragmentActivated(); if (pageFragment instanceof OnSaveButtonClickListener) { onSaveButtonClickListener = (OnSaveButtonClickListener) pageFragment; } else { onSaveButtonClickListener = null; } + resetNestedScrollView(); } @Override - public void onPageScrollStateChanged(int i) { - + public void onPageScrollStateChanged(int state) { + super.onPageScrollStateChanged(state); } }); - TabLayout tabLayout = view.findViewById(R.id.tab_layout); + tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); // tabLayout.setupWithViewPager(viewPager); // Hack due to bug in design support library v22.2.1 // https://code.google.com/p/android/issues/detail?id=180462 // TODO remove in new version - if (Build.VERSION.SDK_INT >= 11) { - if (ViewCompat.isLaidOut(tabLayout)) { - tabLayout.setupWithViewPager(viewPager); - } else { - tabLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - tabLayout.setupWithViewPager(viewPager); - tabLayout.removeOnLayoutChangeListener(this); - } - }); - } + if (ViewCompat.isLaidOut(tabLayout)) { + TabLayoutMediator mediator = new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText(pagerAdapter.getPageTitle(position))); + mediator.attach(); } else { - ViewTreeObserver vto = view.getViewTreeObserver(); - vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - + tabLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override - public void onGlobalLayout() { - if (getActivity() != null) { - tabLayout.setupWithViewPager(viewPager); - } + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + TabLayoutMediator mediator = new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText(pagerAdapter.getPageTitle(position))); + mediator.attach(); + tabLayout.removeOnLayoutChangeListener(this); } }); } @@ -523,11 +523,12 @@ public void setSubCategory(String subCategory) { } public void smoothScrollToBottom() { - ScrollView scrollView = view.findViewById(R.id.scroll_view); - int height = scrollView.getHeight(); + NestedScrollView scrollView = view.findViewById(R.id.scroll_view); + scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN)); +/* int height = scrollView.getHeight(); int bottom = scrollView.getChildAt(0).getBottom(); int maxScrollY = Math.max(0, bottom - height); - scrollView.smoothScrollTo(0, maxScrollY); + scrollView.smoothScrollTo(0, maxScrollY);*/ } public static void commitEntity(Action action, @@ -714,29 +715,29 @@ private Entity getExistingOsmEditEntity(@NonNull OsmEditingPlugin osmEditingPlug }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - public static class PoiInfoPagerAdapter extends FragmentPagerAdapter { + public static class PoiInfoPagerAdapter extends FragmentStateAdapter { - private final Fragment[] fragments = {new BasicEditPoiFragment(), new AdvancedEditPoiFragment()}; + private final Fragment[] fragments = {new NewBasicEditPoiFragment(), new NewAdvancedEditPoiFragment()}; private final String[] titles; - PoiInfoPagerAdapter(FragmentManager fm, String basicTitle, String extendedTitle) { - super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + PoiInfoPagerAdapter(Fragment fm, String basicTitle, String extendedTitle) { + super(fm); titles = new String[] {basicTitle, extendedTitle}; } - @Override - public int getCount() { - return fragments.length; + public CharSequence getPageTitle(int position) { + return titles[position]; } + @NonNull @Override - public Fragment getItem(int position) { + public Fragment createFragment(int position) { return fragments[position]; } @Override - public CharSequence getPageTitle(int position) { - return titles[position]; + public int getItemCount() { + return fragments.length; } } diff --git a/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/NewBasicEditPoiFragment.java b/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/NewBasicEditPoiFragment.java new file mode 100644 index 00000000000..0e850a67cd8 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/NewBasicEditPoiFragment.java @@ -0,0 +1,268 @@ +package net.osmand.plus.plugins.osmedit.dialogs; + +import static net.osmand.plus.plugins.osmedit.dialogs.EditPoiDialogFragment.AMENITY_TEXT_LENGTH; +import static net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter.TYPE_ADD_OPENING_HOURS; +import static net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter.TYPE_BASIC_INFO; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.text.InputFilter; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import net.osmand.osm.edit.OSMSettings; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.base.BaseOsmAndFragment; +import net.osmand.plus.helpers.LocaleHelper; +import net.osmand.plus.plugins.osmedit.data.EditPoiData; +import net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter; +import net.osmand.plus.utils.AndroidUtils; +import net.osmand.plus.utils.ColorUtilities; +import net.osmand.util.OpeningHoursParser; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class NewBasicEditPoiFragment extends BaseOsmAndFragment implements EditPoiDialogFragment.OnFragmentActivatedListener { + + private static final String OPENING_HOURS = "opening_hours"; + private OpeningHoursAdapter openingHoursAdapter; + + private boolean basicTagsInitialized; + private EditPoiContentAdapter contentAdapter; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + updateNightMode(); + View view = themedInflater.inflate(R.layout.fragment_edit_poi_advanced_new, container, false); + + RecyclerView recyclerView = view.findViewById(R.id.content_recycler_view); + recyclerView.setNestedScrollingEnabled(true); + InputFilter[] lengthLimit = { + new InputFilter.LengthFilter(AMENITY_TEXT_LENGTH) + }; + + int iconColor = ColorUtilities.getSecondaryTextColor(app, nightMode); + Drawable clockDrawable = getPaintedContentIcon(R.drawable.ic_action_time, iconColor); + Drawable deleteDrawable = getPaintedContentIcon(R.drawable.ic_action_remove_dark, iconColor); + if (savedInstanceState != null && savedInstanceState.containsKey(OPENING_HOURS)) { + OpeningHoursParser.OpeningHours openingHours = AndroidUtils.getSerializable(savedInstanceState, OPENING_HOURS, OpeningHoursParser.OpeningHours.class); + openingHoursAdapter = new OpeningHoursAdapter(app, openingHours, + getData(), clockDrawable, deleteDrawable); + openingHoursAdapter.updateHoursData(); + } else { + openingHoursAdapter = new OpeningHoursAdapter(app, new OpeningHoursParser.OpeningHours(), + getData(), clockDrawable, deleteDrawable); + } + + EditPoiContentAdapter.EditPoiListener editPoiListener = new EditPoiContentAdapter.EditPoiListener() { + @Override + public void onAddNewItem(int position, int buttonType) { + if (buttonType == TYPE_ADD_OPENING_HOURS) { + OpeningHoursParser.BasicOpeningHourRule rule = new OpeningHoursParser.BasicOpeningHourRule(); + rule.setStartTime(9 * 60); + rule.setEndTime(18 * 60); + if (openingHoursAdapter.openingHours.getRules().isEmpty()) { + rule.setDays(new boolean[]{true, true, true, true, true, false, false}); + } + OpeningHoursDaysDialogFragment fragment = OpeningHoursDaysDialogFragment.createInstance(rule, -1); + fragment.show(getChildFragmentManager(), "OpenTimeDialogFragment"); + } + } + + @Override + public void onDeleteItem(int position) { + + } + + @Override + public InputFilter[] getLengthLimit() { + return lengthLimit; + } + + @Override + public FragmentManager getChildFragmentManager() { + return NewBasicEditPoiFragment.this.getChildFragmentManager(); + } + + @Override + public boolean isFragmentResumed() { + return NewBasicEditPoiFragment.this.isResumed(); + } + + @Override + public boolean isBasicTagsInitialized() { + return NewBasicEditPoiFragment.this.basicTagsInitialized; + } + }; + + contentAdapter = new EditPoiContentAdapter((MapActivity) requireActivity(), getContentList(), + null, null, openingHoursAdapter, nightMode, getEditPoiFragment(), editPoiListener); + recyclerView.setLayoutManager(new LinearLayoutManager(app)); + recyclerView.setAdapter(contentAdapter); + onFragmentActivated(); + + return view; + } + + private EditPoiDialogFragment getEditPoiFragment() { + return (EditPoiDialogFragment) getParentFragment(); + } + + private void updateViews() { + if (contentAdapter != null) { + contentAdapter.setItems(getContentList()); + } + } + + public record OpenHoursItem(int position, long id) { + } + + private List getContentList() { + List list = new ArrayList<>(); + EditPoiData data = getData(); + if (data == null) { + return list; + } + + list.add(TYPE_BASIC_INFO); + for (int i = 0; i < openingHoursAdapter.openingHours.getRules().size(); i++) { + list.add(new OpenHoursItem(i, System.currentTimeMillis())); + } + list.add(TYPE_ADD_OPENING_HOURS); + + return list; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putSerializable(OPENING_HOURS, openingHoursAdapter.openingHours); + super.onSaveInstanceState(outState); + } + + public void setBasicOpeningHoursRule(OpeningHoursParser.BasicOpeningHourRule item, int position) { + if (item.getStartTime() == 0 && item.getEndTime() == 0 && item.isOpenedEveryDay()) { + item.setEndTime(24 * 60); + } + openingHoursAdapter.setOpeningHoursRule(item, position); + } + + public void removeUnsavedOpeningHours() { + EditPoiData data = getData(); + if (data != null) { + OpeningHoursParser.OpeningHours openingHours = OpeningHoursParser.parseOpenedHoursHandleErrors(data.getTagValues() + .get(OSMSettings.OSMTagKey.OPENING_HOURS.getValue())); + if (openingHours == null) { + openingHours = new OpeningHoursParser.OpeningHours(); + } + openingHoursAdapter.replaceOpeningHours(openingHours); + updateViews(); + openingHoursAdapter.updateHoursData(); + } + contentAdapter.notifyDataSetChanged(); + } + + private EditPoiData getData() { + return getEditPoiFragment().getEditPoiData(); + } + + @Override + public void onFragmentActivated() { + EditPoiData data = getData(); + if (data == null) { + return; + } + basicTagsInitialized = false; + + Map tagValues = data.getTagValues(); + OpeningHoursParser.OpeningHours openingHours = OpeningHoursParser.parseOpenedHoursHandleErrors(tagValues.get(OSMSettings.OSMTagKey.OPENING_HOURS.getValue())); + if (openingHours == null) { + openingHours = new OpeningHoursParser.OpeningHours(); + } + openingHoursAdapter.replaceOpeningHours(openingHours); + updateViews(); + openingHoursAdapter.updateHoursData(); + + basicTagsInitialized = true; + } + + public class OpeningHoursAdapter { + + private final OsmandApplication app; + private OpeningHoursParser.OpeningHours openingHours; + + private final EditPoiData data; + private final Drawable clockDrawable; + private final Drawable deleteDrawable; + + public OpeningHoursAdapter(@NonNull OsmandApplication app, + @NonNull OpeningHoursParser.OpeningHours openingHours, + @NonNull EditPoiData data, + @NonNull Drawable clockDrawable, + @NonNull Drawable deleteDrawable) { + this.app = app; + this.openingHours = openingHours; + this.data = data; + this.clockDrawable = clockDrawable; + this.deleteDrawable = deleteDrawable; + } + + public Drawable getClockDrawable() { + return clockDrawable; + } + + public Drawable getDeleteDrawable() { + return deleteDrawable; + } + + public OpeningHoursParser.OpeningHours getOpeningHours() { + return openingHours; + } + + public void setOpeningHoursRule(OpeningHoursParser.BasicOpeningHourRule rule, int position) { + if (position == -1) { + openingHours.addRule(rule); + } else { + openingHours.getRules().set(position, rule); + } + updateViews(); + updateHoursData(); + contentAdapter.notifyDataSetChanged(); + } + + public void replaceOpeningHours(OpeningHoursParser.OpeningHours openingHours) { + this.openingHours = openingHours; + } + + public void updateHoursData() { + if (!data.isInEdit()) { + LocaleHelper helper = app.getLocaleHelper(); + helper.updateTimeFormatting(false, Locale.getDefault()); + String openingHoursString = openingHours.toString(); + helper.updateTimeFormatting(); + + if (!TextUtils.isEmpty(openingHoursString)) { + if (openingHours.getOriginal() == null || + !OpeningHoursParser.parseOpenedHoursHandleErrors(openingHours.getOriginal()).toString().equals(openingHoursString)) { + data.putTag(OSMSettings.OSMTagKey.OPENING_HOURS.getValue(), openingHoursString); + } + } else if (basicTagsInitialized && isResumed()) { + data.removeTag(OSMSettings.OSMTagKey.OPENING_HOURS.getValue()); + } + } + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursDaysDialogFragment.java b/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursDaysDialogFragment.java index 42515a99a21..b4c95701e90 100644 --- a/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursDaysDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursDaysDialogFragment.java @@ -74,7 +74,7 @@ public void onClick(DialogInterface dialog, int which) { OpeningHoursHoursDialogFragment.createInstance(item, positionToAdd, true, 0) .show(getFragmentManager(), "TimePickerDialogFragment"); } else { - ((BasicEditPoiFragment) getParentFragment()) + ((NewBasicEditPoiFragment) getParentFragment()) .setBasicOpeningHoursRule(item, positionToAdd); } } else { diff --git a/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursHoursDialogFragment.java b/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursHoursDialogFragment.java index ab4e6a6a719..8a0b8e5889a 100644 --- a/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursHoursDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/plugins/osmedit/dialogs/OpeningHoursHoursDialogFragment.java @@ -79,13 +79,13 @@ public void onClick(DialogInterface dialog, int which) { } else { item.setEndTime(time, timePosition); } - ((BasicEditPoiFragment) getParentFragment()) + ((NewBasicEditPoiFragment) getParentFragment()) .setBasicOpeningHoursRule(item, rulePosition); } } }) .setNegativeButton(R.string.shared_string_cancel, (dialog, which) -> { - BasicEditPoiFragment editPoiFragment = ((BasicEditPoiFragment) getParentFragment()); + NewBasicEditPoiFragment editPoiFragment = ((NewBasicEditPoiFragment) getParentFragment()); if (editPoiFragment != null) { editPoiFragment.removeUnsavedOpeningHours(); } diff --git a/OsmAnd/src/net/osmand/plus/plugins/osmedit/fragments/EditPoiContentAdapter.java b/OsmAnd/src/net/osmand/plus/plugins/osmedit/fragments/EditPoiContentAdapter.java new file mode 100644 index 00000000000..ce998566fa1 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/plugins/osmedit/fragments/EditPoiContentAdapter.java @@ -0,0 +1,599 @@ +package net.osmand.plus.plugins.osmedit.fragments; + +import static net.osmand.plus.plugins.osmedit.dialogs.EditPoiDialogFragment.AMENITY_TEXT_LENGTH; +import static net.osmand.plus.plugins.osmedit.fragments.NewAdvancedEditPoiFragment.*; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.graphics.drawable.Drawable; +import android.text.Editable; +import android.text.InputFilter; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.RecyclerView; + +import net.osmand.osm.PoiCategory; +import net.osmand.osm.PoiType; +import net.osmand.osm.edit.OSMSettings; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.plugins.osmedit.data.EditPoiData; +import net.osmand.plus.plugins.osmedit.dialogs.EditPoiDialogFragment; +import net.osmand.plus.plugins.osmedit.dialogs.NewBasicEditPoiFragment.OpenHoursItem; +import net.osmand.plus.plugins.osmedit.dialogs.NewBasicEditPoiFragment.OpeningHoursAdapter; +import net.osmand.plus.plugins.osmedit.dialogs.OpeningHoursDaysDialogFragment; +import net.osmand.plus.plugins.osmedit.dialogs.OpeningHoursHoursDialogFragment; +import net.osmand.plus.plugins.osmedit.fragments.NewAdvancedEditPoiFragment.OsmTagsArrayAdapter; +import net.osmand.plus.utils.AndroidUtils; +import net.osmand.plus.utils.UiUtilities; +import net.osmand.plus.widgets.OsmandTextFieldBoxes; +import net.osmand.plus.widgets.tools.SimpleTextWatcher; +import net.osmand.util.Algorithms; +import net.osmand.util.OpeningHoursParser; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import gnu.trove.list.array.TIntArrayList; +import studio.carbonylgroup.textfieldboxes.ExtendedEditText; + +public class EditPoiContentAdapter extends RecyclerView.Adapter { + public static final int TYPE_DESCRIPTION_ITEM = 1; + public static final int TYPE_TAG_ITEM = 2; + public static final int TYPE_ADD_TAG = 3; + public static final int TYPE_ADD_OPENING_HOURS = 4; + public static final int TYPE_BASIC_INFO = 5; + public static final int TYPE_OPEN_TIME_LIST_ITEM = 6; + + public static final int PAYLOAD_NAME = 0; + public static final int PAYLOAD_AMENITY = 1; + public static final int PAYLOAD_FOCUS_ON_ITEM = 2; + + private final LayoutInflater themedInflater; + private final OsmandApplication app; + private final boolean nightMode; + + private final EditPoiDialogFragment editPoiDialogFragment; + + private List items = new ArrayList<>(); + + public final OsmTagsArrayAdapter tagAdapter; + private final ArrayAdapter valueAdapter; + private final OpeningHoursAdapter openingHoursAdapter; + private final Activity activity; + + private EditText currentTagEditText; + private final EditPoiListener editPoiListener; + + + public EditPoiContentAdapter(@NonNull MapActivity mapActivity, @NonNull List items, + ArrayAdapter valueAdapter, OsmTagsArrayAdapter tagAdapter, + OpeningHoursAdapter openingHoursAdapter, boolean nightMode, EditPoiDialogFragment editPoiDialogFragment, + EditPoiListener editPoiListener) { + setHasStableIds(true); + this.items.addAll(items); + this.nightMode = nightMode; + this.app = mapActivity.getMyApplication(); + this.activity = mapActivity; + this.valueAdapter = valueAdapter; + this.tagAdapter = tagAdapter; + this.openingHoursAdapter = openingHoursAdapter; + this.editPoiDialogFragment = editPoiDialogFragment; + this.editPoiListener = editPoiListener; + themedInflater = UiUtilities.getInflater(mapActivity, nightMode); + } + + private EditPoiData getData() { + return editPoiDialogFragment.getEditPoiData(); + } + + @SuppressLint("NotifyDataSetChanged") + public void setItems(@NonNull List items) { + this.items = items; + notifyDataSetChanged(); + } + + @NonNull + public List getItems() { + return items; + } + + @Override + public int getItemViewType(int position) { + Object object = items.get(position); + if (object instanceof Integer integer) { + return integer; + } else if (object instanceof TagItem) { + return TYPE_TAG_ITEM; + } else if (object instanceof OpenHoursItem) { + return TYPE_OPEN_TIME_LIST_ITEM; + } + throw new IllegalArgumentException("Unsupported view type"); + } + + @Override + public long getItemId(int position) { + Object object = items.get(position); + if (object instanceof Integer integer) { + return integer; + } else if (object instanceof TagItem tagItem) { + return tagItem.id(); + } else if (object instanceof OpenHoursItem openHoursItem) { + return openHoursItem.id(); + } + return super.getItemId(position); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View itemView; + return switch (viewType) { + case TYPE_DESCRIPTION_ITEM -> { + itemView = themedInflater.inflate(R.layout.edit_poi_description_item, parent, false); + yield new DescriptionItemHolder(itemView); + } + case TYPE_TAG_ITEM -> { + itemView = themedInflater.inflate(R.layout.list_item_poi_tag, parent, false); + yield new TagItemHolder(itemView, app); + } + case TYPE_ADD_TAG -> { + itemView = themedInflater.inflate(R.layout.edit_poi_add_item, parent, false); + yield new AddItemHolder(itemView); + } + case TYPE_BASIC_INFO -> { + itemView = themedInflater.inflate(R.layout.edit_poi_basic_info_item, parent, false); + yield new BasicInfoHolder(itemView); + } + case TYPE_ADD_OPENING_HOURS -> { + itemView = themedInflater.inflate(R.layout.edit_poi_add_opening_hours_item, parent, false); + yield new AddOpeningHoursHolder(itemView); + } + case TYPE_OPEN_TIME_LIST_ITEM -> { + itemView = themedInflater.inflate(R.layout.open_time_list_item, parent, false); + yield new OpenTimeListHolder(itemView); + } + + default -> throw new IllegalArgumentException("Unsupported view type"); + }; + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + Object item = items.get(position); + + if (holder instanceof DescriptionItemHolder descriptionItemHolder) { + descriptionItemHolder.bindView(); + } else if (holder instanceof TagItemHolder tagItemHolder && item instanceof TagItem tagItem) { + tagItemHolder.bindView(holder, tagItem, nightMode); + } else if (holder instanceof AddItemHolder addItemHolder) { + addItemHolder.bindView(); + } else if (holder instanceof BasicInfoHolder basicInfoHolder) { + basicInfoHolder.bindView(); + } else if (holder instanceof AddOpeningHoursHolder addOpeningHoursHolder) { + addOpeningHoursHolder.bindView(); + } else if (holder instanceof OpenTimeListHolder openTimeListHolder && item instanceof OpenHoursItem openHoursItem) { + openTimeListHolder.bindView(openHoursItem); + } + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List payloads) { + if (!Algorithms.isEmpty(payloads) && payloads.get(0) instanceof Integer payLoadInteger) { + if (holder instanceof DescriptionItemHolder descriptionItemHolder) { + switch (payLoadInteger) { + case PAYLOAD_NAME: + descriptionItemHolder.updateName(); + break; + case PAYLOAD_AMENITY: + descriptionItemHolder.updatePoiType(); + break; + } + } else if (holder instanceof TagItemHolder tagItemHolder && PAYLOAD_FOCUS_ON_ITEM == payLoadInteger) { + tagItemHolder.focusOnTagEdit(); + } + } else { + super.onBindViewHolder(holder, position, payloads); + } + } + + @Override + public int getItemCount() { + return items.size(); + } + + private void showKeyboard(@NonNull View view) { + view.requestFocus(); + if (activity != null) { + AndroidUtils.showSoftKeyboard(activity, view); + } + } + + public void clearFocus() { + if (currentTagEditText != null) { + currentTagEditText.clearFocus(); + currentTagEditText = null; + } + } + + public void removeItem(int position) { + notifyItemRemoved(position); + items.remove(position); + } + + class TagItemHolder extends RecyclerView.ViewHolder { + + private final OsmandApplication app; + private final OsmandTextFieldBoxes tagFB; + private final OsmandTextFieldBoxes valueFB; + private final ExtendedEditText tagEditText; + private final AutoCompleteTextView valueEditText; + private final View deleteButton; + + public TagItemHolder(@NonNull View itemView, @NonNull OsmandApplication app) { + super(itemView); + this.app = app; + tagFB = itemView.findViewById(R.id.tag_fb); + valueFB = itemView.findViewById(R.id.value_fb); + tagEditText = itemView.findViewById(R.id.tagEditText); + valueEditText = itemView.findViewById(R.id.valueEditText); + deleteButton = itemView.findViewById(R.id.delete_button); + } + + public void focusOnTagEdit() { + showKeyboard(tagEditText); + } + + public void bindView(@NonNull RecyclerView.ViewHolder holder, @NonNull TagItem tagItem, boolean nightMode) { + Drawable deleteDrawable = app.getUIUtilities().getIcon(R.drawable.ic_action_remove_dark, !nightMode); + + String tag = tagItem.tag(); + String value = tagItem.value(); + tagFB.setClearButton(deleteDrawable); + tagFB.post(tagFB::hideClearButton); + + valueFB.setClearButton(deleteDrawable); + valueFB.post(valueFB::hideClearButton); + + tagEditText.setText(tag); + tagEditText.setAdapter(tagAdapter); + tagEditText.setThreshold(1); + + String[] previousTag = {tag}; + tagEditText.setOnFocusChangeListener((v, hasFocus) -> { + updateCurrentTagEditText(hasFocus); + updateClearButtonVisibility(hasFocus); + if (Algorithms.isEmpty(tagEditText.getText().toString())) { + return; + } + if (!hasFocus) { + if (!getData().isInEdit()) { + String s = tagEditText.getText().toString(); + if (!previousTag[0].equals(s)) { + getData().removeTag(previousTag[0]); + getData().putTag(s, valueEditText.getText().toString()); + previousTag[0] = s; + } + } + } else { + tagAdapter.getFilter().filter(tagEditText.getText()); + } + }); + + valueEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(AMENITY_TEXT_LENGTH)}); + valueEditText.setText(value); + valueEditText.setAdapter(valueAdapter); + valueEditText.setThreshold(3); + valueEditText.addTextChangedListener(new SimpleTextWatcher() { + @Override + public void afterTextChanged(Editable s) { + if (!getData().isInEdit()) { + getData().putTag(tagEditText.getText().toString(), s.toString()); + } + } + }); + + valueEditText.setOnFocusChangeListener((v, hasFocus) -> { + if (hasFocus) { + valueFB.showClearButton(); + valueAdapter.getFilter().filter(valueEditText.getText()); + } else { + valueFB.hideClearButton(); + } + }); + + deleteButton.setOnClickListener(v -> { + int itemPosition = holder.getAdapterPosition(); + editPoiListener.onDeleteItem(itemPosition); + removeItem(itemPosition); + getData().removeTag(tagEditText.getText().toString()); + }); + } + + private void updateCurrentTagEditText(boolean hasFocus){ + if (!hasFocus) { + if (tagEditText.equals(currentTagEditText)) { + currentTagEditText = null; + } + } else { + currentTagEditText = tagEditText; + } + } + + private void updateClearButtonVisibility(boolean hasFocus) { + if (!hasFocus) { + tagFB.hideClearButton(); + } else { + tagFB.showClearButton(); + } + } + } + + class DescriptionItemHolder extends RecyclerView.ViewHolder { + private final TextView nameTextView; + private final TextView amenityTagTextView; + private final TextView amenityTextView; + + public DescriptionItemHolder(@NonNull View itemView) { + super(itemView); + this.nameTextView = itemView.findViewById(R.id.nameTextView); + this.amenityTagTextView = itemView.findViewById(R.id.amenityTagTextView); + this.amenityTextView = itemView.findViewById(R.id.amenityTextView); + } + + public void bindView() { + updateName(); + updatePoiType(); + } + + public void updateName() { + nameTextView.setText(getData().getTag(OSMSettings.OSMTagKey.NAME.getValue())); + } + + public void updatePoiType() { + PoiType pt = getData().getPoiTypeDefined(); + if (pt != null) { + amenityTagTextView.setText(pt.getEditOsmTag()); + amenityTextView.setText(pt.getEditOsmValue()); + } else { + PoiCategory category = getData().getPoiCategory(); + if (category != null) { + amenityTagTextView.setText(category.getDefaultTag()); + } else { + amenityTagTextView.setText(R.string.tag_poi_amenity); + } + amenityTextView.setText(getData().getPoiTypeString()); + } + } + } + + class AddItemHolder extends RecyclerView.ViewHolder { + + private final View addTagButton; + + public AddItemHolder(@NonNull View itemView) { + super(itemView); + this.addTagButton = itemView.findViewById(R.id.addTagButton); + } + + public void bindView() { + addTagButton.setOnClickListener(v -> editPoiListener.onAddNewItem(getAdapterPosition(), TYPE_ADD_TAG)); + } + } + + class BasicInfoHolder extends RecyclerView.ViewHolder { + + private final EditText streetEditText; + private final EditText houseNumberEditText; + private final EditText phoneEditText; + private final EditText webSiteEditText; + private final EditText descriptionEditText; + + public BasicInfoHolder(@NonNull View itemView) { + super(itemView); + streetEditText = itemView.findViewById(R.id.streetEditText); + houseNumberEditText = itemView.findViewById(R.id.houseNumberEditText); + phoneEditText = itemView.findViewById(R.id.phoneEditText); + webSiteEditText = itemView.findViewById(R.id.webSiteEditText); + descriptionEditText = itemView.findViewById(R.id.descriptionEditText); + } + + protected void addTextWatcher(String tag, EditText e) { + e.addTextChangedListener(new SimpleTextWatcher() { + @Override + public void afterTextChanged(Editable s) { + EditPoiData data = getData(); + if (data != null && !data.isInEdit()) { + if (!TextUtils.isEmpty(s)) { + data.putTag(tag, s.toString()); + } else if (editPoiListener.isBasicTagsInitialized() && editPoiListener.isFragmentResumed()) { + data.removeTag(tag); + } + } + } + + }); + } + + public void bindView() { + addTextWatcher(OSMSettings.OSMTagKey.ADDR_STREET.getValue(), streetEditText); + addTextWatcher(OSMSettings.OSMTagKey.WEBSITE.getValue(), webSiteEditText); + addTextWatcher(OSMSettings.OSMTagKey.PHONE.getValue(), phoneEditText); + addTextWatcher(OSMSettings.OSMTagKey.ADDR_HOUSE_NUMBER.getValue(), houseNumberEditText); + addTextWatcher(OSMSettings.OSMTagKey.DESCRIPTION.getValue(), descriptionEditText); + InputFilter[] lengthLimit = editPoiListener.getLengthLimit(); + streetEditText.setFilters(lengthLimit); + houseNumberEditText.setFilters(lengthLimit); + phoneEditText.setFilters(lengthLimit); + webSiteEditText.setFilters(lengthLimit); + descriptionEditText.setFilters(lengthLimit); + + AndroidUtils.setTextHorizontalGravity(streetEditText, Gravity.START); + AndroidUtils.setTextHorizontalGravity(houseNumberEditText, Gravity.START); + AndroidUtils.setTextHorizontalGravity(phoneEditText, Gravity.START); + AndroidUtils.setTextHorizontalGravity(webSiteEditText, Gravity.START); + AndroidUtils.setTextHorizontalGravity(descriptionEditText, Gravity.START); + + EditPoiData data = getData(); + if (data == null) { + return; + } + Map tagValues = data.getTagValues(); + streetEditText.setText(tagValues.get(OSMSettings.OSMTagKey.ADDR_STREET.getValue())); + houseNumberEditText.setText(tagValues.get(OSMSettings.OSMTagKey.ADDR_HOUSE_NUMBER.getValue())); + phoneEditText.setText(tagValues.get(OSMSettings.OSMTagKey.PHONE.getValue())); + webSiteEditText.setText(tagValues.get(OSMSettings.OSMTagKey.WEBSITE.getValue())); + descriptionEditText.setText(tagValues.get(OSMSettings.OSMTagKey.DESCRIPTION.getValue())); + } + } + + class AddOpeningHoursHolder extends RecyclerView.ViewHolder { + + private final View button; + + public AddOpeningHoursHolder(@NonNull View itemView) { + super(itemView); + button = itemView.findViewById(R.id.addOpeningHoursButton); + } + + public void bindView() { + button.setOnClickListener(v -> editPoiListener.onAddNewItem(getAdapterPosition(), TYPE_ADD_OPENING_HOURS)); + } + } + + class OpenTimeListHolder extends RecyclerView.ViewHolder { + + private final ImageView clockIconImageView; + private final TextView daysTextView; + private final LinearLayout timeListContainer; + private final ImageButton deleteItemImageButton; + private final Button addTimeSpanButton; + + public OpenTimeListHolder(@NonNull View itemView) { + super(itemView); + clockIconImageView = itemView.findViewById(R.id.clockIconImageView); + daysTextView = itemView.findViewById(R.id.daysTextView); + timeListContainer = itemView.findViewById(R.id.timeListContainer); + deleteItemImageButton = itemView.findViewById(R.id.deleteItemImageButton); + addTimeSpanButton = itemView.findViewById(R.id.addTimeSpanButton); + } + + public void bindView(@NonNull OpenHoursItem openHoursItem) { + timeListContainer.removeAllViews(); + OpeningHoursParser.OpeningHours openingHours = openingHoursAdapter.getOpeningHours(); + int position = openHoursItem.position(); + clockIconImageView.setImageDrawable(openingHoursAdapter.getClockDrawable()); + + if (openingHours.getRules().get(position) instanceof OpeningHoursParser.BasicOpeningHourRule rule) { + StringBuilder stringBuilder = new StringBuilder(); + rule.appendDaysString(stringBuilder); + + daysTextView.setText(stringBuilder.toString()); + daysTextView.setOnClickListener(v -> { + OpeningHoursDaysDialogFragment fragment = + OpeningHoursDaysDialogFragment.createInstance(rule, position); + fragment.show(getManager(), "OpenTimeDialogFragment"); + }); + + TIntArrayList startTimes = rule.getStartTimes(); + TIntArrayList endTimes = rule.getEndTimes(); + for (int i = 0; i < startTimes.size(); i++) { + View timeFromToLayout = LayoutInflater.from(activity) + .inflate(R.layout.time_from_to_layout, timeListContainer, false); + TextView openingTextView = timeFromToLayout.findViewById(R.id.openingTextView); + openingTextView.setText(Algorithms.formatMinutesDuration(startTimes.get(i))); + + TextView closingTextView = timeFromToLayout.findViewById(R.id.closingTextView); + closingTextView.setText(Algorithms.formatMinutesDuration(endTimes.get(i))); + + openingTextView.setTag(i); + openingTextView.setOnClickListener(v -> { + int index = (int) v.getTag(); + OpeningHoursHoursDialogFragment.createInstance(rule, position, true, index) + .show(getManager(), "OpeningHoursHoursDialogFragment"); + }); + closingTextView.setTag(i); + closingTextView.setOnClickListener(v -> { + int index = (int) v.getTag(); + OpeningHoursHoursDialogFragment.createInstance(rule, position, false, index) + .show(getManager(), "OpeningHoursHoursDialogFragment"); + }); + + ImageButton deleteTimeSpanImageButton = timeFromToLayout + .findViewById(R.id.deleteTimespanImageButton); + deleteTimeSpanImageButton.setImageDrawable(openingHoursAdapter.getDeleteDrawable()); + int timeSpanPosition = i; + deleteTimeSpanImageButton.setOnClickListener(v -> { + if (startTimes.size() == 1) { + openingHours.getRules().remove(position); + } else { + rule.deleteTimeRange(timeSpanPosition); + } + openingHoursAdapter.updateHoursData(); + notifyDataSetChanged(); + }); + timeListContainer.addView(timeFromToLayout); + } + + deleteItemImageButton.setVisibility(View.GONE); + addTimeSpanButton.setVisibility(View.VISIBLE); + addTimeSpanButton.setOnClickListener(v -> OpeningHoursHoursDialogFragment.createInstance(rule, position, true, + startTimes.size()).show(getManager(), + "TimePickerDialogFragment")); + } else if (openingHours.getRules().get(position) instanceof OpeningHoursParser.UnparseableRule) { + daysTextView.setText(openingHours.getRules().get(position).toRuleString()); + timeListContainer.removeAllViews(); + + deleteItemImageButton.setVisibility(View.VISIBLE); + deleteItemImageButton.setImageDrawable(openingHoursAdapter.getDeleteDrawable()); + deleteItemImageButton.setOnClickListener(v -> { + openingHours.getRules().remove(position); + openingHoursAdapter.updateHoursData(); + notifyDataSetChanged(); + }); + addTimeSpanButton.setVisibility(View.GONE); + } + } + + private FragmentManager getManager() { + return editPoiListener.getChildFragmentManager(); + } + + } + + + public interface EditPoiListener { + void onAddNewItem(int position, int buttonType); + void onDeleteItem(int position); + + InputFilter[] getLengthLimit(); + + FragmentManager getChildFragmentManager(); + + default boolean isFragmentResumed() { + return false; + } + + default boolean isBasicTagsInitialized() { + return false; + } + } +} + diff --git a/OsmAnd/src/net/osmand/plus/plugins/osmedit/fragments/NewAdvancedEditPoiFragment.java b/OsmAnd/src/net/osmand/plus/plugins/osmedit/fragments/NewAdvancedEditPoiFragment.java new file mode 100644 index 00000000000..e0d6d69b2b0 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/plugins/osmedit/fragments/NewAdvancedEditPoiFragment.java @@ -0,0 +1,333 @@ +package net.osmand.plus.plugins.osmedit.fragments; + +import static net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter.PAYLOAD_AMENITY; +import static net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter.PAYLOAD_FOCUS_ON_ITEM; +import static net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter.PAYLOAD_NAME; +import static net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter.TYPE_ADD_TAG; +import static net.osmand.plus.plugins.osmedit.fragments.EditPoiContentAdapter.TYPE_DESCRIPTION_ITEM; + +import android.content.Context; +import android.graphics.Rect; +import android.os.Bundle; +import android.text.InputFilter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Filter; +import android.widget.Filterable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import net.osmand.osm.AbstractPoiType; +import net.osmand.osm.MapPoiTypes; +import net.osmand.osm.PoiCategory; +import net.osmand.osm.PoiFilter; +import net.osmand.osm.PoiType; +import net.osmand.osm.edit.Entity; +import net.osmand.osm.edit.OSMSettings; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.base.BaseOsmAndFragment; +import net.osmand.plus.plugins.osmedit.data.EditPoiData; +import net.osmand.plus.plugins.osmedit.dialogs.EditPoiDialogFragment; +import net.osmand.plus.utils.AndroidUtils; +import net.osmand.util.Algorithms; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class NewAdvancedEditPoiFragment extends BaseOsmAndFragment implements EditPoiDialogFragment.OnFragmentActivatedListener, + EditPoiDialogFragment.OnSaveButtonClickListener { + + private EditPoiData.TagsChangedListener mTagsChangedListener; + + private String[] allTags; + private EditPoiContentAdapter contentAdapter; + private RecyclerView recyclerView; + + public OsmTagsArrayAdapter tagAdapter; + private ArrayAdapter valueAdapter; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + updateNightMode(); + View view = themedInflater.inflate(R.layout.fragment_edit_poi_advanced_new, container, false); + + recyclerView = view.findViewById(R.id.content_recycler_view); + recyclerView.setNestedScrollingEnabled(true); + + tagAdapter = new OsmTagsArrayAdapter(app, R.layout.list_textview); + valueAdapter = new ArrayAdapter<>(app, R.layout.list_textview); + + Set tagKeys = new HashSet<>(); + Set valueKeys = new HashSet<>(); + fillKeysValues(tagKeys, valueKeys); + + setTagData(tagKeys.toArray(new String[0])); + setValueData(valueKeys.toArray(new String[0])); + allTags = tagKeys.toArray(new String[0]); + + EditPoiContentAdapter.EditPoiListener editPoiListener = new EditPoiContentAdapter.EditPoiListener() { + @Override + public void onAddNewItem(int position, int buttonType) { + long id = System.currentTimeMillis(); + contentAdapter.getItems().add(position, new TagItem("", "", id)); + contentAdapter.notifyItemInserted(position); + recyclerView.postDelayed(() -> { + getEditPoiFragment().smoothScrollToBottom(); + getEditPoiFragment().scrollView.post(() -> contentAdapter.notifyItemChanged(position, PAYLOAD_FOCUS_ON_ITEM)); + }, 300); + } + + @Override + public void onDeleteItem(int position) { + LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); + boolean clearFocus = false; + View focusedView = recyclerView.getFocusedChild(); + if (focusedView != null && manager != null) { + Rect mReact = new Rect(); + getEditPoiFragment().scrollView.getHitRect(mReact); + + clearFocus = !focusedView.getLocalVisibleRect(mReact); + } + + if (clearFocus) { + AndroidUtils.hideSoftKeyboard(requireActivity(), focusedView); + focusedView.clearFocus(); + } + } + + @Override + public InputFilter[] getLengthLimit() { + return new InputFilter[0]; + } + + @Override + public FragmentManager getChildFragmentManager() { + return NewAdvancedEditPoiFragment.this.getChildFragmentManager(); + } + }; + + contentAdapter = new EditPoiContentAdapter((MapActivity) requireActivity(), getContentList(), + valueAdapter, tagAdapter, null, nightMode, getEditPoiFragment(), editPoiListener); + recyclerView.setLayoutManager(new LinearLayoutManager(app)); + recyclerView.setAdapter(contentAdapter); + + return view; + } + + public record TagItem(@NonNull String tag, @NonNull String value, long id) { + } + + public void setValueData(@NonNull String[] values) { + valueAdapter.clear(); + for (String s : values) { + valueAdapter.add(s); + } + valueAdapter.sort(String.CASE_INSENSITIVE_ORDER); + valueAdapter.notifyDataSetChanged(); + } + + public void setTagData(@NonNull String[] tags) { + tagAdapter.clear(); + for (String s : tags) { + tagAdapter.add(s); + } + tagAdapter.sort(String.CASE_INSENSITIVE_ORDER); + tagAdapter.notifyDataSetChanged(); + } + + private void updateViews() { + if (contentAdapter != null) { + contentAdapter.setItems(getContentList()); + } + } + + private List getContentList() { + List list = new ArrayList<>(); + list.add(TYPE_DESCRIPTION_ITEM); + + EditPoiData editPoiData = getData(); + + editPoiData.setIsInEdit(true); + PoiType pt = editPoiData.getCurrentPoiType(); + String currentPoiTypeKey = ""; + if (pt != null) { + currentPoiTypeKey = pt.getEditOsmTag(); + } + for (Map.Entry tag : editPoiData.getTagValues().entrySet()) { + if (tag.getKey().equals(Entity.POI_TYPE_TAG) + || tag.getKey().equals(OSMSettings.OSMTagKey.NAME.getValue()) + || tag.getKey().startsWith(Entity.REMOVE_TAG_PREFIX) + || tag.getKey().equals(currentPoiTypeKey)) { + continue; + } + list.add(new TagItem(tag.getKey(), tag.getValue(), System.currentTimeMillis())); + } + + editPoiData.setIsInEdit(false); + list.add(TYPE_ADD_TAG); + + return list; + } + + private void fillKeysValues(@NonNull Set tagKeys, @NonNull Set valueKeys) { + MapPoiTypes mapPoiTypes = app.getPoiTypes(); + Map translatedTypes = getData().getAllTranslatedSubTypes(); + for (AbstractPoiType abstractPoiType : translatedTypes.values()) { + addPoiToStringSet(abstractPoiType, tagKeys, valueKeys); + } + addPoiToStringSet(mapPoiTypes.getOtherMapCategory(), tagKeys, valueKeys); + } + + @Override + public void onResume() { + super.onResume(); + updateName(); + updatePoiType(); + mTagsChangedListener = anyTag -> { + if (Algorithms.objectEquals(anyTag, OSMSettings.OSMTagKey.NAME.getValue())) { + updateName(); + } + if (Algorithms.objectEquals(anyTag, Entity.POI_TYPE_TAG)) { + updatePoiType(); + } + }; + getData().addListener(mTagsChangedListener); + } + + @Override + public void onPause() { + super.onPause(); + getData().deleteListener(mTagsChangedListener); + } + + private EditPoiDialogFragment getEditPoiFragment() { + return (EditPoiDialogFragment) getParentFragment(); + } + + private EditPoiData getData() { + return getEditPoiFragment().getEditPoiData(); + } + + @Override + public void onFragmentActivated() { + updateViews(); + } + + private void updateName() { + contentAdapter.notifyItemChanged(contentAdapter.getItems().indexOf(TYPE_DESCRIPTION_ITEM), PAYLOAD_NAME); + } + + private void updatePoiType() { + contentAdapter.notifyItemChanged(contentAdapter.getItems().indexOf(TYPE_DESCRIPTION_ITEM), PAYLOAD_AMENITY); + } + + @Override + public void onSaveButtonClick() { + contentAdapter.clearFocus(); + } + + public static void addPoiToStringSet(AbstractPoiType abstractPoiType, Set stringSet, + Set values) { + if (abstractPoiType instanceof PoiType) { + PoiType poiType = (PoiType) abstractPoiType; + if (poiType.isNotEditableOsm() || poiType.getBaseLangType() != null) { + return; + } + if (poiType.getEditOsmTag() != null && + !poiType.getEditOsmTag().equals(OSMSettings.OSMTagKey.NAME.getValue())) { + String editOsmTag = poiType.getEditOsmTag(); + stringSet.add(editOsmTag); + if (poiType.getOsmTag2() != null) { + stringSet.add(poiType.getOsmTag2()); + } + if (poiType.getEditOsmTag2() != null) { + stringSet.add(poiType.getEditOsmTag2()); + } + } + if (poiType.getEditOsmValue() != null) { + values.add(poiType.getEditOsmValue()); + } + if (poiType.getOsmValue2() != null) { + values.add(poiType.getOsmValue2()); + } + for (PoiType type : poiType.getPoiAdditionals()) { + addPoiToStringSet(type, stringSet, values); + } + } else if (abstractPoiType instanceof PoiCategory) { + PoiCategory poiCategory = (PoiCategory) abstractPoiType; + for (PoiFilter filter : poiCategory.getPoiFilters()) { + addPoiToStringSet(filter, stringSet, values); + } + for (PoiType poiType : poiCategory.getPoiTypes()) { + addPoiToStringSet(poiType, stringSet, values); + } + for (PoiType poiType : poiCategory.getPoiAdditionals()) { + addPoiToStringSet(poiType, stringSet, values); + } + } else if (abstractPoiType instanceof PoiFilter) { + PoiFilter poiFilter = (PoiFilter) abstractPoiType; + for (PoiType poiType : poiFilter.getPoiTypes()) { + addPoiToStringSet(poiType, stringSet, values); + } + } else { + throw new IllegalArgumentException("abstractPoiType can't be instance of class " + + abstractPoiType.getClass()); + } + } + + public class OsmTagsArrayAdapter extends ArrayAdapter implements Filterable { + + private OsmTagsFilter filter; + + public OsmTagsArrayAdapter(Context context, int resource) { + super(context, resource); + } + + @Override + public Filter getFilter() { + if (filter == null) { + filter = new OsmTagsFilter(); + } + return filter; + } + } + + private class OsmTagsFilter extends Filter { + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults results = new FilterResults(); + List filteredTags = new ArrayList(); + String query = constraint.toString().trim(); + for (String tag : allTags) { + if (tag.startsWith(query) || tag.contains(":" + query)) { + filteredTags.add(tag); + } + } + results.values = filteredTags; + results.count = filteredTags.size(); + return results; + } + + @SuppressWarnings("unchecked") + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + if (results.values != null) { + String[] filteredHints = ((List) results.values).toArray(new String[0]); + setTagData(filteredHints); + tagAdapter.notifyDataSetChanged(); + } + } + } +} \ No newline at end of file