This commit is contained in:
Dmitriy Prodchenko 2018-01-25 19:14:49 +02:00
commit 81b2fcee4d
18 changed files with 788 additions and 612 deletions

View file

@ -8,9 +8,11 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import gnu.trove.list.array.TIntArrayList;
@ -27,6 +29,7 @@ public class OpeningHoursParser {
private static final String[] localDaysStr;
private static final String[] monthsStr;
private static final String[] localMothsStr;
private static final Map<String, String> additionalStrings = new HashMap<>();
private static final int LOW_TIME_LIMIT = 120;
private static final int WITHOUT_TIME_LIMIT = -1;
@ -39,6 +42,23 @@ public class OpeningHoursParser {
dateFormatSymbols = DateFormatSymbols.getInstance();
localMothsStr = dateFormatSymbols.getShortMonths();
localDaysStr = getTwoLettersStringArray(dateFormatSymbols.getShortWeekdays());
additionalStrings.put("off", "off");
additionalStrings.put("is_open", "Open");
additionalStrings.put("is_open_24_7", "Open 24/7");
additionalStrings.put("will_open_at", "Will open at");
additionalStrings.put("open_from", "Open from");
additionalStrings.put("will_close_at", "Will close at");
additionalStrings.put("open_till", "Open till");
additionalStrings.put("will_open_tomorrow_at", "Will open tomorrow at");
additionalStrings.put("will_open_on", "Will open on");
}
/**
* Set additional localized strings like "off", etc.
*/
public static void setAdditionalString(String key, String value) {
additionalStrings.put(key, value);
}
/**
@ -89,11 +109,61 @@ public class OpeningHoursParser {
*/
public static class OpeningHours implements Serializable {
public static final int ALL_SEQUENCES = -1;
/**
* list of the different rules
*/
private ArrayList<OpeningHoursRule> rules;
private String original;
private int sequenceCount;
public static class Info {
private boolean opened;
private boolean opened24_7;
private String openingTime;
private String nearToOpeningTime;
private String closingTime;
private String nearToClosingTime;
private String openingTomorrow;
private String openingDay;
private String ruleString;
public boolean isOpened() {
return opened;
}
public boolean isOpened24_7() {
return opened24_7;
}
public String getInfo() {
if (isOpened24_7()) {
if (!Algorithms.isEmpty(ruleString)) {
return additionalStrings.get("is_open") + " " + ruleString;
} else {
return additionalStrings.get("is_open_24_7");
}
} else if (!Algorithms.isEmpty(nearToOpeningTime)) {
return additionalStrings.get("will_open_at") + " " + nearToOpeningTime;
} else if (!Algorithms.isEmpty(openingTime)) {
return additionalStrings.get("open_from") + " " + openingTime;
} else if (!Algorithms.isEmpty(nearToClosingTime)) {
return additionalStrings.get("will_close_at") + " " + nearToClosingTime;
} else if (!Algorithms.isEmpty(closingTime)) {
return additionalStrings.get("open_till") + " " + closingTime;
} else if (!Algorithms.isEmpty(openingTomorrow)) {
return additionalStrings.get("will_open_tomorrow_at") + " " + openingTomorrow;
} else if (!Algorithms.isEmpty(openingDay)) {
return additionalStrings.get("will_open_on") + " " + openingDay + ".";
} else if (!Algorithms.isEmpty(ruleString)) {
return ruleString;
} else {
return "";
}
}
}
/**
* Constructor
@ -111,6 +181,45 @@ public class OpeningHoursParser {
rules = new ArrayList<OpeningHoursRule>();
}
public List<Info> getInfo() {
return getInfo(Calendar.getInstance());
}
public List<Info> getInfo(Calendar cal) {
List<Info> res = new ArrayList<>();
for (int i = 0; i < sequenceCount; i++) {
Info info = getInfo(cal, i);
res.add(info);
}
return res.isEmpty() ? null : res;
}
public Info getCombinedInfo() {
return getCombinedInfo(Calendar.getInstance());
}
public Info getCombinedInfo(Calendar cal) {
return getInfo(cal, ALL_SEQUENCES);
}
private Info getInfo(Calendar cal, int sequenceIndex) {
Info info = new Info();
boolean opened = isOpenedForTimeV2(cal, sequenceIndex);
info.opened = opened;
info.ruleString = getCurrentRuleTime(cal, sequenceIndex);
if (opened) {
info.opened24_7 = isOpened24_7(sequenceIndex);
info.closingTime = getClosingTime(cal, sequenceIndex);
info.nearToClosingTime = getNearToClosingTime(cal, sequenceIndex);
} else {
info.openingTime = getOpeningTime(cal, sequenceIndex);
info.nearToOpeningTime = getNearToOpeningTime(cal, sequenceIndex);
info.openingTomorrow = getOpeningTomorrow(cal, sequenceIndex);
info.openingDay = getOpeningDay(cal, sequenceIndex);
}
return info;
}
/**
* add a rule to the opening hours
*
@ -125,10 +234,18 @@ public class OpeningHoursParser {
*
* @param rules to add
*/
public void addRules(List<OpeningHoursRule> rules) {
public void addRules(List<? extends OpeningHoursRule> rules) {
this.rules.addAll(rules);
}
public int getSequenceCount() {
return sequenceCount;
}
public void setSequenceCount(int sequenceCount) {
this.sequenceCount = sequenceCount;
}
/**
* return the list of rules
*
@ -137,32 +254,47 @@ public class OpeningHoursParser {
public ArrayList<OpeningHoursRule> getRules() {
return rules;
}
public ArrayList<OpeningHoursRule> getRules(int sequenceIndex) {
if (sequenceIndex == ALL_SEQUENCES) {
return rules;
} else {
ArrayList<OpeningHoursRule> sequenceRules = new ArrayList<>();
for (OpeningHoursRule r : rules) {
if (r.getSequenceIndex() == sequenceIndex) {
sequenceRules.add(r);
}
}
return sequenceRules;
}
}
/**
* check if the feature is opened at time "cal"
*
* @param cal the time to check
* @return true if feature is open
*/
public boolean isOpenedForTimeV2(Calendar cal) {
public boolean isOpenedForTimeV2(Calendar cal, int sequenceIndex) {
// make exception for overlapping times i.e.
// (1) Mo 14:00-16:00; Tu off
// (2) Mo 14:00-02:00; Tu off
// in (2) we need to check first rule even though it is against specification
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
boolean overlap = false;
for(int i = rules.size() - 1; i >= 0 ; i--) {
for (int i = rules.size() - 1; i >= 0 ; i--) {
OpeningHoursRule r = rules.get(i);
if(r.hasOverlapTimes()) {
if (r.hasOverlapTimes()) {
overlap = true;
break;
}
}
// start from the most specific rule
for(int i = rules.size() - 1; i >= 0 ; i--) {
for (int i = rules.size() - 1; i >= 0 ; i--) {
OpeningHoursRule r = rules.get(i);
if(r.contains(cal)) {
if (r.contains(cal)) {
boolean open = r.isOpenedForTime(cal);
if(!open && overlap ) {
if (!open && overlap ) {
continue;
} else {
return open;
@ -179,12 +311,24 @@ public class OpeningHoursParser {
* @return true if feature is open
*/
public boolean isOpenedForTime(Calendar cal) {
return isOpenedForTimeV2(cal, ALL_SEQUENCES);
}
/**
* check if the feature is opened at time "cal"
*
* @param cal the time to check
* @param sequenceIndex the sequence index to check
* @return true if feature is open
*/
public boolean isOpenedForTime(Calendar cal, int sequenceIndex) {
/*
* first check for rules that contain the current day
* afterwards check for rules that contain the previous
* day with overlapping times (times after midnight)
*/
boolean isOpenDay = false;
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
for (OpeningHoursRule r : rules) {
if (r.containsDay(cal) && r.containsMonth(cal)) {
isOpenDay = r.isOpenedForTime(cal, false);
@ -199,33 +343,48 @@ public class OpeningHoursParser {
return isOpenDay || isOpenPrevious;
}
public boolean isOpened24_7() {
public boolean isOpened24_7(int sequenceIndex) {
boolean opened24_7 = false;
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
for (OpeningHoursRule r : rules) {
opened24_7 = r.isOpened24_7();
}
return opened24_7;
}
public String getNearToOpeningTime(Calendar cal) {
return getTime(cal, LOW_TIME_LIMIT, true);
public String getNearToOpeningTime(Calendar cal, int sequenceIndex) {
return getTime(cal, LOW_TIME_LIMIT, true, sequenceIndex);
}
public String getOpeningTime(Calendar cal) {
return getTime(cal, CURRENT_DAY_TIME_LIMIT, true);
public String getOpeningTime(Calendar cal, int sequenceIndex) {
return getTime(cal, CURRENT_DAY_TIME_LIMIT, true, sequenceIndex);
}
public String getNearToClosingTime(Calendar cal) {
return getTime(cal, LOW_TIME_LIMIT, false);
public String getNearToClosingTime(Calendar cal, int sequenceIndex) {
return getTime(cal, LOW_TIME_LIMIT, false, sequenceIndex);
}
public String getClosingTime(Calendar cal) {
return getTime(cal, WITHOUT_TIME_LIMIT, false);
public String getClosingTime(Calendar cal, int sequenceIndex) {
return getTime(cal, WITHOUT_TIME_LIMIT, false, sequenceIndex);
}
public String getOpeningDay(Calendar calendar) {
public String getOpeningTomorrow(Calendar calendar, int sequenceIndex) {
Calendar cal = (Calendar) calendar.clone();
String openingTime = "";
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
cal.add(Calendar.DAY_OF_MONTH, 1);
for (OpeningHoursRule r : rules) {
if (r.containsDay(cal) && r.containsMonth(cal)) {
openingTime = r.getTime(cal, false, WITHOUT_TIME_LIMIT, true);
}
}
return openingTime;
}
public String getOpeningDay(Calendar calendar, int sequenceIndex) {
Calendar cal = (Calendar) calendar.clone();
String openingTime = "";
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
for (int i = 0; i < 7; i++) {
cal.add(Calendar.DAY_OF_MONTH, 1);
for (OpeningHoursRule r : rules) {
@ -241,16 +400,17 @@ public class OpeningHoursParser {
return openingTime;
}
private String getTime(Calendar cal, int limit, boolean opening) {
String time = getTimeDay(cal, limit, opening);
private String getTime(Calendar cal, int limit, boolean opening, int sequenceIndex) {
String time = getTimeDay(cal, limit, opening, sequenceIndex);
if (Algorithms.isEmpty(time)) {
time = getTimeAnotherDay(cal, limit, opening);
time = getTimeAnotherDay(cal, limit, opening, sequenceIndex);
}
return time;
}
private String getTimeDay(Calendar cal, int limit, boolean opening) {
private String getTimeDay(Calendar cal, int limit, boolean opening, int sequenceIndex) {
String atTime = "";
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
for (OpeningHoursRule r : rules) {
if (r.containsDay(cal) && r.containsMonth(cal)) {
atTime = r.getTime(cal, false, limit, opening);
@ -259,8 +419,9 @@ public class OpeningHoursParser {
return atTime;
}
private String getTimeAnotherDay(Calendar cal, int limit, boolean opening) {
private String getTimeAnotherDay(Calendar cal, int limit, boolean opening, int sequenceIndex) {
String atTime = "";
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
for (OpeningHoursRule r : rules) {
if (((opening && r.containsPreviousDay(cal)) || (!opening && r.containsNextDay(cal))) && r.containsMonth(cal)) {
atTime = r.getTime(cal, true, limit, opening);
@ -270,10 +431,15 @@ public class OpeningHoursParser {
}
public String getCurrentRuleTime(Calendar cal) {
return getCurrentRuleTime(cal, ALL_SEQUENCES);
}
public String getCurrentRuleTime(Calendar cal, int sequenceIndex) {
// make exception for overlapping times i.e.
// (1) Mo 14:00-16:00; Tu off
// (2) Mo 14:00-02:00; Tu off
// in (2) we need to check first rule even though it is against specification
ArrayList<OpeningHoursRule> rules = getRules(sequenceIndex);
String ruleClosed = null;
boolean overlap = false;
for (int i = rules.size() - 1; i >= 0; i--) {
@ -428,6 +594,7 @@ public class OpeningHoursParser {
*/
public boolean contains(Calendar cal);
public int getSequenceIndex();
public String toRuleString();
@ -475,6 +642,26 @@ public class OpeningHoursParser {
*/
private boolean off = false;
/**
* Aadditional information or limitation.
* https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification#explain:comment
*/
private String comment;
private int sequenceIndex;
public BasicOpeningHourRule() {
this.sequenceIndex = 0;
}
public BasicOpeningHourRule(int sequenceIndex) {
this.sequenceIndex = sequenceIndex;
}
public int getSequenceIndex() {
return sequenceIndex;
}
/**
* return an array representing the days of the rule
*
@ -511,7 +698,14 @@ public class OpeningHoursParser {
public boolean appliesToSchoolHolidays() {
return schoolHoliday;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
/**
* set a single start time, erase all previously added start times
@ -786,27 +980,45 @@ public class OpeningHoursParser {
appendDaysString(b, dayNames);
// Time
if (startTimes == null || startTimes.size() == 0) {
b.append("off");
if (isOpened24_7()) {
b.setLength(0);
b.append("24/7 ");
}
if (off) {
b.append(additionalStrings.get("off"));
}
} else {
if (isOpened24_7()) {
return "24/7";
}
for (int i = 0; i < startTimes.size(); i++) {
int startTime = startTimes.get(i);
int endTime = endTimes.get(i);
if(i > 0) {
b.append(", ");
b.setLength(0);
b.append("24/7");
} else {
for (int i = 0; i < startTimes.size(); i++) {
int startTime = startTimes.get(i);
int endTime = endTimes.get(i);
if (i > 0) {
b.append(", ");
}
int stHour = startTime / 60;
int stTime = startTime - stHour * 60;
int enHour = endTime / 60;
int enTime = endTime - enHour * 60;
formatTime(stHour, stTime, b);
b.append("-"); //$NON-NLS-1$
formatTime(enHour, enTime, b);
}
if (off) {
b.append(" ").append(additionalStrings.get("off"));
}
int stHour = startTime / 60;
int stTime = startTime - stHour * 60;
int enHour = endTime / 60;
int enTime = endTime - enHour * 60;
formatTime(stHour, stTime, b);
b.append("-"); //$NON-NLS-1$
formatTime(enHour, enTime, b);
}
if(off) {
b.append(" off");
}
if (!Algorithms.isEmpty(comment)) {
if (b.length() > 0) {
if (b.charAt(b.length() - 1) != ' ') {
b.append(" ");
}
b.append("(").append(comment).append(")");
} else {
b.append(comment);
}
}
return b.toString();
@ -853,13 +1065,17 @@ public class OpeningHoursParser {
}
}
if (opened24_7 && startTimes != null && startTimes.size() > 0) {
for (int i = 0; i < startTimes.size(); i++) {
int startTime = startTimes.get(i);
int endTime = endTimes.get(i);
if (startTime == 0 && endTime / 60 == 24) {
return true;
if (opened24_7) {
if (startTimes != null && startTimes.size() > 0) {
for (int i = 0; i < startTimes.size(); i++) {
int startTime = startTimes.get(i);
int endTime = endTimes.get(i);
if (startTime == 0 && endTime / 60 == 24) {
return true;
}
}
} else {
return true;
}
}
return false;
@ -918,7 +1134,11 @@ public class OpeningHoursParser {
}
}
}
return sb.toString();
String res = sb.toString();
if (res.length() > 0 && !Algorithms.isEmpty(comment)) {
res += " (" + comment + ")";
}
return res;
}
@Override
@ -1015,7 +1235,7 @@ public class OpeningHoursParser {
int c = calculate(cal);
return c != 0;
}
@Override
public boolean hasOverlapTimes() {
for (int i = 0; i < startTimes.size(); i++) {
@ -1062,14 +1282,16 @@ public class OpeningHoursParser {
}
}
}
if (thisDay && (startTimes == null || startTimes.isEmpty() || !off)) {
if (thisDay && (startTimes == null || startTimes.isEmpty()) && !off) {
return 1;
} else if (thisDay && (startTimes == null || startTimes.isEmpty() || !off)) {
return -1;
}
return 0;
}
}
public static class UnparseableRule implements OpeningHoursParser.OpeningHoursRule {
public static class UnparseableRule implements OpeningHoursRule {
private String ruleString;
public UnparseableRule(String ruleString) {
@ -1140,6 +1362,11 @@ public class OpeningHoursParser {
public boolean contains(Calendar cal) {
return false;
}
@Override
public int getSequenceIndex() {
return 0;
}
}
private enum TokenType {
@ -1185,8 +1412,22 @@ public class OpeningHoursParser {
}
}
public static void parseRuleV2(String r, List<OpeningHoursParser.OpeningHoursRule> rules) {
r = r.toLowerCase();
public static void parseRuleV2(String r, int sequenceIndex, List<OpeningHoursRule> rules) {
String comment = null;
int q1Index = r.indexOf('"');
if (q1Index >= 0) {
int q2Index = r.indexOf('"', q1Index + 1);
if (q2Index >= 0) {
comment = r.substring(q1Index + 1, q2Index);
String a = r.substring(0, q1Index);
String b = "";
if (r.length() > q2Index + 1) {
b = r.substring(q2Index + 1);
}
r = a + b;
}
}
r = r.toLowerCase().trim();
final String[] daysStr = new String[]{"mo", "tu", "we", "th", "fr", "sa", "su"};
final String[] monthsStr = new String[]{"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
@ -1198,7 +1439,8 @@ public class OpeningHoursParser {
r = r.replace(')', ' ');
String localRuleString = r.replaceAll("sunset", sunset).replaceAll("sunrise", sunrise)
.replaceAll("\\+", "-" + endOfDay);
BasicOpeningHourRule basic = new BasicOpeningHourRule();
BasicOpeningHourRule basic = new BasicOpeningHourRule(sequenceIndex);
basic.setComment(comment);
boolean[] days = basic.getDays();
boolean[] months = basic.getMonths();
boolean[] dayMonths = basic.getDayMonths();
@ -1211,32 +1453,32 @@ public class OpeningHoursParser {
}
List<Token> tokens = new ArrayList<>();
int startWord = 0;
for(int i = 0; i <= localRuleString.length(); i++) {
for (int i = 0; i <= localRuleString.length(); i++) {
char ch = i == localRuleString.length() ? ' ' : localRuleString.charAt(i);
boolean delimiter = false;
Token del = null;
if(Character.isWhitespace(ch)) {
if (Character.isWhitespace(ch)) {
delimiter = true;
} else if(ch == ':') {
} else if (ch == ':') {
del = new Token(TokenType.TOKEN_COLON, ":");
} else if(ch == '-') {
} else if (ch == '-') {
del = new Token(TokenType.TOKEN_DASH, "-");
} else if(ch == ',') {
} else if (ch == ',') {
del = new Token(TokenType.TOKEN_COMMA, ",");
}
if(delimiter || del != null) {
if (delimiter || del != null) {
String wrd = localRuleString.substring(startWord, i).trim();
if(wrd.length() > 0) {
tokens.add(new Token(TokenType.TOKEN_UNKNOWN, wrd));
}
startWord = i + 1;
if(del != null) {
if (del != null) {
tokens.add(del);
}
}
}
// recognize day of week
for(Token t : tokens) {
for (Token t : tokens) {
if(t.type == TokenType.TOKEN_UNKNOWN) {
findInArray(t, daysStr, TokenType.TOKEN_DAY_WEEK);
}
@ -1360,7 +1602,9 @@ public class OpeningHoursParser {
currentParse = t.type;
}
} else if (t.type.ord() < currentParse.ord() && indexP == 0 && tokens.size() > i) {
buildRule(new BasicOpeningHourRule(), tokens.subList(i, tokens.size()), rules);
BasicOpeningHourRule newRule = new BasicOpeningHourRule(basic.getSequenceIndex());
newRule.setComment(basic.getComment());
buildRule(newRule, tokens.subList(i, tokens.size()), rules);
tokens = tokens.subList(0, i + 1);
} else if (t.type == TokenType.TOKEN_COMMA) {
if (tokens.size() > i + 1 && tokens.get(i + 1) != null && tokens.get(i + 1).type.ord() < currentParse.ord()) {
@ -1405,14 +1649,55 @@ public class OpeningHoursParser {
}
}
private static List<List<String>> splitSequences(String format) {
if (format == null) {
return null;
}
List<List<String>> res = new ArrayList<>();
String[] sequences = format.split("\\|\\|");
for (String seq : sequences) {
seq = seq.trim();
if (seq.length() == 0) {
continue;
}
List<String> rules = new ArrayList<>();
boolean comment = false;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < seq.length(); i++) {
char c = seq.charAt(i);
if (c == '"') {
comment = !comment;
sb.append(c);
} else if (c == ';' && !comment) {
if (sb.length() > 0) {
String s = sb.toString().trim();
if (s.length() > 0) {
rules.add(s);
}
sb.setLength(0);
}
} else {
sb.append(c);
}
}
if (sb.length() > 0) {
rules.add(sb.toString());
sb.setLength(0);
}
res.add(rules);
}
return res;
}
/**
* Parse an opening_hours string from OSM to an OpeningHours object which can be used to check
*
* @param r the string to parse
* @return BasicRule if the String is successfully parsed and UnparseableRule otherwise
*/
public static void parseRules(String r, List<OpeningHoursParser.OpeningHoursRule> rules) {
parseRuleV2(r, rules);
public static void parseRules(String r, int sequenceIndex, List<OpeningHoursRule> rules) {
parseRuleV2(r, sequenceIndex, rules);
}
/**
@ -1425,25 +1710,38 @@ public class OpeningHoursParser {
if (format == null) {
return null;
}
// split the OSM string in multiple rules
String[] rules = format.split(";"); //$NON-NLS-1$
// FIXME: What if the semicolon is inside a quoted string?
OpeningHours rs = new OpeningHours();
rs.setOriginal(format);
for (String r : rules) {
r = r.trim();
if (r.length() == 0) {
continue;
}
// check if valid
List<OpeningHoursParser.OpeningHoursRule> rList = new ArrayList<>();
parseRules(r, rList);
for (OpeningHoursParser.OpeningHoursRule rule : rList) {
if (rule instanceof BasicOpeningHourRule) {
rs.addRule(rule);
// split the OSM string in multiple rules
List<List<String>> sequences = splitSequences(format);
for (int i = 0; i < sequences.size(); i++) {
List<String> rules = sequences.get(i);
List<BasicOpeningHourRule> basicRules = new ArrayList<>();
for (String r : rules) {
// check if valid
List<OpeningHoursRule> rList = new ArrayList<>();
parseRules(r, i, rList);
for (OpeningHoursRule rule : rList) {
if (rule instanceof BasicOpeningHourRule) {
basicRules.add((BasicOpeningHourRule) rule);
}
}
}
String basicRuleComment = null;
for (BasicOpeningHourRule bRule : basicRules) {
if (!Algorithms.isEmpty(bRule.getComment())) {
basicRuleComment = bRule.getComment();
break;
}
}
if (!Algorithms.isEmpty(basicRuleComment)) {
for (BasicOpeningHourRule bRule : basicRules) {
bRule.setComment(basicRuleComment);
}
}
rs.addRules(basicRules);
}
rs.setSequenceCount(sequences.size());
return rs;
}
@ -1459,22 +1757,35 @@ public class OpeningHoursParser {
if (format == null) {
return null;
}
String[] rules = format.split(";"); //$NON-NLS-1$
OpeningHoursParser.OpeningHours rs = new OpeningHoursParser.OpeningHours();
rs.setOriginal(format);
for (String r : rules) {
r = r.trim();
if (r.length() == 0) {
continue;
List<List<String>> sequences = splitSequences(format);
for (int i = sequences.size() - 1; i >= 0; i--) {
List<String> rules = sequences.get(i);
for (String r : rules) {
r = r.trim();
if (r.length() == 0) {
continue;
}
// check if valid
List<OpeningHoursRule> rList = new ArrayList<>();
parseRules(r, i, rList);
rs.addRules(rList);
}
// check if valid
List<OpeningHoursParser.OpeningHoursRule> rList = new ArrayList<>();
parseRules(r, rList);
rs.addRules(rList);
}
rs.setSequenceCount(sequences.size());
return rs;
}
public static List<OpeningHours.Info> getInfo(String format) {
OpeningHours openingHours = OpeningHoursParser.parseOpenedHours(format);
if (openingHours == null) {
return null;
} else {
return openingHours.getInfo();
}
}
private static void formatTime(int h, int t, StringBuilder b) {
if (h < 10) {
b.append("0"); //$NON-NLS-1$
@ -1502,9 +1813,9 @@ public class OpeningHoursParser {
private static void testOpened(String time, OpeningHours hours, boolean expected) throws ParseException {
Calendar cal = Calendar.getInstance();
cal.setTime(new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.US).parse(time));
boolean calculated = hours.isOpenedForTimeV2(cal);
boolean calculated = hours.isOpenedForTimeV2(cal, OpeningHours.ALL_SEQUENCES);
System.out.printf(" %sok: Expected %s: %b = %b (rule %s)\n",
((calculated != expected) ? "NOT " : ""), time, expected, calculated, hours.getCurrentRuleTime(cal));
((calculated != expected) ? "NOT " : ""), time, expected, calculated, hours.getCurrentRuleTime(cal, OpeningHours.ALL_SEQUENCES));
if (calculated != expected) {
throw new IllegalArgumentException("BUG!!!");
}
@ -1521,50 +1832,49 @@ public class OpeningHoursParser {
* "Open till HH:mm" - close in 5 hours
* "Will close at HH:mm" - close in 2 hours
* "Will open on HH:mm (Mo,Tu,We,Th,Fr,Sa,Su)" - open in >5 hours
* "Will open tomorrow at HH:mm" - open in >5 hours tomorrow
* "Open 24/7" - open 24/7
*/
private static void testInfo(String time, OpeningHours hours, String expected) throws ParseException
{
testInfo(time, hours, expected, OpeningHours.ALL_SEQUENCES);
}
/**
* test if the calculated opening hours are what you expect
*
* @param time the time to test in the format "dd.MM.yyyy HH:mm"
* @param hours the OpeningHours object
* @param expected the expected string in format:
* "Open from HH:mm" - open in 5 hours
* "Will open at HH:mm" - open in 2 hours
* "Open till HH:mm" - close in 5 hours
* "Will close at HH:mm" - close in 2 hours
* "Will open on HH:mm (Mo,Tu,We,Th,Fr,Sa,Su)" - open in >5 hours
* "Will open tomorrow at HH:mm" - open in >5 hours tomorrow
* "Open 24/7" - open 24/7
* @param sequenceIndex sequence index of rules separated by ||
*/
private static void testInfo(String time, OpeningHours hours, String expected, int sequenceIndex) throws ParseException
{
Calendar cal = Calendar.getInstance();
cal.setTime(new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.US).parse(time));
boolean opened24_calc = false;
boolean openFrom_calc = false;
boolean willOpenAt_calc = false;
boolean openTill_calc = false;
boolean willCloseAt_calc = false;
boolean willOpenOn_calc = false;
boolean opened = hours.isOpenedForTimeV2(cal);
if (opened) {
opened24_calc = hours.isOpened24_7();
openTill_calc = !Algorithms.isEmpty(hours.getClosingTime(cal));
willCloseAt_calc = !Algorithms.isEmpty(hours.getNearToClosingTime(cal));
} else {
openFrom_calc = !Algorithms.isEmpty(hours.getOpeningTime(cal));
willOpenAt_calc = !Algorithms.isEmpty(hours.getNearToOpeningTime(cal));
willOpenOn_calc = !Algorithms.isEmpty(hours.getOpeningDay(cal));
}
String description = "Unknown";
if (opened24_calc) {
description = "Open 24/7";
} else if (willOpenAt_calc) {
description = "Will open at " + hours.getNearToOpeningTime(cal);
} else if (openFrom_calc) {
description = "Open from " + hours.getOpeningTime(cal);
} else if (willCloseAt_calc) {
description = "Will close at " + hours.getNearToClosingTime(cal);
} else if (openTill_calc) {
description = "Open till " + hours.getClosingTime(cal);
} else if (willOpenOn_calc) {
description = "Will open on " + hours.getOpeningDay(cal);
}
boolean result = expected.equalsIgnoreCase(description);
String description;
boolean result;
if (sequenceIndex == OpeningHours.ALL_SEQUENCES) {
OpeningHours.Info info = hours.getCombinedInfo(cal);
description = info.getInfo();
result = expected.equalsIgnoreCase(description);
} else {
List<OpeningHours.Info> infos = hours.getInfo(cal);
OpeningHours.Info info = infos.get(sequenceIndex);
description = info.getInfo();
result = expected.equalsIgnoreCase(description);
}
System.out.printf(" %sok: Expected %s (%s): %s (rule %s)\n",
(!result ? "NOT " : ""), time, expected, description, hours.getCurrentRuleTime(cal));
(!result ? "NOT " : ""), time, expected, description, hours.getCurrentRuleTime(cal, sequenceIndex));
if (!result)
throw new IllegalArgumentException("BUG!!!");
@ -1584,22 +1894,15 @@ public class OpeningHoursParser {
// 0. not supported MON DAY-MON DAY (only supported Feb 2-14 or Feb-Oct: 09:00-17:30)
// parseOpenedHours("Feb 16-Oct 15: 09:00-18:30; Oct 16-Nov 15: 09:00-17:30; Nov 16-Feb 15: 09:00-16:30");
// 1. not supported (,)
// hours = parseOpenedHours("Mo-Su 07:00-23:00, Fr 08:00-20:00");
// 2. not supported break properly
// parseOpenedHours("Sa-Su 10:00-17:00 || \"by appointment\"");
// comment is dropped
// 3. not properly supported
// 1. not properly supported
// hours = parseOpenedHours("Mo-Su (sunrise-00:30)-(sunset+00:30)");
// Test basic case
// test basic case
OpeningHours hours = parseOpenedHours("Mo-Fr 08:30-14:40"); //$NON-NLS-1$
System.out.println(hours);
testOpened("09.08.2012 11:00", hours, true);
testOpened("09.08.2012 16:00", hours, false);
hours = parseOpenedHours("mo-fr 07:00-19:00; sa 12:00-18:00");
System.out.println(hours);
String string = "Mo-Fr 11:30-15:00, 17:30-23:00; Sa, Su, PH 11:30-23:00";
hours = parseOpenedHours(string);
@ -1793,11 +2096,11 @@ public class OpeningHoursParser {
testInfo("15.01.2018 13:10", hours, "Will open at 14:00");
testInfo("15.01.2018 14:00", hours, "Open till 18:00");
testInfo("15.01.2018 16:00", hours, "Will close at 18:00");
testInfo("15.01.2018 18:10", hours, "Will open on 09:00 Tu");
testInfo("15.01.2018 18:10", hours, "Will open tomorrow at 09:00");
hours = parseOpenedHours("Mo-Sa 02:00-10:00; Th off");
System.out.println(hours);
testInfo("15.01.2018 23:00", hours, "Will open on 02:00 Tu");
testInfo("15.01.2018 23:00", hours, "Will open tomorrow at 02:00");
hours = parseOpenedHours("Mo-Sa 23:00-02:00; Th off");
System.out.println(hours);
@ -1808,10 +2111,10 @@ public class OpeningHoursParser {
hours = parseOpenedHours("Mo-Sa 08:30-17:00; Th off");
System.out.println(hours);
testInfo("17.01.2018 20:00", hours, "Will open on 08:30 Fr");
testInfo("18.01.2018 05:00", hours, "Will open on 08:30 Fr");
testInfo("17.01.2018 20:00", hours, "Will open on 08:30 Fr.");
testInfo("18.01.2018 05:00", hours, "Will open tomorrow at 08:30");
testInfo("20.01.2018 05:00", hours, "Open from 08:30");
testInfo("21.01.2018 05:00", hours, "Will open on 08:30 Mo");
testInfo("21.01.2018 05:00", hours, "Will open tomorrow at 08:30");
testInfo("22.01.2018 02:00", hours, "Open from 08:30");
testInfo("22.01.2018 04:00", hours, "Open from 08:30");
testInfo("22.01.2018 07:00", hours, "Will open at 08:30");
@ -1830,6 +2133,31 @@ public class OpeningHoursParser {
testOpened("19.01.2018 07:45", hours, false);
testOpened("19.01.2018 08:45", hours, true);
testOpened("19.01.2018 20:45", hours, false);
// test fallback case
hours = parseOpenedHours("07:00-01:00 open \"Restaurant\" || Mo 00:00-04:00,07:00-04:00; Tu-Th 07:00-04:00; Fr 07:00-24:00; Sa,Su 00:00-24:00 open \"McDrive\"");
System.out.println(hours);
testOpened("22.01.2018 00:30", hours, true);
testOpened("22.01.2018 08:00", hours, true);
testOpened("22.01.2018 03:30", hours, true);
testOpened("22.01.2018 05:00", hours, false);
testOpened("23.01.2018 05:00", hours, false);
testOpened("27.01.2018 05:00", hours, true);
testOpened("28.01.2018 05:00", hours, true);
testInfo("22.01.2018 05:00", hours, "Will open at 07:00 (Restaurant)", 0);
testInfo("26.01.2018 00:00", hours, "Will close at 01:00 (Restaurant)", 0);
testInfo("22.01.2018 05:00", hours, "Will open at 07:00 (McDrive)", 1);
testInfo("22.01.2018 00:00", hours, "Open till 04:00 (McDrive)", 1);
testInfo("22.01.2018 02:00", hours, "Will close at 04:00 (McDrive)", 1);
testInfo("27.01.2018 02:00", hours, "Open till 24:00 (McDrive)", 1);
hours = parseOpenedHours("07:00-03:00 open \"Restaurant\" || 24/7 open \"McDrive\"");
System.out.println(hours);
testOpened("22.01.2018 02:00", hours, true);
testOpened("22.01.2018 17:00", hours, true);
testInfo("22.01.2018 05:00", hours, "Will open at 07:00 (Restaurant)", 0);
testInfo("22.01.2018 04:00", hours, "Open 24/7 (McDrive)", 1);
}
}

View file

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/context_menu_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/context_menu_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
xmlns:osmand="http://schemas.android.com/apk/res-auto">
android:background="@android:color/transparent">
<LinearLayout
android:id="@+id/context_menu_main"
@ -18,47 +18,47 @@
android:id="@+id/context_menu_top_shadow_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:background="?attr/bg_map_context_menu"
android:clickable="true"
android:orientation="vertical">
<LinearLayout
android:id="@+id/context_menu_top_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default"
android:orientation="horizontal">
android:paddingRight="@dimen/context_menu_padding_margin_default">
<LinearLayout
android:layout_marginTop="@dimen/context_menu_first_line_top_margin"
android:layout_marginRight="@dimen/context_menu_padding_margin_default"
android:layout_marginEnd="@dimen/context_menu_padding_margin_default"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/context_menu_padding_margin_default"
android:layout_marginRight="@dimen/context_menu_padding_margin_default"
android:layout_marginTop="@dimen/context_menu_first_line_top_margin"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/context_menu_line1"
style="@style/TextAppearance.ContextMenuTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/search_address_building"
style="@style/TextAppearance.ContextMenuTitle"/>
android:text="@string/search_address_building"/>
<LinearLayout
android:layout_marginTop="@dimen/context_menu_subtitle_margin"
android:id="@+id/context_menu_line2_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/context_menu_subtitle_margin">
<TextView
android:id="@+id/context_menu_line2"
style="@style/TextAppearance.ContextMenuSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/amenity_type_finance"
style="@style/TextAppearance.ContextMenuSubtitle"/>
android:text="@string/amenity_type_finance"/>
</LinearLayout>
@ -80,40 +80,42 @@
android:columnWidth="@dimen/context_menu_transport_grid_item_width"
android:horizontalSpacing="@dimen/context_menu_transport_grid_spacing"
android:numColumns="auto_fit"
android:paddingBottom="@dimen/context_menu_transport_grid_spacing"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default"
android:paddingTop="@dimen/context_menu_transport_padding_top"
android:paddingBottom="@dimen/context_menu_transport_grid_spacing"
android:verticalSpacing="@dimen/context_menu_transport_grid_spacing"
android:visibility="gone"/>
<LinearLayout
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default"
android:layout_marginTop="@dimen/context_menu_padding_margin_tiny"
android:layout_marginBottom="@dimen/context_menu_direction_margin"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="@dimen/context_menu_sub_info_height"
android:orientation="horizontal">
android:layout_height= "wrap_content"
android:layout_marginBottom="@dimen/context_menu_direction_margin"
android:layout_marginTop="@dimen/context_menu_padding_margin_tiny"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default">
<net.osmand.plus.widgets.TextViewEx
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
android:layout_marginEnd="@dimen/context_menu_padding_margin_small"
tools:text="Museum"
android:id="@+id/context_menu_line3"
style="@style/TextAppearance.ContextMenuSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/context_menu_line3"
android:layout_marginEnd="@dimen/context_menu_padding_margin_small"
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
osmand:typeface="@string/font_roboto_medium"
style="@style/TextAppearance.ContextMenuSubtitle"/>
tools:text="Museum"/>
<TextView
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
android:layout_marginEnd="@dimen/context_menu_padding_margin_small"
tools:text="Closed till 10:00"
android:id="@+id/additional_info_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/context_menu_padding_margin_small"
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
tools:text="Closed till 10:00"/>
<LinearLayout
android:id="@+id/compass_layout"
@ -122,12 +124,12 @@
android:orientation="horizontal">
<ImageView
tools:src="@drawable/ic_direction_arrow"
android:id="@+id/direction"
android:layout_width="@dimen/directionIconSize"
android:layout_height="@dimen/directionIconSize"
android:layout_gravity="center_vertical"
android:layout_marginTop="1sp"
android:layout_gravity="center_vertical"/>
tools:src="@drawable/ic_direction_arrow"/>
<TextView
android:id="@+id/distance"
@ -136,9 +138,9 @@
android:layout_gravity="center_vertical"
android:layout_marginLeft="2dp"
android:layout_marginStart="2dp"
tools:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size"
tools:text="100500 km"/>
tools:text="100500 km"
tools:textColor="?android:textColorSecondary"/>
</LinearLayout>
@ -155,17 +157,16 @@
tools:visibility="visible">
<LinearLayout
android:id="@+id/title_button_view"
android:layout_width="0dp"
android:layout_height="@dimen/context_menu_controller_height"
android:layout_marginLeft="@dimen/context_menu_padding_margin_small"
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
android:id="@+id/title_button_view"
tools:background="?attr/ctx_menu_controller_bg"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="@dimen/context_menu_controller_height"
android:orientation="horizontal">
android:orientation="horizontal"
tools:background="?attr/ctx_menu_controller_bg">
<net.osmand.plus.widgets.TextViewEx
osmand:typeface="@string/font_roboto_medium"
android:id="@+id/title_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@ -173,10 +174,11 @@
android:gravity="center_vertical"
android:paddingLeft="@dimen/context_menu_button_padding_x"
android:paddingRight="@dimen/context_menu_button_padding_x"
android:textAllCaps="true"
android:text="@string/recording_context_menu_play"
tools:textColor="?attr/ctx_menu_controller_text_color"
android:textSize="@dimen/default_desc_text_size"/>
android:textAllCaps="true"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctx_menu_controller_text_color"/>
<TextView
android:id="@+id/title_button_right_text"
@ -190,18 +192,16 @@
</LinearLayout>
<LinearLayout
android:id="@+id/title_button_right_view"
android:layout_width="0dp"
android:layout_height="@dimen/context_menu_controller_height"
android:layout_marginLeft="@dimen/context_menu_padding_margin_small"
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
android:id="@+id/title_button_right_view"
tools:background="?attr/ctx_menu_controller_bg"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="@dimen/context_menu_controller_height"
android:orientation="horizontal">
android:orientation="horizontal"
tools:background="?attr/ctx_menu_controller_bg">
<net.osmand.plus.widgets.TextViewEx
osmand:typeface="@string/font_roboto_medium"
android:textAllCaps="true"
android:id="@+id/title_button_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@ -210,8 +210,10 @@
android:paddingLeft="@dimen/context_menu_button_padding_x"
android:paddingRight="@dimen/context_menu_button_padding_x"
android:text="@string/shared_string_delete"
tools:textColor="?attr/ctx_menu_controller_text_color"
android:textSize="@dimen/default_desc_text_size"/>
android:textAllCaps="true"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctx_menu_controller_text_color"/>
</LinearLayout>
@ -229,16 +231,14 @@
<LinearLayout
android:id="@+id/download_button_left_view"
tools:background="?attr/ctx_menu_controller_bg"
android:layout_width="0dp"
android:layout_height="@dimen/context_menu_controller_height"
android:layout_marginLeft="@dimen/context_menu_padding_margin_small"
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="@dimen/context_menu_controller_height">
tools:background="?attr/ctx_menu_controller_bg">
<net.osmand.plus.widgets.TextViewEx
osmand:typeface="@string/font_roboto_medium"
android:textAllCaps="true"
android:id="@+id/download_button_left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@ -247,23 +247,23 @@
android:paddingLeft="@dimen/context_menu_button_padding_x"
android:paddingRight="@dimen/context_menu_button_padding_x"
android:text="@string/shared_string_download"
tools:textColor="?attr/ctx_menu_controller_text_color"
android:textSize="@dimen/default_desc_text_size"/>
android:textAllCaps="true"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctx_menu_controller_text_color"/>
</LinearLayout>
<LinearLayout
android:id="@+id/download_button_right_view"
tools:background="?attr/ctx_menu_controller_bg"
android:layout_width="0dp"
android:layout_height="@dimen/context_menu_controller_height"
android:layout_marginLeft="@dimen/context_menu_padding_margin_small"
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="@dimen/context_menu_controller_height">
tools:background="?attr/ctx_menu_controller_bg">
<net.osmand.plus.widgets.TextViewEx
osmand:typeface="@string/font_roboto_medium"
android:textAllCaps="true"
android:id="@+id/download_button_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@ -272,8 +272,10 @@
android:paddingLeft="@dimen/context_menu_button_padding_x"
android:paddingRight="@dimen/context_menu_button_padding_x"
android:text="@string/shared_string_others"
tools:textColor="?attr/ctx_menu_controller_text_color"
android:textSize="@dimen/default_desc_text_size"/>
android:textAllCaps="true"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctx_menu_controller_text_color"/>
</LinearLayout>
@ -308,9 +310,9 @@
android:paddingRight="@dimen/context_menu_button_padding_x"
android:text="@string/shared_string_delete"
android:textAllCaps="true"
tools:textColor="?attr/ctx_menu_controller_text_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"/>
osmand:typeface="@string/font_roboto_medium"
tools:textColor="?attr/ctx_menu_controller_text_color"/>
</LinearLayout>
@ -325,10 +327,10 @@
android:minHeight="@dimen/context_menu_action_buttons_h"
android:orientation="horizontal"
android:paddingBottom="@dimen/context_menu_padding_margin_small"
android:paddingLeft="@dimen/context_menu_progress_padding_left"
android:paddingStart="@dimen/context_menu_progress_padding_left"
android:paddingRight="@dimen/context_menu_progress_padding_right"
android:paddingEnd="@dimen/context_menu_progress_padding_right"
android:paddingLeft="@dimen/context_menu_progress_padding_left"
android:paddingRight="@dimen/context_menu_progress_padding_right"
android:paddingStart="@dimen/context_menu_progress_padding_left"
android:visibility="gone"
tools:visibility="visible">
@ -364,11 +366,11 @@
<ImageView
android:id="@+id/progressButton"
android:contentDescription="@string/shared_string_close"
android:layout_width="@dimen/context_menu_progress_cancel_button_size"
android:layout_height="@dimen/context_menu_progress_cancel_button_size"
android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/shared_string_close"
android:scaleType="center"
tools:src="@drawable/map_action_cancel"/>
@ -383,73 +385,73 @@
android:background="?attr/ctx_menu_divider"/>
<LinearLayout
android:paddingLeft="@dimen/map_widget_icon_margin"
android:paddingRight="@dimen/map_widget_icon_margin"
android:id="@+id/context_menu_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="horizontal"
android:paddingLeft="@dimen/map_widget_icon_margin"
android:paddingRight="@dimen/map_widget_icon_margin">
<LinearLayout
android:id="@+id/context_menu_fav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?selectableItemBackground"
android:clickable="true"
android:id="@+id/context_menu_fav_view"
android:gravity="center"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/context_menu_main_actions_padding_top"
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom">
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom"
android:paddingTop="@dimen/context_menu_main_actions_padding_top">
<ImageView
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:id="@+id/context_menu_fav_image_view"
android:contentDescription="@string/shared_string_add_to_favorites"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:contentDescription="@string/shared_string_add_to_favorites"
android:scaleType="center"
android:src="@drawable/map_action_fav_dark"/>
<net.osmand.plus.widgets.TextViewEx
android:maxLines="1"
android:ellipsize="end"
android:id="@+id/context_menu_fav_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@string/shared_string_add"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/ctx_menu_buttons_text_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"/>
osmand:typeface="@string/font_roboto_regular"
tools:text="@string/shared_string_add"/>
</LinearLayout>
<LinearLayout
android:id="@+id/context_menu_route_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?selectableItemBackground"
android:clickable="true"
android:id="@+id/context_menu_route_view"
android:gravity="center"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/context_menu_main_actions_padding_top"
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom">
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom"
android:paddingTop="@dimen/context_menu_main_actions_padding_top">
<ImageView
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:id="@+id/context_menu_route_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:scaleType="center"
android:src="@drawable/map_action_flag_dark"/>
<net.osmand.plus.widgets.TextViewEx
android:maxLines="1"
android:ellipsize="end"
android:id="@+id/context_menu_route_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/shared_string_marker"
android:textColor="@color/ctx_menu_buttons_text_color"
android:textSize="@dimen/default_desc_text_size"
@ -458,32 +460,32 @@
</LinearLayout>
<LinearLayout
android:id="@+id/context_menu_share_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?selectableItemBackground"
android:clickable="true"
android:id="@+id/context_menu_share_view"
android:gravity="center"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/context_menu_main_actions_padding_top"
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom">
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom"
android:paddingTop="@dimen/context_menu_main_actions_padding_top">
<ImageView
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:id="@+id/context_menu_share_image_view"
android:contentDescription="@string/context_menu_item_share_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:contentDescription="@string/context_menu_item_share_location"
android:scaleType="center"
android:src="@drawable/map_action_gshare_dark"/>
<net.osmand.plus.widgets.TextViewEx
android:maxLines="1"
android:ellipsize="end"
android:id="@+id/context_menu_share_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/shared_string_share"
android:textColor="@color/ctx_menu_buttons_text_color"
android:textSize="@dimen/default_desc_text_size"
@ -492,32 +494,32 @@
</LinearLayout>
<LinearLayout
android:id="@+id/context_menu_more_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?selectableItemBackground"
android:clickable="true"
android:id="@+id/context_menu_more_view"
android:gravity="center"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/context_menu_main_actions_padding_top"
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom">
android:paddingBottom="@dimen/context_menu_main_actions_padding_bottom"
android:paddingTop="@dimen/context_menu_main_actions_padding_top">
<ImageView
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:id="@+id/context_menu_more_image_view"
android:contentDescription="@string/shared_string_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/context_menu_main_actions_icon_margin"
android:contentDescription="@string/shared_string_more"
android:scaleType="center"
android:src="@drawable/map_overflow_menu_white"/>
<net.osmand.plus.widgets.TextViewEx
android:maxLines="1"
android:ellipsize="end"
android:id="@+id/context_menu_more_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/shared_string_actions"
android:textColor="@color/ctx_menu_buttons_text_color"
android:textSize="@dimen/default_desc_text_size"
@ -539,35 +541,35 @@
android:layout_height="@dimen/context_menu_buttons_bottom_height">
<net.osmand.plus.widgets.TextViewEx
android:textAllCaps="true"
android:background="?attr/selectableItemBackground"
android:id="@+id/context_menu_details_button"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default"
android:gravity="start|center_vertical"
android:layout_gravity="center_vertical"
tools:textColor="?attr/contextMenuButtonColor"
android:text="@string/description"
android:textAllCaps="true"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
android:text="@string/description"/>
tools:textColor="?attr/contextMenuButtonColor"/>
<net.osmand.plus.widgets.TextViewEx
android:textAllCaps="true"
android:background="?attr/selectableItemBackground"
android:id="@+id/context_menu_directions_button"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:gravity="end|center_vertical"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default"
android:gravity="end|center_vertical"
tools:textColor="?attr/contextMenuButtonColor"
android:text="@string/get_directions"
android:textAllCaps="true"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
android:text="@string/get_directions"/>
tools:textColor="?attr/contextMenuButtonColor"/>
</LinearLayout>
@ -589,8 +591,8 @@
android:id="@+id/context_menu_bottom_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:background="?attr/ctx_menu_info_view_bg"
android:orientation="vertical">
android:orientation="vertical"
tools:background="?attr/ctx_menu_info_view_bg">
</LinearLayout>
@ -610,8 +612,8 @@
android:layout_width="@dimen/fab_size_with_shadow"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:gravity="center"
android:layout_marginRight="@dimen/fab_margin_right"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">

View file

@ -9,6 +9,23 @@
android:descendantFocusability="blocksDescendants"
android:minHeight="50dp">
<ImageView
android:id="@+id/info_move"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="2dp"
android:layout_marginRight="2dp"
android:clickable="false"
android:contentDescription="@string/shared_string_more"
android:focusable="false"
android:paddingLeft="12dp"
android:paddingStart="12dp"
android:scaleType="center"
android:src="@drawable/ic_flat_list_dark"
android:visibility="gone"
tools:visibility="visible"/>
<ImageView
android:id="@+id/waypoint_icon"
android:layout_width="56dp"
@ -106,19 +123,4 @@
android:scaleType="center"
android:src="@drawable/ic_action_remove_dark"/>
<ImageView
android:id="@+id/info_move"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="2dp"
android:layout_marginRight="2dp"
android:clickable="false"
android:contentDescription="@string/shared_string_more"
android:focusable="false"
android:scaleType="center"
android:src="@drawable/ic_flat_list_dark"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>

View file

@ -9,6 +9,7 @@
3. All your modified/created strings are in the top of the file (to make easier find what\'s translated).
PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy
-->
<string name="will_open_tomorrow_at">Will open tomorrow at</string>
<string name="rendering_attr_hidePOILabels_name">POI labels</string>
<string name="shared_string_without_name">Without name</string>
<string name="what_is_here">What\'s here:</string>

View file

@ -47,6 +47,7 @@ import net.osmand.plus.voice.TTSCommandPlayerImpl;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.RoutingConfiguration;
import net.osmand.util.Algorithms;
import net.osmand.util.OpeningHoursParser;
import org.xmlpull.v1.XmlPullParserException;
@ -378,8 +379,21 @@ public class AppInitializer implements IProgress {
app.mapMarkersDbHelper = startupInit(new MapMarkersDbHelper(app), MapMarkersDbHelper.class);
app.mapMarkersHelper = startupInit(new MapMarkersHelper(app), MapMarkersHelper.class);
app.searchUICore = startupInit(new QuickSearchHelper(app), QuickSearchHelper.class);
initOpeningHoursParser();
}
private void initOpeningHoursParser() {
OpeningHoursParser.setAdditionalString("off", app.getString(R.string.day_off_label));
OpeningHoursParser.setAdditionalString("is_open", app.getString(R.string.poi_dialog_opening_hours));
OpeningHoursParser.setAdditionalString("is_open_24_7", app.getString(R.string.shared_string_is_open_24_7));
OpeningHoursParser.setAdditionalString("will_open_at", app.getString(R.string.will_open_at));
OpeningHoursParser.setAdditionalString("open_from", app.getString(R.string.open_from));
OpeningHoursParser.setAdditionalString("will_close_at", app.getString(R.string.will_close_at));
OpeningHoursParser.setAdditionalString("open_till", app.getString(R.string.open_till));
OpeningHoursParser.setAdditionalString("will_open_tomorrow_at", app.getString(R.string.will_open_tomorrow_at));
OpeningHoursParser.setAdditionalString("will_open_on", app.getString(R.string.will_open_on));
}
private void updateRegionVars() {
app.regions.setTranslator(new RegionTranslation() {

View file

@ -55,6 +55,7 @@ import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.TargetPointsHelper;
import net.osmand.plus.TargetPointsHelper.TargetPoint;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.dashboard.tools.DashFragmentData;
@ -244,6 +245,10 @@ public class DashboardOnMap implements ObservableScrollViewCallbacks, DynamicLis
&& (visibleType == DashboardType.WAYPOINTS || visibleType == DashboardType.WAYPOINTS_FLAT)) {
List<Object> activeObjects = ((StableArrayAdapter) listAdapter).getActiveObjects();
Object obj = listAdapter.getItem(position);
if (obj instanceof LocationPointWrapper) {
LocationPointWrapper w = (LocationPointWrapper) obj;
return !((TargetPoint) w.getPoint()).start;
}
return activeObjects.contains(obj);
}
return false;
@ -287,10 +292,11 @@ public class DashboardOnMap implements ObservableScrollViewCallbacks, DynamicLis
@Override
public String getTitle() {
List<Object> activeObjects;
if ((visibleType == DashboardType.WAYPOINTS || visibleType == DashboardType.WAYPOINTS_FLAT)
&& (getMyApplication().getRoutingHelper().isRoutePlanningMode() || getMyApplication().getRoutingHelper().isFollowingMode())
&& item != null
&& stableAdapter.getActiveObjects().size() == 0) {
&& ((activeObjects = stableAdapter.getActiveObjects()).isEmpty() || isContainsOnlyStart(activeObjects))) {
return mapActivity.getResources().getString(R.string.cancel_navigation);
} else {
return null;
@ -307,7 +313,8 @@ public class DashboardOnMap implements ObservableScrollViewCallbacks, DynamicLis
if (visibleType == DashboardType.WAYPOINTS || visibleType == DashboardType.WAYPOINTS_FLAT) {
onItemsSwapped(stableAdapter.getActiveObjects());
}
if (stableAdapter.getActiveObjects().size() == 0) {
List<Object> activeObjects = stableAdapter.getActiveObjects();
if (activeObjects.isEmpty() || isContainsOnlyStart(activeObjects)) {
hideDashboard();
if (visibleType == DashboardType.WAYPOINTS || visibleType == DashboardType.WAYPOINTS_FLAT) {
mapActivity.getMapActions().stopNavigationWithoutConfirm();
@ -319,6 +326,17 @@ public class DashboardOnMap implements ObservableScrollViewCallbacks, DynamicLis
}
}
}
private boolean isContainsOnlyStart(List<Object> items) {
if (items.size() == 1) {
Object item = items.get(0);
if (item instanceof LocationPointWrapper) {
LocationPointWrapper w = (LocationPointWrapper) item;
return ((TargetPoint) w.getPoint()).start;
}
}
return false;
}
});
gradientToolbar = ContextCompat.getDrawable(mapActivity, R.drawable.gradient_toolbar).mutate();
@ -1471,10 +1489,8 @@ public class DashboardOnMap implements ObservableScrollViewCallbacks, DynamicLis
LocationPointWrapper p = (LocationPointWrapper) obj;
if (p.getPoint() instanceof TargetPoint) {
TargetPoint t = (TargetPoint) p.getPoint();
if (!t.start) {
t.intermediate = true;
allTargets.add(t);
}
t.intermediate = true;
allTargets.add(t);
}
}
}
@ -1482,9 +1498,15 @@ public class DashboardOnMap implements ObservableScrollViewCallbacks, DynamicLis
allTargets.get(allTargets.size() - 1).intermediate = false;
}
}
getMyApplication().getTargetPointsHelper().reorderAllTargetPoints(allTargets, false);
TargetPointsHelper targetPointsHelper = getMyApplication().getTargetPointsHelper();
if (allTargets.size() > 0) {
TargetPoint start = allTargets.remove(0);
targetPointsHelper.setStartPoint(new LatLon(start.getLatitude(), start.getLongitude()),
false, start.getPointDescription(getMyApplication()));
}
targetPointsHelper.reorderAllTargetPoints(allTargets, false);
newRouteIsCalculated(false, new ValueHolder<Boolean>());
getMyApplication().getTargetPointsHelper().updateRouteAndRefresh(true);
targetPointsHelper.updateRouteAndRefresh(true);
}
}

View file

@ -213,7 +213,7 @@ public class WaypointDialogHelper {
for (Object p : points) {
if (p instanceof LocationPointWrapper) {
LocationPointWrapper w = (LocationPointWrapper) p;
if (w.type == WaypointHelper.TARGETS && !((TargetPoint) w.point).start) {
if (w.type == WaypointHelper.TARGETS) {
activePoints.add(p);
}
}
@ -222,31 +222,20 @@ public class WaypointDialogHelper {
}
private List<Drawable> getCustomDividers(Context ctx, List<Object> points, boolean nightMode) {
int color;
int pointColor;
if (nightMode) {
color = ContextCompat.getColor(ctx, R.color.dashboard_divider_dark);
pointColor = ContextCompat.getColor(ctx, R.color.dashboard_divider_dark);
} else {
color = ContextCompat.getColor(ctx, R.color.dashboard_divider_light);
pointColor = ContextCompat.getColor(ctx, R.color.ctx_menu_info_divider_light);
}
int color = ContextCompat.getColor(ctx, nightMode
? R.color.dashboard_divider_dark : R.color.dashboard_divider_light);
Shape fullDividerShape = new ListDividerShape(color, 0);
Shape halfDividerShape = new ListDividerShape(color, AndroidUtils.dpToPx(ctx, 56f));
Shape halfPointDividerShape = new ListDividerShape(color, AndroidUtils.dpToPx(ctx, 56f),
pointColor, AndroidUtils.dpToPx(ctx, 1.5f), true);
Shape headerDividerShape = new ListDividerShape(color, AndroidUtils.dpToPx(ctx, 16f));
final ShapeDrawable fullDivider = new ShapeDrawable(fullDividerShape);
final ShapeDrawable halfDivider = new ShapeDrawable(halfDividerShape);
final ShapeDrawable halfPointDivider = new ShapeDrawable(halfPointDividerShape);
final ShapeDrawable headerDivider = new ShapeDrawable(headerDividerShape);
int divHeight = AndroidUtils.dpToPx(ctx, 1f);
fullDivider.setIntrinsicHeight(divHeight);
halfDivider.setIntrinsicHeight(divHeight);
halfPointDivider.setIntrinsicHeight(divHeight);
headerDivider.setIntrinsicHeight(divHeight);
List<Drawable> res = new ArrayList<>();
@ -267,16 +256,7 @@ public class WaypointDialogHelper {
Drawable d = null;
if (locationPointNext) {
if (locationPoint) {
LocationPointWrapper w = (LocationPointWrapper) obj;
if (w.type == WaypointHelper.TARGETS) {
d = halfPointDivider;
} else {
d = halfDivider;
}
} else {
d = fullDivider;
}
d = locationPoint ? halfDivider : fullDivider;
} else if (objNext instanceof RadiusItem && labelView) {
d = headerDivider;
} else if (locationPoint && !bottomDividerViewNext) {
@ -398,108 +378,61 @@ public class WaypointDialogHelper {
v = ctx.getLayoutInflater().inflate(R.layout.waypoint_reached, null);
}
updatePointInfoView(app, ctx, v, point, true, nightMode, edit, false);
final View more = v.findViewById(R.id.all_points);
final View move = v.findViewById(R.id.info_move);
final View remove = v.findViewById(R.id.info_close);
v.findViewById(R.id.all_points).setVisibility(View.GONE);
final ImageView move = (ImageView) v.findViewById(R.id.info_move);
final ImageButton remove = (ImageButton) v.findViewById(R.id.info_close);
if (!edit) {
remove.setVisibility(View.GONE);
move.setVisibility(View.GONE);
more.setVisibility(View.GONE);
} else if (point.type == WaypointHelper.TARGETS && !flat) {
if (((TargetPoint) point.point).start) {
remove.setVisibility(View.GONE);
move.setVisibility(View.GONE);
more.setVisibility(View.GONE);
} else {
remove.setVisibility(View.GONE);
move.setVisibility(View.VISIBLE);
more.setVisibility(View.GONE);
((ImageView) move).setImageDrawable(app.getIconsCache().getIcon(
R.drawable.ic_action_reorder, !nightMode));
if (app.accessibilityEnabled()) {
move.setOnClickListener(new View.OnClickListener() {
} else {
boolean notFlatTargets = point.type == WaypointHelper.TARGETS && !flat;
boolean startPoint = ((TargetPoint) point.point).start;
final TargetPointsHelper targetPointsHelper = app.getTargetPointsHelper();
boolean canRemove = !targetPointsHelper.getIntermediatePoints().isEmpty();
remove.setVisibility(View.VISIBLE);
remove.setImageDrawable(app.getIconsCache().getIcon(R.drawable.ic_action_remove_dark, !nightMode));
remove.setEnabled(canRemove);
remove.setAlpha(canRemove ? 1 : .5f);
if (canRemove) {
if (notFlatTargets && startPoint) {
remove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
((DragIcon) view.getTag()).onClick();
public void onClick(View v) {
if (targetPointsHelper.getPointToStart() == null) {
if (!targetPointsHelper.getIntermediatePoints().isEmpty()) {
replaceStartWithFirstIntermediate(targetPointsHelper, ctx, helper);
}
} else {
targetPointsHelper.setStartPoint(null, true, null);
updateControls(ctx, helper);
}
}
});
} else {
remove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
deletePoint(app, ctx, adapter, helper, point, deletedPoints, true);
}
});
}
}
move.setVisibility(notFlatTargets ? View.VISIBLE : View.GONE);
if (notFlatTargets) {
move.setImageDrawable(app.getIconsCache().getIcon(R.drawable.ic_action_reorder, !nightMode));
move.setTag(new DragIcon() {
@Override
public void onClick() {
final PopupMenu optionsMenu = new PopupMenu(ctx, move);
DirectionsDialogs.setupPopUpMenuIcon(optionsMenu);
List<Object> activeObjects = ((StableArrayAdapter) adapter).getActiveObjects();
int count = activeObjects.size();
int t = -1;
for (int i = 0; i < activeObjects.size(); i++) {
Object o = activeObjects.get(i);
if (point == o) {
t = i;
break;
}
}
final int index = t;
MenuItem item;
final TargetPointsHelper targetPointsHelper = app.getTargetPointsHelper();
final TargetPoint start = targetPointsHelper.getPointToStart();
if (count > 1 && (index > 0 || start != null)) {
item = optionsMenu.getMenu().add(R.string.shared_string_move_up)
.setIcon(app.getIconsCache().getThemedIcon(R.drawable.ic_action_arrow_drop_up));
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (index == 0) {
switchStartAndFirstIntermediate(targetPointsHelper, ctx, start, helper);
} else if (helper != null && helper.helperCallbacks != null) {
helper.helperCallbacks.exchangeWaypoints(index, index - 1);
}
updateRouteInfoMenu(ctx);
return true;
}
});
}
if (index < count - 1 && count > 1) {
item = optionsMenu.getMenu().add(R.string.shared_string_move_down)
.setIcon(app.getIconsCache().getThemedIcon(R.drawable.ic_action_arrow_drop_down));
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (helper != null && helper.helperCallbacks != null) {
helper.helperCallbacks.exchangeWaypoints(index, index + 1);
}
updateRouteInfoMenu(ctx);
return true;
}
});
}
item = optionsMenu.getMenu().add(R.string.shared_string_remove)
.setIcon(app.getIconsCache().getThemedIcon(R.drawable.ic_action_remove_dark));
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
deletePoint(app, ctx, adapter, helper, point, deletedPoints, true);
return true;
}
});
optionsMenu.show();
// do nothing
}
});
}
} else {
remove.setVisibility(View.VISIBLE);
move.setVisibility(View.GONE);
more.setVisibility(View.GONE);
((ImageButton) remove).setImageDrawable(app.getIconsCache().getIcon(
R.drawable.ic_action_remove_dark, !nightMode));
remove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
deletePoint(app, ctx, adapter, helper, point, deletedPoints, true);
}
});
}
return v;
}
@ -535,6 +468,17 @@ public class WaypointDialogHelper {
updateRouteInfoMenu(ctx);
}
private static void replaceStartWithFirstIntermediate(TargetPointsHelper targetPointsHelper, Activity ctx,
WaypointDialogHelper helper) {
List<TargetPoint> intermediatePoints = targetPointsHelper.getIntermediatePointsWithTarget();
TargetPoint firstIntermediate = intermediatePoints.remove(0);
targetPointsHelper.setStartPoint(new LatLon(firstIntermediate.getLatitude(),
firstIntermediate.getLongitude()), false, firstIntermediate.getPointDescription(ctx));
targetPointsHelper.reorderAllTargetPoints(intermediatePoints, true);
updateControls(ctx, helper);
}
// switch start & first intermediate point
private static void switchStartAndFirstIntermediate(TargetPointsHelper targetPointsHelper, Activity ctx,
TargetPoint start, WaypointDialogHelper helper) {
@ -675,7 +619,20 @@ public class WaypointDialogHelper {
public void onClick(View v) {
boolean hasActivePoints = false;
if (thisAdapter instanceof StableArrayAdapter) {
hasActivePoints = ((StableArrayAdapter) thisAdapter).getActiveObjects().size() > 0;
List<Object> items = ((StableArrayAdapter) thisAdapter).getActiveObjects();
if (items.size() > 0) {
if (items.size() > 1) {
hasActivePoints = true;
} else {
Object item = items.get(0);
if (item instanceof LocationPointWrapper) {
LocationPointWrapper w = (LocationPointWrapper) item;
hasActivePoints = !((TargetPoint) w.point).start;
} else {
hasActivePoints = true;
}
}
}
}
final PopupMenu optionsMenu = new PopupMenu(ctx, moreBtn);

View file

@ -1175,7 +1175,7 @@ public class MapContextMenu extends MenuTitleController implements StateChangedL
return 0;
}
public String getAdditionalInfo() {
public CharSequence getAdditionalInfo() {
if (menuController != null) {
return menuController.getAdditionalInfoStr();
}

View file

@ -1196,7 +1196,7 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
}
TextView additionalInfoTextView = (TextView) view.findViewById(R.id.additional_info_text_view);
String additionalInfoStr = menu.getAdditionalInfo();
CharSequence additionalInfoStr = menu.getAdditionalInfo();
if (!TextUtils.isEmpty(additionalInfoStr)) {
int colorId = menu.getAdditionalInfoColor();
int additionalInfoIconRes = menu.getAdditionalInfoIconRes();

View file

@ -8,6 +8,8 @@ import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
@ -29,9 +31,7 @@ import net.osmand.map.WorldRegion;
import net.osmand.plus.GPXUtilities.WptPt;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.MapMarkersHelper.MapMarker;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.TargetPointsHelper.TargetPoint;
import net.osmand.plus.activities.MapActivity;
@ -74,6 +74,7 @@ import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControll
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.osmand.util.OpeningHoursParser.OpeningHours;
import java.io.IOException;
import java.util.Iterator;
@ -117,6 +118,8 @@ public abstract class MenuController extends BaseMenuController {
private WorldRegion downloadRegion;
private DownloadIndexesThread downloadThread;
protected List<OpeningHours.Info> openingHoursInfo;
public MenuController(MenuBuilder builder, PointDescription pointDescription, MapActivity mapActivity) {
super(mapActivity);
this.pointDescription = pointDescription;
@ -243,12 +246,8 @@ public abstract class MenuController extends BaseMenuController {
}
protected void addMyLocationToPlainItems(LatLon latLon) {
OsmandSettings st = ((OsmandApplication) getMapActivity().getApplicationContext()).getSettings();
addPlainMenuItem(R.drawable.ic_action_get_my_location, null, PointDescription.getLocationName(getMapActivity(),
latLon.getLatitude(), latLon.getLongitude(), true).replaceAll("\n", " "), false, false, null);
//if (st.COORDINATES_FORMAT.get() != PointDescription.OLC_FORMAT)
// addPlainMenuItem(R.drawable.ic_action_get_my_location, PointDescription.getLocationOlcName(
// latLon.getLatitude(), latLon.getLongitude()).replaceAll("\n", " "), false, false, null);
}
public PointDescription getPointDescription() {
@ -442,21 +441,47 @@ public abstract class MenuController extends BaseMenuController {
}
public int getAdditionalInfoColor() {
if (indexItem != null) {
if (openingHoursInfo != null) {
return 0;
} else if (indexItem != null) {
return R.color.icon_color;
}
return 0;
}
public String getAdditionalInfoStr() {
if (indexItem != null) {
public CharSequence getAdditionalInfoStr() {
if (openingHoursInfo != null) {
StringBuilder sb = new StringBuilder();
int colorOpen = getMapActivity().getResources().getColor(R.color.ctx_menu_amenity_opened_text_color);
int colorClosed = getMapActivity().getResources().getColor(R.color.ctx_menu_amenity_closed_text_color);
int[] pos = new int[openingHoursInfo.size()];
for (int i = 0; i < openingHoursInfo.size(); i++) {
OpeningHours.Info info = openingHoursInfo.get(i);
if (sb.length() > 0) {
sb.append("\n");
}
sb.append(info.getInfo());
pos[i] = sb.length();
}
SpannableString infoStr = new SpannableString(sb.toString());
int k = 0;
for (int i = 0; i < openingHoursInfo.size(); i++) {
OpeningHours.Info info = openingHoursInfo.get(i);
infoStr.setSpan(new ForegroundColorSpan(info.isOpened() ? colorOpen : colorClosed), k, pos[i], 0);
k = pos[i];
}
return infoStr;
} else if (indexItem != null) {
return getMapActivity().getString(R.string.file_size_in_mb, indexItem.getArchiveSizeMB());
}
return "";
}
public int getAdditionalInfoIconRes() {
if (indexItem != null) {
if (openingHoursInfo != null) {
return R.drawable.ic_action_opening_hour_16;
} else if (indexItem != null) {
return R.drawable.ic_sdcard_16;
}
return 0;

View file

@ -1,90 +0,0 @@
package net.osmand.plus.mapcontextmenu;
import android.content.Context;
import net.osmand.plus.R;
import net.osmand.util.Algorithms;
public class OpeningHoursInfo {
private boolean opened;
private boolean opened24_7;
private String openingTime = "";
private String nearToOpeningTime = "";
private String closingTime = "";
private String nearToClosingTime = "";
private String openingDay = "";
public boolean isOpened() {
return opened;
}
public void setOpened(boolean opened) {
this.opened = opened;
}
public boolean isOpened24_7() {
return opened24_7;
}
public void setOpened24_7(boolean opened24_7) {
this.opened24_7 = opened24_7;
}
public String getOpeningTime() {
return openingTime;
}
public void setOpeningTime(String openFromTime) {
this.openingTime = openFromTime;
}
public String getNearToOpeningTime() {
return nearToOpeningTime;
}
public void setNearToOpeningTime(String nearToOpeningTime) {
this.nearToOpeningTime = nearToOpeningTime;
}
public String getClosingTime() {
return closingTime;
}
public void setClosingTime(String closingTime) {
this.closingTime = closingTime;
}
public String getNearToClosingTime() {
return nearToClosingTime;
}
public void setNearToClosingTime(String nearToClosingTime) {
this.nearToClosingTime = nearToClosingTime;
}
public String getOpeningDay() {
return openingDay;
}
public void setOpeningDay(String openingDay) {
this.openingDay = openingDay;
}
public String getInfo(Context context) {
if (isOpened24_7()) {
return context.getString(R.string.shared_string_is_open_24_7);
} else if (!Algorithms.isEmpty(getNearToOpeningTime())) {
return context.getString(R.string.will_open_at) + " " + getNearToOpeningTime();
} else if (!Algorithms.isEmpty(getOpeningTime())) {
return context.getString(R.string.open_from) + " " + getOpeningTime();
} else if (!Algorithms.isEmpty(getNearToClosingTime())) {
return context.getString(R.string.will_close_at) + " " + getNearToClosingTime();
} else if (!Algorithms.isEmpty(getClosingTime())) {
return context.getString(R.string.open_till) + " " + getClosingTime();
} else if (!Algorithms.isEmpty(getOpeningDay())) {
return context.getString(R.string.will_open_on) + " " + getOpeningDay() + ".";
}
return "";
}
}

View file

@ -529,11 +529,6 @@ public class AmenityMenuBuilder extends MenuBuilder {
buildRow(view, R.drawable.ic_action_get_my_location, null, PointDescription.getLocationName(app,
amenity.getLocation().getLatitude(), amenity.getLocation().getLongitude(), true)
.replaceAll("\n", " "), 0, false, null, false, 0, false, null, false);
//if (st.COORDINATES_FORMAT.get() != PointDescription.OLC_FORMAT)
// buildRow(view, R.drawable.ic_action_get_my_location, PointDescription.getLocationOlcName(
// amenity.getLocation().getLatitude(), amenity.getLocation().getLongitude())
// .replaceAll("\n", " "), 0, false, null, false, 0, false, null);
}
public void buildAmenityRow(View view, AmenityInfoRow info) {

View file

@ -1,17 +1,12 @@
package net.osmand.plus.mapcontextmenu.builders;
import android.graphics.Color;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.Amenity;
import net.osmand.data.FavouritePoint;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect;
import net.osmand.data.TransportStop;
import net.osmand.osm.PoiCategory;
@ -19,7 +14,6 @@ import net.osmand.plus.FavouritesDbHelper.FavoriteGroup;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.mapcontextmenu.MenuBuilder;
import net.osmand.plus.widgets.TextViewEx;
import net.osmand.util.MapUtils;
import java.util.List;
@ -36,7 +30,7 @@ public class FavouritePointMenuBuilder extends MenuBuilder {
acquireOriginObject();
}
public void acquireOriginObject()
private void acquireOriginObject()
{
String originObjectName = fav.getOriginObjectName();
if (originObjectName.length() > 0) {

View file

@ -14,19 +14,17 @@ import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.mapcontextmenu.MenuBuilder;
import net.osmand.plus.mapcontextmenu.MenuController;
import net.osmand.plus.mapcontextmenu.OpeningHoursInfo;
import net.osmand.plus.mapcontextmenu.WikipediaDialogFragment;
import net.osmand.plus.mapcontextmenu.builders.AmenityMenuBuilder;
import net.osmand.plus.transport.TransportStopRoute;
import net.osmand.plus.render.RenderingIcons;
import net.osmand.plus.resources.TransportIndexRepository;
import net.osmand.plus.transport.TransportStopRoute;
import net.osmand.plus.transport.TransportStopType;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.osmand.util.OpeningHoursParser;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@ -36,7 +34,6 @@ public class AmenityMenuController extends MenuController {
private Amenity amenity;
private List<TransportStopRoute> routes = new ArrayList<>();
private OpeningHoursInfo openingHoursInfo;
private MapMarker marker;
@ -77,7 +74,7 @@ public class AmenityMenuController extends MenuController {
leftTitleButtonController.updateStateListDrawableIcon(R.drawable.ic_action_read_text, true);
}
openingHoursInfo = processOpeningHours(amenity);
openingHoursInfo = OpeningHoursParser.getInfo(amenity.getOpeningHours());
}
@Override
@ -146,30 +143,6 @@ public class AmenityMenuController extends MenuController {
return getTypeStr(amenity);
}
@Override
public int getAdditionalInfoColor() {
if (openingHoursInfo != null) {
return openingHoursInfo.isOpened() ? R.color.ctx_menu_amenity_opened_text_color : R.color.ctx_menu_amenity_closed_text_color;
}
return super.getAdditionalInfoColor();
}
@Override
public String getAdditionalInfoStr() {
if (openingHoursInfo != null) {
return openingHoursInfo.getInfo(getMapActivity());
}
return super.getAdditionalInfoStr();
}
@Override
public int getAdditionalInfoIconRes() {
if (openingHoursInfo != null) {
return R.drawable.ic_action_opening_hour_16;
}
return super.getAdditionalInfoIconRes();
}
public static String getTypeStr(Amenity amenity) {
PoiCategory pc = amenity.getType();
PoiType pt = pc.getPoiTypeByKeyName(amenity.getSubType());
@ -182,28 +155,6 @@ public class AmenityMenuController extends MenuController {
return typeStr;
}
public static OpeningHoursInfo processOpeningHours(Amenity amenity) {
OpeningHoursParser.OpeningHours openingHours = OpeningHoursParser.parseOpenedHours(amenity.getOpeningHours());
if (openingHours == null) {
return null;
} else {
OpeningHoursInfo info = new OpeningHoursInfo();
Calendar cal = Calendar.getInstance();
boolean opened = openingHours.isOpenedForTime(cal);
info.setOpened(opened);
if (opened) {
info.setOpened24_7(openingHours.isOpened24_7());
info.setClosingTime(openingHours.getClosingTime(cal));
info.setNearToClosingTime(openingHours.getNearToClosingTime(cal));
} else {
info.setOpeningTime(openingHours.getOpeningTime(cal));
info.setNearToOpeningTime(openingHours.getNearToOpeningTime(cal));
info.setOpeningDay(openingHours.getOpeningDay(cal));
}
return info;
}
}
@Override
public String getCommonTypeStr() {
PoiCategory pc = amenity.getType();
@ -219,7 +170,8 @@ public class AmenityMenuController extends MenuController {
public void addPlainMenuItems(String typeStr, PointDescription pointDescription, LatLon latLon) {
}
public static void addPlainMenuItems(Amenity amenity, String typeStr, MenuBuilder builder) {
public static void addTypeMenuItem(Amenity amenity, MenuBuilder builder) {
String typeStr = getTypeStr(amenity);
if (!Algorithms.isEmpty(typeStr)) {
int resId = getRightIconId(amenity);
if (resId == 0) {

View file

@ -14,12 +14,12 @@ 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.OpeningHoursInfo;
import net.osmand.plus.mapcontextmenu.builders.FavouritePointMenuBuilder;
import net.osmand.plus.mapcontextmenu.editors.FavoritePointEditor;
import net.osmand.plus.mapcontextmenu.editors.FavoritePointEditorFragment;
import net.osmand.plus.transport.TransportStopRoute;
import net.osmand.util.Algorithms;
import net.osmand.util.OpeningHoursParser;
import java.util.ArrayList;
import java.util.List;
@ -29,7 +29,6 @@ public class FavouritePointMenuController extends MenuController {
private FavouritePoint fav;
private MapMarker mapMarker;
private List<TransportStopRoute> routes = new ArrayList<>();
private OpeningHoursInfo openingHoursInfo;
public FavouritePointMenuController(MapActivity mapActivity, PointDescription pointDescription, final FavouritePoint fav) {
super(new FavouritePointMenuBuilder(mapActivity, fav), pointDescription, mapActivity);
@ -55,7 +54,7 @@ public class FavouritePointMenuController extends MenuController {
Object originObject = getBuilder().getOriginObject();
if (originObject instanceof Amenity) {
openingHoursInfo = AmenityMenuController.processOpeningHours((Amenity) originObject);
openingHoursInfo = OpeningHoursParser.getInfo(((Amenity) originObject).getOpeningHours());
}
}
@ -144,35 +143,10 @@ public class FavouritePointMenuController extends MenuController {
Object originObject = getBuilder().getOriginObject();
if (originObject != null) {
if (originObject instanceof Amenity) {
Amenity amenity = (Amenity) originObject;
AmenityMenuController.addPlainMenuItems(amenity, AmenityMenuController.getTypeStr(amenity), builder);
AmenityMenuController.addTypeMenuItem((Amenity) originObject, builder);
}
} else {
addMyLocationToPlainItems(latLon);
}
}
@Override
public int getAdditionalInfoColor() {
if (openingHoursInfo != null) {
return openingHoursInfo.isOpened() ? R.color.ctx_menu_amenity_opened_text_color : R.color.ctx_menu_amenity_closed_text_color;
}
return 0;
}
@Override
public String getAdditionalInfoStr() {
if (openingHoursInfo != null) {
return openingHoursInfo.getInfo(getMapActivity());
}
return "";
}
@Override
public int getAdditionalInfoIconRes() {
if (openingHoursInfo != null) {
return R.drawable.ic_action_opening_hour_16;
}
return 0;
}
}

View file

@ -190,7 +190,7 @@ public class MapDataMenuController extends MenuController {
}
@Override
public String getAdditionalInfoStr() {
public CharSequence getAdditionalInfoStr() {
double mb = 0;
if (backuped) {
if (localIndexInfo != null) {

View file

@ -131,7 +131,7 @@ public class EditPOIMenuController extends MenuController {
}
@Override
public String getAdditionalInfoStr() {
public CharSequence getAdditionalInfoStr() {
return actionStr;
}

View file

@ -68,7 +68,7 @@ public class ParkingPositionMenuController extends MenuController {
}
@Override
public String getAdditionalInfoStr() {
public CharSequence getAdditionalInfoStr() {
return parkingLeftDescription;
}