WayPoint context menu upgrade:

Link to article
Phone, website, email: a separate line
Make clickable
Show only this waypoint group
This commit is contained in:
Nazar 2019-07-13 13:58:10 +03:00 committed by crimean
parent bbba770fc2
commit 108b2f6730
3 changed files with 247 additions and 24 deletions

View file

@ -2,6 +2,7 @@ package net.osmand.plus.mapcontextmenu;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Color; import android.graphics.Color;
@ -14,6 +15,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.view.ContextThemeWrapper; import android.support.v7.view.ContextThemeWrapper;
import android.text.ClipboardManager; import android.text.ClipboardManager;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
@ -500,9 +502,23 @@ public class MenuBuilder {
needLinks, textLinesLimit, isUrl, onClickListener, matchWidthDivider); needLinks, textLinesLimit, isUrl, onClickListener, matchWidthDivider);
} }
public View buildRow(final View view, Drawable icon, final String buttonText, final String text, int textColor, String secondaryText,
boolean collapsable, final CollapsableView collapsableView, boolean needLinks,
int textLinesLimit, boolean isUrl, OnClickListener onClickListener, boolean matchWidthDivider) {
return buildRow(view, icon, buttonText, text, textColor, secondaryText, collapsable, collapsableView,
needLinks, textLinesLimit, isUrl, false, false, onClickListener, matchWidthDivider);
}
public View buildRow(View view, int iconId, String buttonText, String text, int textColor,
boolean collapsable, final CollapsableView collapsableView,
boolean needLinks, int textLinesLimit, boolean isUrl, boolean isNumber, boolean isEmail, OnClickListener onClickListener, boolean matchWidthDivider) {
return buildRow(view, iconId == 0 ? null : getRowIcon(iconId), buttonText, text, textColor, null, collapsable, collapsableView,
needLinks, textLinesLimit, isUrl, isNumber, isEmail, onClickListener, matchWidthDivider);
}
public View buildRow(final View view, Drawable icon, final String buttonText, final String text, int textColor, String secondaryText, public View buildRow(final View view, Drawable icon, final String buttonText, final String text, int textColor, String secondaryText,
boolean collapsable, final CollapsableView collapsableView, boolean needLinks, boolean collapsable, final CollapsableView collapsableView, boolean needLinks,
int textLinesLimit, boolean isUrl, OnClickListener onClickListener, boolean matchWidthDivider) { int textLinesLimit, boolean isUrl, boolean isNumber, boolean isEmail, OnClickListener onClickListener, boolean matchWidthDivider) {
if (!isFirstRow()) { if (!isFirstRow()) {
buildRowDivider(view); buildRowDivider(view);
@ -567,7 +583,7 @@ public class MenuBuilder {
int linkTextColor = ContextCompat.getColor(view.getContext(), light ? R.color.ctx_menu_bottom_view_url_color_light : R.color.ctx_menu_bottom_view_url_color_dark); int linkTextColor = ContextCompat.getColor(view.getContext(), light ? R.color.ctx_menu_bottom_view_url_color_light : R.color.ctx_menu_bottom_view_url_color_dark);
if (isUrl) { if (isUrl || isNumber || isEmail) {
textView.setTextColor(linkTextColor); textView.setTextColor(linkTextColor);
} else if (needLinks) { } else if (needLinks) {
Linkify.addLinks(textView, Linkify.ALL); Linkify.addLinks(textView, Linkify.ALL);
@ -664,6 +680,46 @@ public class MenuBuilder {
v.getContext().startActivity(intent); v.getContext().startActivity(intent);
} }
}); });
} else if (isNumber) {
ll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
//todo extract to MenuBuilder method
//the same logic uses for AmenityMenuBuilder and can be generalized
final String[] phones = text.split(",|;");
for (int i = 0; i < phones.length; i++) {
phones[i] = phones[i].trim();
}
if (phones.length > 1) {
AlertDialog.Builder dlg = new AlertDialog.Builder(v.getContext());
dlg.setNegativeButton(R.string.shared_string_cancel, null);
dlg.setItems(phones, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + phones[which]));
v.getContext().startActivity(intent);
}
});
dlg.show();
} else {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + text));
v.getContext().startActivity(intent);
}
}
});
} else if (isEmail) { //todo the same for the phones
ll.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:" + text));
v.getContext().startActivity(intent);
}
});
} }
((LinearLayout) view).addView(baseView); ((LinearLayout) view).addView(baseView);

