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.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
@ -14,6 +15,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.view.ContextThemeWrapper;
import android.text.ClipboardManager;
import android.text.SpannableStringBuilder;
@ -500,9 +502,23 @@ public class MenuBuilder {
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,
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()) {
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);
if (isUrl) {
if (isUrl || isNumber || isEmail) {
textView.setTextColor(linkTextColor);
} else if (needLinks) {
Linkify.addLinks(textView, Linkify.ALL);
@ -664,6 +680,46 @@ public class MenuBuilder {
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);

View file

@ -26,10 +26,19 @@ import net.osmand.util.Algorithms;
import java.io.File;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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;
@ -72,16 +81,46 @@ public class WptPtMenuBuilder extends MenuBuilder {
null, Algorithms.capitalizeFirstLetterAndLowercase(app.getString(R.string.plugin_distance_point_hdop)) + ": " + (int)wpt.hdop, 0,
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() {
@Override
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));
}
});
}
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)) {
final View rowc = buildRow(view, R.drawable.ic_action_note_dark, null, wpt.comment, 0,
false, null, true, 10, false, null, false);
@ -96,6 +135,95 @@ public class WptPtMenuBuilder extends MenuBuilder {
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) {
GpxSelectionHelper gpxSelectionHelper = app.getSelectedGpxHelper();
@ -138,24 +266,32 @@ public class WptPtMenuBuilder extends MenuBuilder {
LinearLayout view = (LinearLayout) buildCollapsableContentView(context, collapsed, true);
List<WptPt> points = gpxFile.getPoints();
for (int i = 0; i < points.size() && i < 10; i++) {
final WptPt point = points.get(i);
boolean selected = selectedPoint != null && selectedPoint.equals(point);
TextViewEx button = buildButtonInCollapsableView(context, selected, false);
button.setText(point.name);
String selectedCategory = selectedPoint != null && selectedPoint.category != null ? selectedPoint.category : "";
int showedCount = 0;
for (final WptPt point : points) {
String currentCategory = point != null ? point.category : null;
if (selectedCategory.equals(currentCategory)) {
showedCount++;
boolean selected = selectedPoint != null && selectedPoint.equals(point);
TextViewEx button = buildButtonInCollapsableView(context, selected, false);
button.setText(point.name);
if (!selected) {
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LatLon latLon = new LatLon(point.getLatitude(), point.getLongitude());
PointDescription pointDescription = new PointDescription(PointDescription.POINT_TYPE_WPT, point.name);
mapActivity.getContextMenu().setCenterMarker(true);
mapActivity.getContextMenu().show(latLon, pointDescription, point);
}
});
if (!selected) {
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LatLon latLon = new LatLon(point.getLatitude(), point.getLongitude());
PointDescription pointDescription = new PointDescription(PointDescription.POINT_TYPE_WPT, point.name);
mapActivity.getContextMenu().setCenterMarker(true);
mapActivity.getContextMenu().show(latLon, pointDescription, point);
}
});
}
view.addView(button);
}
if (showedCount >= 10) {
break;
}
view.addView(button);
}
if (points.size() > 10) {

View file

@ -1,9 +1,12 @@
package net.osmand.plus.mapcontextmenu.controllers;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import net.osmand.GPXUtilities;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.GPXUtilities.WptPt;
@ -11,11 +14,14 @@ import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.MapMarkersHelper;
import net.osmand.plus.MapMarkersHelper.MapMarker;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.FavoriteImageDrawable;
import net.osmand.plus.mapcontextmenu.MenuController;
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 java.io.File;
@ -25,7 +31,7 @@ public class WptPtMenuController extends MenuController {
private WptPt wpt;
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);
this.wpt = wpt;
@ -33,13 +39,38 @@ public class WptPtMenuController extends MenuController {
mapMarker = markersHelper.getMapMarker(wpt);
if (mapMarker == null) {
mapMarker = markersHelper.getMapMarker(new LatLon(wpt.lat, wpt.lon));
}
if (mapMarker != null) {
} else {
MapMarkerMenuController markerMenuController =
new MapMarkerMenuController(mapActivity, mapMarker.getPointDescription(mapActivity), mapMarker);
leftTitleButtonController = markerMenuController.getLeftTitleButtonController();
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