package net.osmand.plus.quickaction.actions; import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.support.design.widget.TextInputLayout; import android.support.v7.widget.SwitchCompat; import android.text.Editable; import android.text.TextWatcher; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import net.osmand.CallbackWithObject; import net.osmand.data.LatLon; import net.osmand.osm.AbstractPoiType; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; import net.osmand.osm.PoiType; import net.osmand.osm.edit.Node; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.osmedit.EditPoiData; import net.osmand.plus.osmedit.EditPoiDialogFragment; import net.osmand.plus.osmedit.OpenstreetmapLocalUtil; import net.osmand.plus.osmedit.OpenstreetmapPoint; import net.osmand.plus.osmedit.OpenstreetmapUtil; import net.osmand.plus.osmedit.OsmEditingPlugin; import net.osmand.plus.osmedit.OsmPoint; import net.osmand.plus.osmedit.dialogs.PoiSubTypeDialogFragment; import net.osmand.plus.quickaction.CreateEditActionDialog; import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickActionFactory; import net.osmand.util.Algorithms; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static net.osmand.plus.osmedit.AdvancedEditPoiFragment.addPoiToStringSet; import static net.osmand.plus.osmedit.EditPoiData.POI_TYPE_TAG; public class AddPOIAction extends QuickAction { public static final int TYPE = 13; public static final String KEY_TAG = "key_tag"; public static final String KEY_DIALOG = "dialog"; private transient EditText title; private transient String prevType = ""; public AddPOIAction() { super(TYPE); } public AddPOIAction(QuickAction quickAction) { super(quickAction); } @Override public void execute(final MapActivity activity) { LatLon latLon = activity.getMapView() .getCurrentRotatedTileBox() .getCenterLatLon(); OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); if (plugin == null) return; Node node = new Node(latLon.getLatitude(), latLon.getLongitude(), -1); node.replaceTags(getTagsFromParams()); EditPoiData editPoiData = new EditPoiData(node, activity.getMyApplication()); if (Boolean.valueOf(getParams().get(KEY_DIALOG))) { Node newNode = editPoiData.getEntity(); EditPoiDialogFragment editPoiDialogFragment = EditPoiDialogFragment.createInstance(newNode, true, getTagsFromParams()); editPoiDialogFragment.show(activity.getSupportFragmentManager(), EditPoiDialogFragment.TAG); } else { OpenstreetmapUtil mOpenstreetmapUtil; if (activity.getMyApplication().getSettings().OFFLINE_EDITION.get() || !activity.getMyApplication().getSettings().isInternetConnectionAvailable(true)) { mOpenstreetmapUtil = plugin.getPoiModificationLocalUtil(); } else { mOpenstreetmapUtil = plugin.getPoiModificationRemoteUtil(); } final boolean offlineEdit = mOpenstreetmapUtil instanceof OpenstreetmapLocalUtil; Node newNode = new Node(node.getLatitude(), node.getLongitude(), node.getId()); OsmPoint.Action action = newNode.getId() < 0 ? OsmPoint.Action.CREATE : OsmPoint.Action.MODIFY; for (Map.Entry<String, String> tag : editPoiData.getTagValues().entrySet()) { if (tag.getKey().equals(EditPoiData.POI_TYPE_TAG)) { final PoiType poiType = editPoiData.getAllTranslatedSubTypes().get(tag.getValue().trim().toLowerCase()); if (poiType != null) { newNode.putTagNoLC(poiType.getOsmTag(), poiType.getOsmValue()); if (poiType.getOsmTag2() != null) { newNode.putTagNoLC(poiType.getOsmTag2(), poiType.getOsmValue2()); } } else if (!Algorithms.isEmpty(tag.getValue())) { newNode.putTagNoLC(editPoiData.getPoiCategory().getDefaultTag(), tag.getValue()); } if (offlineEdit && !Algorithms.isEmpty(tag.getValue())) { newNode.putTagNoLC(tag.getKey(), tag.getValue()); } } else if (!Algorithms.isEmpty(tag.getKey()) && !Algorithms.isEmpty(tag.getValue())) { newNode.putTagNoLC(tag.getKey(), tag.getValue()); } } EditPoiDialogFragment.commitNode(action, newNode, mOpenstreetmapUtil.getEntityInfo(newNode.getId()), "", false, new CallbackWithObject<Node>() { @Override public boolean processResult(Node result) { if (result != null) { OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); if (plugin != null && offlineEdit) { List<OpenstreetmapPoint> points = plugin.getDBPOI().getOpenstreetmapPoints(); if (activity instanceof MapActivity && points.size() > 0) { OsmPoint point = points.get(points.size() - 1); activity.getContextMenu().showOrUpdate( new LatLon(point.getLatitude(), point.getLongitude()), plugin.getOsmEditsLayer(activity).getObjectName(point), point); } } if (activity instanceof MapActivity) { activity.getMapView().refreshMap(true); } } else { // OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); // mOpenstreetmapUtil = plugin.getPoiModificationLocalUtil(); // Button saveButton = (Button) view.findViewById(R.id.saveButton); // saveButton.setText(mOpenstreetmapUtil instanceof OpenstreetmapRemoteUtil // ? R.string.shared_string_upload : R.string.shared_string_save); } return false; } }, activity, mOpenstreetmapUtil); } } @Override public void setAutoGeneratedTitle(EditText title) { this.title = title; } @Override public void drawUI(final ViewGroup parent, final MapActivity activity) { final View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.quick_action_add_poi_layout, parent, false); final OsmandApplication application = activity.getMyApplication(); Drawable deleteDrawable = application.getIconsCache().getPaintedIcon(R.drawable.ic_action_remove_dark, activity.getResources().getColor(R.color.dash_search_icon_dark)); final LinearLayout editTagsLineaLayout = (LinearLayout) view.findViewById(R.id.editTagsList); final MapPoiTypes poiTypes = application.getPoiTypes(); final Map<String, PoiType> allTranslatedNames = poiTypes.getAllTranslatedNames(true); final TagAdapterLinearLayoutHack mAdapter = new TagAdapterLinearLayoutHack(editTagsLineaLayout, getTagsFromParams(), deleteDrawable); // It is possible to not restart initialization every time, and probably move initialization to appInit Map<String, PoiType> translatedTypes = poiTypes.getAllTranslatedNames(true); HashSet<String> tagKeys = new HashSet<>(); HashSet<String> valueKeys = new HashSet<>(); for (AbstractPoiType abstractPoiType : translatedTypes.values()) { addPoiToStringSet(abstractPoiType, tagKeys, valueKeys); } addPoiToStringSet(poiTypes.getOtherMapCategory(), tagKeys, valueKeys); tagKeys.addAll(EditPoiDialogFragment.BASIC_TAGS); mAdapter.setTagData(tagKeys.toArray(new String[tagKeys.size()])); mAdapter.setValueData(valueKeys.toArray(new String[valueKeys.size()])); Button addTagButton = (Button) view.findViewById(R.id.addTagButton); addTagButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for (int i = 0; i < editTagsLineaLayout.getChildCount(); i++) { View item = editTagsLineaLayout.getChildAt(i); if (((EditText) item.findViewById(R.id.tagEditText)).getText().toString().isEmpty() && ((EditText) item.findViewById(R.id.valueEditText)).getText().toString().isEmpty()) return; } mAdapter.addTagView("", ""); } }); mAdapter.updateViews(); final TextInputLayout poiTypeTextInputLayout = (TextInputLayout) view.findViewById(R.id.poiTypeTextInputLayout); final AutoCompleteTextView poiTypeEditText = (AutoCompleteTextView) view.findViewById(R.id.poiTypeEditText); final SwitchCompat showDialog = (SwitchCompat) view.findViewById(R.id.saveButton); // showDialog.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { // @Override // public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // getParams().put(KEY_DIALOG, Boolean.toString(isChecked)); // } // }); showDialog.setChecked(Boolean.valueOf(getParams().get(KEY_DIALOG))); final String text = getTagsFromParams().get(POI_TYPE_TAG); poiTypeEditText.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 tp = s.toString(); putTagIntoParams(POI_TYPE_TAG, tp); PoiCategory category = getCategory(allTranslatedNames); if (category != null) { poiTypeTextInputLayout.setHint(category.getTranslation()); } String add = application.getString(R.string.shared_string_add); if (title != null) { if (prevType.equals(title.getText().toString()) || title.getText().toString().equals(activity.getString(getNameRes())) || title.getText().toString().equals((add + " "))) { if (!tp.isEmpty()) { title.setText(add + " " + tp); prevType = title.getText().toString(); } } } } }); poiTypeEditText.setText(text != null ? text : ""); poiTypeEditText.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(final View v, MotionEvent event) { final EditText editText = (EditText) v; final int DRAWABLE_RIGHT = 2; if (event.getAction() == MotionEvent.ACTION_UP) { if (event.getX() >= (editText.getRight() - editText.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width() - editText.getPaddingRight())) { PoiCategory category = getCategory(allTranslatedNames); PoiCategory tempPoiCategory = (category != null) ? category : poiTypes.getOtherPoiCategory(); PoiSubTypeDialogFragment f = PoiSubTypeDialogFragment.createInstance(tempPoiCategory); f.setOnItemSelectListener(new PoiSubTypeDialogFragment.OnItemSelectListener() { @Override public void select(String category) { poiTypeEditText.setText(category); } }); CreateEditActionDialog parentFragment = (CreateEditActionDialog) activity.getSupportFragmentManager().findFragmentByTag(CreateEditActionDialog.TAG); f.show(activity.getSupportFragmentManager(), "PoiSubTypeDialogFragment"); return true; } } return false; } }); setUpAdapterForPoiTypeEditText(activity, allTranslatedNames, poiTypeEditText); ImageButton onlineDocumentationButton = (ImageButton) view.findViewById(R.id.onlineDocumentationButton); onlineDocumentationButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://wiki.openstreetmap.org/wiki/Map_Features"))); } }); boolean isLightTheme = activity.getMyApplication().getSettings().OSMAND_THEME.get() == OsmandSettings.OSMAND_LIGHT_THEME; final int colorId = isLightTheme ? R.color.inactive_item_orange : R.color.dash_search_icon_dark; final int color = activity.getResources().getColor(colorId); onlineDocumentationButton.setImageDrawable(activity.getMyApplication().getIconsCache().getPaintedIcon(R.drawable.ic_action_help, color)); // poiTypeEditText.setCompoundDrawables(null, null, activity.getMyApplication().getIconsCache().getPaintedIcon(R.drawable.ic_action_arrow_drop_down, color), null); // Button addTypeButton = (Button) view.findViewById(R.id.addTypeButton); // addTypeButton.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // PoiSubTypeDialogFragment f = PoiSubTypeDialogFragment.createInstance(poiTypes.getOtherPoiCategory()); // f.setOnItemSelectListener(new PoiSubTypeDialogFragment.OnItemSelectListener() { // @Override // public void select(String category) { // putTagIntoParams(POI_TYPE_TAG, category); // } // }); // // CreateEditActionDialog parentFragment = (CreateEditActionDialog) activity.getSupportFragmentManager().findFragmentByTag(CreateEditActionDialog.TAG); // f.show(parentFragment.getChildFragmentManager(), "PoiSubTypeDialogFragment"); // } // }); parent.addView(view); } private void setUpAdapterForPoiTypeEditText(final MapActivity activity, final Map<String, PoiType> allTranslatedNames, final AutoCompleteTextView poiTypeEditText) { final Map<String, PoiType> subCategories = new LinkedHashMap<>(); // PoiCategory ct = editPoiData.getPoiCategory(); // if (ct != null) { // for (PoiType s : ct.getPoiTypes()) { // if (!s.isReference() && !s.isNotEditableOsm() && s.getBaseLangType() == null) { // addMapEntryAdapter(subCategories, s.getTranslation(), s); // if(!s.getKeyName().contains("osmand")) { // addMapEntryAdapter(subCategories, s.getKeyName().replace('_', ' '), s); // } // } // } // } for (Map.Entry<String, PoiType> s : allTranslatedNames.entrySet()) { addMapEntryAdapter(subCategories, s.getKey(), s.getValue()); } final ArrayAdapter<Object> adapter; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { adapter = new ArrayAdapter<>(activity, R.layout.list_textview, subCategories.keySet().toArray()); } else { TypedValue typedValue = new TypedValue(); Resources.Theme theme = activity.getTheme(); theme.resolveAttribute(android.R.attr.textColorSecondary, typedValue, true); final int textColor = typedValue.data; adapter = new ArrayAdapter<Object>(activity, R.layout.list_textview, subCategories.keySet().toArray()) { @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = super.getView(position, convertView, parent); ((TextView) view.findViewById(R.id.textView)).setTextColor(textColor); return view; } }; } adapter.sort(new Comparator<Object>() { @Override public int compare(Object lhs, Object rhs) { return lhs.toString().compareTo(rhs.toString()); } }); poiTypeEditText.setAdapter(adapter); poiTypeEditText.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { Object item = parent.getAdapter().getItem(position); poiTypeEditText.setText(item.toString()); setUpAdapterForPoiTypeEditText(activity, allTranslatedNames, poiTypeEditText); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); } private PoiCategory getCategory(Map<String, PoiType> allTranslatedNames) { String tp = getTagsFromParams().get(POI_TYPE_TAG); if (tp == null) return null; PoiType pt = allTranslatedNames.get(tp.toLowerCase()); if (pt != null) { return pt.getCategory(); } else return null; } private void addMapEntryAdapter(final Map<String, PoiType> subCategories, String key, PoiType v) { if (!subCategories.containsKey(key.toLowerCase())) { subCategories.put(Algorithms.capitalizeFirstLetterAndLowercase(key), v); } } private class TagAdapterLinearLayoutHack { private final LinearLayout linearLayout; private final Map<String, String> tagsData; private final ArrayAdapter<String> tagAdapter; private final ArrayAdapter<String> valueAdapter; private final Drawable deleteDrawable; public TagAdapterLinearLayoutHack(LinearLayout linearLayout, Map<String, String> tagsData, Drawable deleteDrawable) { this.linearLayout = linearLayout; this.tagsData = tagsData; this.deleteDrawable = deleteDrawable; tagAdapter = new ArrayAdapter<>(linearLayout.getContext(), R.layout.list_textview); valueAdapter = new ArrayAdapter<>(linearLayout.getContext(), R.layout.list_textview); } public void updateViews() { linearLayout.removeAllViews(); List<Map.Entry<String, String>> entries = new ArrayList<>(tagsData.entrySet()); for (Map.Entry<String, String> tag : entries) { if (tag.getKey().equals(POI_TYPE_TAG) /*|| tag.getKey().equals(OSMSettings.OSMTagKey.NAME.getValue())*/) continue; addTagView(tag.getKey(), tag.getValue()); } } public void addTagView(String tg, String vl) { View convertView = LayoutInflater.from(linearLayout.getContext()) .inflate(R.layout.poi_tag_list_item, null, false); final AutoCompleteTextView tagEditText = (AutoCompleteTextView) convertView.findViewById(R.id.tagEditText); ImageButton deleteItemImageButton = (ImageButton) convertView.findViewById(R.id.deleteItemImageButton); deleteItemImageButton.setImageDrawable(deleteDrawable); final String[] previousTag = new String[]{tg}; deleteItemImageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { linearLayout.removeView((View) v.getParent()); tagsData.remove(tagEditText.getText().toString()); setTagsIntoParams(tagsData); } }); final AutoCompleteTextView valueEditText = (AutoCompleteTextView) convertView.findViewById(R.id.valueEditText); tagEditText.setText(tg); tagEditText.setAdapter(tagAdapter); tagEditText.setThreshold(1); tagEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { String s = tagEditText.getText().toString(); tagsData.remove(previousTag[0]); tagsData.put(s.toString(), valueEditText.getText().toString()); previousTag[0] = s.toString(); setTagsIntoParams(tagsData); } else { tagAdapter.getFilter().filter(tagEditText.getText()); } } }); valueEditText.setText(vl); valueEditText.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) { tagsData.put(tagEditText.getText().toString(), s.toString()); setTagsIntoParams(tagsData); } }); initAutocompleteTextView(valueEditText, valueAdapter); linearLayout.addView(convertView); tagEditText.requestFocus(); } public void setTagData(String[] tags) { tagAdapter.clear(); for (String s : tags) { tagAdapter.add(s); } tagAdapter.sort(String.CASE_INSENSITIVE_ORDER); tagAdapter.notifyDataSetChanged(); } public void setValueData(String[] values) { valueAdapter.clear(); for (String s : values) { valueAdapter.add(s); } valueAdapter.sort(String.CASE_INSENSITIVE_ORDER); valueAdapter.notifyDataSetChanged(); } } private static void initAutocompleteTextView(final AutoCompleteTextView textView, final ArrayAdapter<String> adapter) { textView.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { adapter.getFilter().filter(textView.getText()); } } }); } @Override public boolean fillParams(View root, MapActivity activity) { getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); return !getParams().isEmpty() && (getParams().get(KEY_TAG) != null || !getTagsFromParams().isEmpty()); } private Map<String, String> getTagsFromParams() { Map<String, String> quickActions = null; if (getParams().get(KEY_TAG) != null) { String json = getParams().get(KEY_TAG); Type type = new TypeToken<LinkedHashMap<String, String>>() { }.getType(); quickActions = new Gson().fromJson(json, type); } return quickActions != null ? quickActions : new LinkedHashMap<String, String>(); } private void setTagsIntoParams(Map<String, String> tags) { getParams().put(KEY_TAG, new Gson().toJson(tags)); } private void putTagIntoParams(String tag, String value) { Map<String, String> tagsFromParams = getTagsFromParams(); tagsFromParams.put(tag, value); setTagsIntoParams(tagsFromParams); } }