View file

@ -26,10 +26,19 @@ import net.osmand.util.Algorithms;
import java.io.File; import java.io.File;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WptPtMenuBuilder extends MenuBuilder { public class WptPtMenuBuilder extends MenuBuilder {
//todo extract
final String KEY_PHONE = "Phone: ";
final String KEY_EMAIL = "Email: ";
final String PHONE_REGEX = "(\\(?\\+?[0-9]*\\)?)?[0-9_\\- \\(\\)]*";
final String EMAIL_REGEX = "([0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+[a-zA-Z]{2,9})";
private final WptPt wpt; private final WptPt wpt;
@ -72,16 +81,46 @@ public class WptPtMenuBuilder extends MenuBuilder {
null, Algorithms.capitalizeFirstLetterAndLowercase(app.getString(R.string.plugin_distance_point_hdop)) + ": " + (int)wpt.hdop, 0, null, Algorithms.capitalizeFirstLetterAndLowercase(app.getString(R.string.plugin_distance_point_hdop)) + ": " + (int)wpt.hdop, 0,
false, null, false, 0, false, null, false); false, null, false, 0, false, null, false);
} }
if (!Algorithms.isEmpty(wpt.desc)) {
final View row = buildRow(view, R.drawable.ic_action_note_dark, null, wpt.desc, 0, false, null, true, 10, false, null, false); String phoneToken = getDescriptionToken(wpt.desc, KEY_PHONE, PHONE_REGEX);
String emailToken = getDescriptionToken(wpt.desc, KEY_EMAIL, EMAIL_REGEX);
final ArrayList<String> phones = findAllElementsInLine(phoneToken, PHONE_REGEX);
final ArrayList<String> emails = findAllElementsInLine(emailToken, EMAIL_REGEX);
final String desc = deleteAllElementsOccurrence(wpt.desc, phoneToken, emailToken);
if (!Algorithms.isEmpty(desc)) {
final View row = buildRow(view, R.drawable.ic_action_note_dark, null, desc, 0, false, null, true, 10, false, null, false);
//todo maybe delete
row.setOnClickListener(new View.OnClickListener() { row.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
POIMapLayer.showDescriptionDialog(row.getContext(), app, wpt.desc, POIMapLayer.showDescriptionDialog(row.getContext(), app, desc,
row.getResources().getString(R.string.shared_string_description)); row.getResources().getString(R.string.shared_string_description));
} }
}); });
} }
if (phones != null) {
String phonesCommaLine = prepareCommaLine(phones);
if (!Algorithms.isEmpty(phonesCommaLine)) {
buildRow(view, R.drawable.ic_action_call_dark,
null, phonesCommaLine, 0,
false, null, false, 0, false, true, false, null, false);
}
}
if (!Algorithms.isEmpty(wpt.link)) {
buildRow(view, R.drawable.ic_world_globe_dark,
null, wpt.link, 0,
false, null, false, 0, true, null, false);
}
if (emails != null) {
String emailsCommaLine = prepareCommaLine(emails);
if (!Algorithms.isEmpty(emailsCommaLine)) {
buildRow(view, R.drawable.ic_action_message,
null, emailsCommaLine, 0,
false, null, false, 0, false, false, true, null, false);
}
}
if (!Algorithms.isEmpty(wpt.comment)) { if (!Algorithms.isEmpty(wpt.comment)) {
final View rowc = buildRow(view, R.drawable.ic_action_note_dark, null, wpt.comment, 0, final View rowc = buildRow(view, R.drawable.ic_action_note_dark, null, wpt.comment, 0,
false, null, true, 10, false, null, false); false, null, true, 10, false, null, false);
@ -96,6 +135,95 @@ public class WptPtMenuBuilder extends MenuBuilder {
buildPlainMenuItems(view); buildPlainMenuItems(view);
} }
//todo extract somewhere / improve algorithm
private String getDescriptionToken(String text, String key, String ... allowedElementsRegEx) {
final String END_TOKEN_REGEX = "( +)?[.,]";
final String SPACE_REGEX = "\\s+";
if (!Algorithms.isEmpty(text)) {
int startId, endId;
if (!Algorithms.isEmpty(key) && text.contains(key)) {
startId = text.indexOf(key);
endId = 0;
int finalIndex = text.indexOf(':', startId + key.length());
if (finalIndex > 0) {
text = text.substring(0, finalIndex);
}
for (String regEx : allowedElementsRegEx) {
ArrayList<String> items = findAllElementsInLine(text, regEx);
for (String item : items) {
int currentEnd = text.indexOf(item) + item.length();
if (endId < currentEnd) {
endId = currentEnd;
}
}
String endedText = text.substring(endId);
if (endedText.startsWith(SPACE_REGEX)) {
endId += findAllElementsInLine(endedText, SPACE_REGEX).get(0).length();
}
}
return text.substring(startId, endId);
}
}
return null;
}
//todo extract somewhere, maybe to Algorithms
private ArrayList<String> findAllElementsInLine(String text, String ... regExArgs) {
ArrayList<String> foundItems = new ArrayList<>();
if (!Algorithms.isEmpty(text)) {
for (String regEx : regExArgs) {
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(text);
while (m.find()) {
String item = m.group().trim();
if (!Algorithms.isEmpty(item)) {
foundItems.add(item);
}
}
}
return foundItems;
}
return null;
}
//todo extract somewhere, maybe to Algorithms / improve algorithm
private String deleteAllElementsOccurrence(String text, String ... args) {
if (text != null) {
for (String s : args) {
if (s != null) {
text = text.replace(s, "");
}
}
while (text.startsWith(".") && text.length() > 1) {
text = text.substring(1);
text = text.trim();
}
text = text.trim();
}
return text;
}
//todo extract somewhere, maybe to Algorithms
private String prepareCommaLine(List<String> items) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < items.size(); i++) {
String item = items.get(i);
if (!Algorithms.isEmpty(item)) {
sb.append(item);
if (i < items.size() - 1) {
sb.append(", ");
}
}
}
return sb.toString();
}
private void buildWaypointsView(View view) { private void buildWaypointsView(View view) {
GpxSelectionHelper gpxSelectionHelper = app.getSelectedGpxHelper(); GpxSelectionHelper gpxSelectionHelper = app.getSelectedGpxHelper();
@ -138,24 +266,32 @@ public class WptPtMenuBuilder extends MenuBuilder {
LinearLayout view = (LinearLayout) buildCollapsableContentView(context, collapsed, true); LinearLayout view = (LinearLayout) buildCollapsableContentView(context, collapsed, true);
List<WptPt> points = gpxFile.getPoints(); List<WptPt> points = gpxFile.getPoints();
for (int i = 0; i < points.size() && i < 10; i++) { String selectedCategory = selectedPoint != null && selectedPoint.category != null ? selectedPoint.category : "";
final WptPt point = points.get(i); int showedCount = 0;
boolean selected = selectedPoint != null && selectedPoint.equals(point); for (final WptPt point : points) {
TextViewEx button = buildButtonInCollapsableView(context, selected, false); String currentCategory = point != null ? point.category : null;
button.setText(point.name); if (selectedCategory.equals(currentCategory)) {
showedCount++;
boolean selected = selectedPoint != null && selectedPoint.equals(point);
TextViewEx button = buildButtonInCollapsableView(context, selected, false);
button.setText(point.name);
if (!selected) { if (!selected) {
button.setOnClickListener(new View.OnClickListener() { button.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LatLon latLon = new LatLon(point.getLatitude(), point.getLongitude()); LatLon latLon = new LatLon(point.getLatitude(), point.getLongitude());
PointDescription pointDescription = new PointDescription(PointDescription.POINT_TYPE_WPT, point.name); PointDescription pointDescription = new PointDescription(PointDescription.POINT_TYPE_WPT, point.name);
mapActivity.getContextMenu().setCenterMarker(true); mapActivity.getContextMenu().setCenterMarker(true);
mapActivity.getContextMenu().show(latLon, pointDescription, point); mapActivity.getContextMenu().show(latLon, pointDescription, point);
} }
}); });
}
view.addView(button);
}
if (showedCount >= 10) {
break;
} }
view.addView(button);
} }
if (points.size() > 10) { if (points.size() > 10) {

View file

@ -1,9 +1,12 @@
package net.osmand.plus.mapcontextmenu.controllers; package net.osmand.plus.mapcontextmenu.controllers;
import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import net.osmand.GPXUtilities;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
@ -11,11 +14,14 @@ import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.MapMarkersHelper; import net.osmand.plus.MapMarkersHelper;
import net.osmand.plus.MapMarkersHelper.MapMarker; import net.osmand.plus.MapMarkersHelper.MapMarker;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.FavoriteImageDrawable; import net.osmand.plus.base.FavoriteImageDrawable;
import net.osmand.plus.mapcontextmenu.MenuController; import net.osmand.plus.mapcontextmenu.MenuController;
import net.osmand.plus.mapcontextmenu.builders.WptPtMenuBuilder; import net.osmand.plus.mapcontextmenu.builders.WptPtMenuBuilder;
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import java.io.File; import java.io.File;
@ -25,7 +31,7 @@ public class WptPtMenuController extends MenuController {
private WptPt wpt; private WptPt wpt;
private MapMarker mapMarker; private MapMarker mapMarker;
public WptPtMenuController(@NonNull MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull WptPt wpt) { public WptPtMenuController(@NonNull MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull final WptPt wpt) {
super(new WptPtMenuBuilder(mapActivity, wpt), pointDescription, mapActivity); super(new WptPtMenuBuilder(mapActivity, wpt), pointDescription, mapActivity);
this.wpt = wpt; this.wpt = wpt;
@ -33,13 +39,38 @@ public class WptPtMenuController extends MenuController {
mapMarker = markersHelper.getMapMarker(wpt); mapMarker = markersHelper.getMapMarker(wpt);
if (mapMarker == null) { if (mapMarker == null) {
mapMarker = markersHelper.getMapMarker(new LatLon(wpt.lat, wpt.lon)); mapMarker = markersHelper.getMapMarker(new LatLon(wpt.lat, wpt.lon));
} } else {
if (mapMarker != null) {
MapMarkerMenuController markerMenuController = MapMarkerMenuController markerMenuController =
new MapMarkerMenuController(mapActivity, mapMarker.getPointDescription(mapActivity), mapMarker); new MapMarkerMenuController(mapActivity, mapMarker.getPointDescription(mapActivity), mapMarker);
leftTitleButtonController = markerMenuController.getLeftTitleButtonController(); leftTitleButtonController = markerMenuController.getLeftTitleButtonController();
rightTitleButtonController = markerMenuController.getRightTitleButtonController(); rightTitleButtonController = markerMenuController.getRightTitleButtonController();
} }
//todo extract / simplify
final OsmandApplication app = mapActivity.getMyApplication();
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedGPXFile(wpt);
GPXUtilities.GPXFile gpxFile = selectedGpxFile != null ? selectedGpxFile.getGpxFile() : null;
GPXUtilities.Metadata metadata = gpxFile != null ? gpxFile.metadata : null;
final TravelArticle article = metadata != null ? getTravelArticle(metadata) : null;
if (article != null) {
leftTitleButtonController = new TitleButtonController() {
@Override
public void buttonPressed() {
WikivoyageArticleDialogFragment.showInstance(app, getMapActivity().getSupportFragmentManager(), article.getTripId(), article.getLang());
}
};
leftTitleButtonController.caption = mapActivity.getString(R.string.context_menu_read_article);
leftTitleButtonController.leftIconId = R.drawable.ic_action_read_text;
}
}
//todo extract somewhere, maybe to TravelDbHelper
private TravelArticle getTravelArticle(@NonNull GPXUtilities.Metadata metadata) {
String title = metadata.getArticleTitle();
String lang = metadata.getArticleLang();
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) {
return getMapActivity().getMyApplication().getTravelDbHelper().getArticle(title, lang);
}
return null;
} }
@Override @Override