diff --git a/DataExtractionOSM/src/com/osmand/ToDoConstants.java b/DataExtractionOSM/src/com/osmand/ToDoConstants.java index 7cde8287dd..d880e93448 100644 --- a/DataExtractionOSM/src/com/osmand/ToDoConstants.java +++ b/DataExtractionOSM/src/com/osmand/ToDoConstants.java @@ -19,7 +19,7 @@ public class ToDoConstants { // Some icons are not fine (as back menu from map - it is blured). // Got by Andrei - // 50. Invent opening hours editor in order to edit POI hours better on device + // 60. Audio guidance for routing // 61. Provide route information for YOURS (calclate turns/angle/expected time). // Fix some missing turns in CloudMade (for secondary roads wo name). Add them (if dist to prev/next turn > 150m) [dacha] @@ -39,16 +39,15 @@ public class ToDoConstants { // FIXME BUGS Android - // FIXME !!!! Check agains ID is not unique ! (for relation/node/way - it could be the same) - checked for data extraction & index creator + // double tap to zoom [done] // REFACTOR Settings activity ( for check box properties!) // Fix bugs with test data (bug with follow turn / left time / add turn) // show POI choose near by or last map selection. // Show poi direction (using sensor) - // double tap to zoom + // hide center point (enable only when trackball using) // forbid rotate map to landscape // Improvement : Show stops in the transport route - // BUG : loshitsa delete POI // TODO swing // 9. Fix issues with big files (such as netherlands) - save memory (!) - very slow due to transport index ! @@ -61,7 +60,8 @@ public class ToDoConstants { // DONE ANDROID : // 33. Build transport locations. Create transport index (transport-stops) (investigate) - // Not implemented : show key/transit stops on map, follow mode (show next stop) + // Not implemented : show key/transit stops on map, follow mode (show next stop) + // 50. Invent opening hours editor in order to edit POI hours better on device // DONE SWING diff --git a/DataExtractionOSM/src/com/osmand/osm/OpeningHoursParser.java b/DataExtractionOSM/src/com/osmand/osm/OpeningHoursParser.java index 3d119d83aa..15d07fd006 100644 --- a/DataExtractionOSM/src/com/osmand/osm/OpeningHoursParser.java +++ b/DataExtractionOSM/src/com/osmand/osm/OpeningHoursParser.java @@ -1,18 +1,120 @@ package com.osmand.osm; import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; +import java.util.Calendar; import java.util.List; -import java.util.Map; public class OpeningHoursParser { private static final String[] daysStr = new String[] {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ - public static boolean parseRule(String r, int[][] hours, boolean[] days){ - Arrays.fill(days, false); + + public static interface OpeningHoursRule { + + public boolean isOpenedForTime(Calendar cal); + + public String toRuleString(); + } + + public static class BasicDayOpeningHourRule implements OpeningHoursRule { + private boolean[] days = new boolean[7]; + private int startTime = -1; + private int endTime = - 1; + + public boolean[] getDays() { + return days; + } + + public void setStartTime(int startTime) { + this.startTime = startTime; + } + public int getStartTime() { + return startTime; + } + + public int getEndTime() { + return endTime; + } + public void setEndTime(int endTime) { + this.endTime = endTime; + } + + @Override + public boolean isOpenedForTime(Calendar cal) { + if(startTime == -1){ + return false; + } + int i = cal.get(Calendar.DAY_OF_WEEK); + int d = (i + 5) % 7; + int p = d - 1; + if(p < 0){ + p+=7; + } + int time = cal.get(Calendar.HOUR) * 60 + cal.get(Calendar.MINUTE); + // one day working 10 - 20 (not 20 - 04) + if(startTime < endTime || endTime == -1){ + if(days[d]){ + if(time >= startTime && (endTime == -1 || time <= endTime)){ + return true; + } + } + return false; + } else { + if (time <= endTime && days[p]) { + // check in previous day + return true; + } else if (time <= startTime && days[d]) { + // check in previous day + return true; + } + return false; + } + } + @Override + public String toRuleString() { + StringBuilder b = new StringBuilder(25); + boolean dash = false; + boolean first = true; + for(int i=0; i< 7; i++){ + if (days[i]) { + if (i > 0 && days[i - 1] && i < 6 && days[i + 1]) { + if (!dash) { + dash = true; + b.append("-"); //$NON-NLS-1$ + } + continue; + } + if (first) { + first = false; + } else if (!dash) { + b.append(", "); //$NON-NLS-1$ + } + b.append(daysStr[i]); + dash = false; + } + } + int stHour = startTime / 60; + int stTime = startTime - stHour * 60; + int enHour = endTime / 60; + int enTime = endTime - enHour * 60; + b.append(" "); //$NON-NLS-1$ + formatTime(stHour, stTime, b); + b.append("-"); //$NON-NLS-1$ + formatTime(enHour, enTime, b); + return b.toString(); + } + + @Override + public String toString() { + return toRuleString(); + } + } + + + public static OpeningHoursRule parseRule(String r){ int startDay = -1; int previousDay = -1; + BasicDayOpeningHourRule basic = new BasicDayOpeningHourRule(); int k = 0; + boolean[] days = basic.getDays(); for (; k < r.length(); k++) { char ch = r.charAt(k); if (Character.isDigit(ch)) { @@ -25,7 +127,7 @@ public class OpeningHoursParser { if(previousDay != -1){ startDay = previousDay; } else { - return false; + return null; } } else if(k < r.length() - 1){ int i = 0; @@ -47,16 +149,16 @@ public class OpeningHoursParser { previousDay = i; } } else { - return false; + return null; } } if(previousDay == -1){ - return false; + return null; } String time = r.substring(k); String[] stEnd = time.split("-"); //$NON-NLS-1$ if(stEnd.length != 2){ - return false; + return null; } int st; int end; @@ -66,87 +168,45 @@ public class OpeningHoursParser { st = Integer.parseInt(stEnd[0].substring(0, i1).trim())* 60 + Integer.parseInt(stEnd[0].substring(i1 + 1).trim()); end = Integer.parseInt(stEnd[1].substring(0, i2).trim())* 60 + Integer.parseInt(stEnd[1].substring(i2 + 1).trim()); } catch (NumberFormatException e) { - return false; + return null; } - for(int i=0; i<7; i++){ - if(days[i]){ - hours[i][0] = st; - hours[i][1] = end; - } - } - return true; + basic.setStartTime(st); + basic.setEndTime(end); + return basic; } - public static int[][] parseOpenedHours(String format){ - int[][] hours = new int[7][2]; - for(int k = 0; k<7;k++){ - hours[k][0] = hours[k][1] = -1; - } - boolean days[] = new boolean[7]; + + public static List parseOpenedHours(String format){ String[] rules = format.split(";"); //$NON-NLS-1$ + List rs = new ArrayList(); for(String r : rules){ r = r.trim(); if(r.length() == 0){ continue; } // check if valid - if(!parseRule(r, hours, days)){ + OpeningHoursRule rule = parseRule(r); + if(rule == null){ return null; } + rs.add(rule); + } - return hours; + return rs; } - public static String toStringOpenedHours(int[][] hours){ - Map> groups = new LinkedHashMap>(); - for (int k = 0; k < 7; k++) { - if (hours[k][0] >= 0 && hours[k][1] >= 0) { - int uniqueInt = hours[k][1] * 60 * 24 + hours[k][0]; - if (!groups.containsKey(uniqueInt)) { - groups.put(uniqueInt, new ArrayList()); - } - groups.get(uniqueInt).add(k); - } - } + public static String toStringOpenedHours(List rules){ StringBuilder b = new StringBuilder(100); boolean first = true; - for(Integer time : groups.keySet()){ - if(first){ + for (OpeningHoursRule p : rules) { + if(p == null){ + continue; + } + if (first) { first = false; } else { b.append("; "); //$NON-NLS-1$ } - int end = time / (60 * 24); - int st = time - end * (60 * 24); - int stHour = st / 60; - int stTime = st - stHour * 60; - int endHour = end / 60; - int endTime = end - endHour * 60; - List list = groups.get(time); - boolean dash = false; - for(int k = 0; k < list.size(); k++){ - Integer val = list.get(k); - if(k > 0){ - if(k < list.size() - 1 && list.get(k + 1) == val + 1 && list.get(k - 1) == val - 1){ - if(!dash){ - b.append("-"); //$NON-NLS-1$ - dash = true; - } - } else if(dash){ - b.append(daysStr[val]); - dash = false; - } else { - b.append(", ").append(daysStr[val]); //$NON-NLS-1$ - } - } else { - b.append(daysStr[val]); - } - } - - b.append(" "); //$NON-NLS-1$ - formatTime(stHour, stTime, b); - b.append("-"); //$NON-NLS-1$ - formatTime(endHour, endTime, b); - + b.append(p.toRuleString()); } return b.toString(); @@ -163,11 +223,11 @@ public class OpeningHoursParser { } public static void main(String[] args) { - int[][] hours = parseOpenedHours("Mo-Fr 08:30-14:40; Sa 08:00 - 14:00"); //$NON-NLS-1$ - System.out.println(Arrays.deepToString(hours)); + List hours = parseOpenedHours("Mo-Fr 08:30-14:40; Sa 08:00 - 14:00"); //$NON-NLS-1$ + System.out.println(hours); System.out.println(toStringOpenedHours(hours)); - hours = parseOpenedHours("Mo, We-Fr 08:30-14:40; Sa 08:00 - 14:00"); //$NON-NLS-1$ - System.out.println(Arrays.deepToString(hours)); + hours = parseOpenedHours("Mo, We-Fr, Th, Sa 08:30-14:40; Sa 08:00 - 14:00"); //$NON-NLS-1$ + System.out.println(hours); System.out.println(toStringOpenedHours(hours)); } diff --git a/DataExtractionOSM/src/com/osmand/osm/io/OsmBoundsFilter.java b/DataExtractionOSM/src/com/osmand/osm/io/OsmBoundsFilter.java index ef4a16d473..ddc36191c0 100644 --- a/DataExtractionOSM/src/com/osmand/osm/io/OsmBoundsFilter.java +++ b/DataExtractionOSM/src/com/osmand/osm/io/OsmBoundsFilter.java @@ -34,7 +34,7 @@ public class OsmBoundsFilter implements IOsmStorageFilter { // filter if one of the instance exists // IMPORTANT : The main assumption is that order is preserved in osm file (first are node, way, relation)!!! if(entity instanceof Way){ - for(Long l : ((Way) entity).getNodeIds()){ + for(EntityId l : ((Way) entity).getEntityIds()){ if(storage.getRegisteredEntities().containsKey(l)){ return true; } diff --git a/OsmAnd/res/drawable/closed_poi.png b/OsmAnd/res/drawable/closed_poi.png index c140e73033..e4e97be1b1 100644 Binary files a/OsmAnd/res/drawable/closed_poi.png and b/OsmAnd/res/drawable/closed_poi.png differ diff --git a/OsmAnd/res/drawable/opened_poi.png b/OsmAnd/res/drawable/opened_poi.png new file mode 100644 index 0000000000..75a7000231 Binary files /dev/null and b/OsmAnd/res/drawable/opened_poi.png differ diff --git a/OsmAnd/res/drawable/poi.png b/OsmAnd/res/drawable/poi.png index 75a7000231..c140e73033 100644 Binary files a/OsmAnd/res/drawable/poi.png and b/OsmAnd/res/drawable/poi.png differ diff --git a/OsmAnd/res/layout/open_hours_edit.xml b/OsmAnd/res/layout/open_hours_edit.xml index e76afd73ef..1a598acc9d 100644 --- a/OsmAnd/res/layout/open_hours_edit.xml +++ b/OsmAnd/res/layout/open_hours_edit.xml @@ -1,27 +1,12 @@ - - - - - - - - - - - - - - - - - + + + + - + + - - + diff --git a/OsmAnd/res/layout/open_hours_list_item.xml b/OsmAnd/res/layout/open_hours_list_item.xml new file mode 100644 index 0000000000..a015460c7a --- /dev/null +++ b/OsmAnd/res/layout/open_hours_list_item.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/values-ru-rRU/strings.xml b/OsmAnd/res/values-ru-rRU/strings.xml index 7d0476fb82..a5645a2e9b 100644 --- a/OsmAnd/res/values-ru-rRU/strings.xml +++ b/OsmAnd/res/values-ru-rRU/strings.xml @@ -1,5 +1,7 @@ + Формат времени работы не поддерживается для редактирования + Новое правило Маршруты Остановка остановок diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index d9dbf4876f..67295670bd 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,5 +1,7 @@ + Opening hours format is not supported for editing + Add new rule Routes Stop stops diff --git a/OsmAnd/src/com/osmand/activities/EditingPOIActivity.java b/OsmAnd/src/com/osmand/activities/EditingPOIActivity.java index 8455c5d6e6..0e6967efb8 100644 --- a/OsmAnd/src/com/osmand/activities/EditingPOIActivity.java +++ b/OsmAnd/src/com/osmand/activities/EditingPOIActivity.java @@ -13,8 +13,7 @@ import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Calendar; +import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; @@ -40,20 +39,13 @@ import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; -import android.text.format.DateFormat; import android.util.Xml; -import android.view.LayoutInflater; import android.view.View; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.EditText; -import android.widget.TextView; -import android.widget.TimePicker; import android.widget.Toast; -import android.widget.TimePicker.OnTimeChangedListener; import com.osmand.AmenityIndexRepository; import com.osmand.Base64; @@ -66,11 +58,14 @@ import com.osmand.data.Amenity; import com.osmand.data.AmenityType; import com.osmand.osm.Entity; import com.osmand.osm.EntityInfo; +import com.osmand.osm.MapUtils; import com.osmand.osm.Node; import com.osmand.osm.OpeningHoursParser; import com.osmand.osm.Entity.EntityId; import com.osmand.osm.Entity.EntityType; import com.osmand.osm.OSMSettings.OSMTagKey; +import com.osmand.osm.OpeningHoursParser.BasicDayOpeningHourRule; +import com.osmand.osm.OpeningHoursParser.OpeningHoursRule; import com.osmand.osm.io.OsmBaseStorage; import com.osmand.views.OsmandMapTileView; @@ -117,7 +112,7 @@ public class EditingPOIActivity { dlg = new Dialog(ctx); Node n = new Node(latitude, longitude, -1); n.putTag(OSMTagKey.AMENITY.getValue(), ""); //$NON-NLS-1$ - n.putTag(OSMTagKey.OPENING_HOURS.getValue(), "Mo-Su 08:00-20:00"); //$NON-NLS-1$ + n.putTag(OSMTagKey.OPENING_HOURS.getValue(), ""); //$NON-NLS-1$ dlg.setTitle(R.string.poi_create_title); showDialog(n); } @@ -130,7 +125,7 @@ public class EditingPOIActivity { } Builder builder = new AlertDialog.Builder(ctx); - builder.setTitle(MessageFormat.format(this.view.getResources().getString(R.string.poi_remove_confirm_template), a.getSimpleFormat(OsmandSettings.usingEnglishNames(ctx)))); + builder.setTitle(MessageFormat.format(this.view.getResources().getString(R.string.poi_remove_confirm_template), a.getStringWithoutType(OsmandSettings.usingEnglishNames(ctx)))); final EditText comment = new EditText(ctx); comment.setText(R.string.poi_remove_title); builder.setView(comment); @@ -264,15 +259,29 @@ public class EditingPOIActivity { private void editOpenHoursDlg(){ - final int[][] time = OpeningHoursParser.parseOpenedHours(openingHours.getText().toString()); + List time = OpeningHoursParser.parseOpenedHours(openingHours.getText().toString()); + List simple = null; + if(time != null){ + simple = new ArrayList(); + for(OpeningHoursRule r : time){ + if(r instanceof BasicDayOpeningHourRule){ + simple.add((BasicDayOpeningHourRule) r); + } else { + time = null; + break; + } + } + } + + if(time == null){ - Toast.makeText(ctx, "Opening hours format is not supported for editing", Toast.LENGTH_LONG).show(); + Toast.makeText(ctx, ctx.getString(R.string.opening_hours_not_supported), Toast.LENGTH_LONG).show(); return; } Builder builder = new AlertDialog.Builder(ctx); final OpeningHoursView v = new OpeningHoursView(ctx); - builder.setView(v.createOpeningHoursEditView(time)); + builder.setView(v.createOpeningHoursEditView(simple)); builder.setPositiveButton(ctx.getString(R.string.default_buttons_apply), new DialogInterface.OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which) { @@ -600,7 +609,11 @@ public class EditingPOIActivity { EntityId id = new Entity.EntityId(EntityType.NODE, n.getId()); Node entity = (Node) st.getRegisteredEntities().get(id); entityInfo = st.getRegisteredEntityInfo().get(id); - return entity; + // check whether this is node (because id of node could be the same as relation) + if(MapUtils.getDistance(entity.getLatLon(), n.getLocation()) < 50){ + return entity; + } + return null; } } catch (IOException e) { diff --git a/OsmAnd/src/com/osmand/activities/FavouritesActivity.java b/OsmAnd/src/com/osmand/activities/FavouritesActivity.java index c0e36d7539..3f81f0f31b 100644 --- a/OsmAnd/src/com/osmand/activities/FavouritesActivity.java +++ b/OsmAnd/src/com/osmand/activities/FavouritesActivity.java @@ -283,7 +283,7 @@ public class FavouritesActivity extends ListActivity { TextView distanceLabel = (TextView) row.findViewById(R.id.favouritedistance_label); ImageView icon = (ImageView) row.findViewById(R.id.favourite_icon); FavouritePoint model = (FavouritePoint) getItem(position); - icon.setImageResource(R.drawable.poi); + icon.setImageResource(R.drawable.opened_poi); LatLon lastKnownMapLocation = OsmandSettings.getLastKnownMapLocation(FavouritesActivity.this); int dist = (int) (MapUtils.getDistance(model.getLatitude(), model.getLongitude(), lastKnownMapLocation.getLatitude(), lastKnownMapLocation.getLongitude())); diff --git a/OsmAnd/src/com/osmand/activities/OpeningHoursView.java b/OsmAnd/src/com/osmand/activities/OpeningHoursView.java index 1b0e3311cd..114b19a798 100644 --- a/OsmAnd/src/com/osmand/activities/OpeningHoursView.java +++ b/OsmAnd/src/com/osmand/activities/OpeningHoursView.java @@ -1,148 +1,229 @@ package com.osmand.activities; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.List; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnMultiChoiceClickListener; +import android.graphics.Typeface; import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; -import android.widget.CheckBox; -import android.widget.CompoundButton; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; import android.widget.TextView; import android.widget.TimePicker; -import android.widget.Toast; import android.widget.TimePicker.OnTimeChangedListener; import com.osmand.R; -import com.osmand.osm.OpeningHoursParser; +import com.osmand.osm.OpeningHoursParser.BasicDayOpeningHourRule; +import com.osmand.osm.OpeningHoursParser.OpeningHoursRule; public class OpeningHoursView { private final Context ctx; - private int selectedDay = -1; - private int[][] time; + private int selectedRule = 0; + private TimeAdapter time; private TimePicker timePickerStart; private TimePicker timePickerEnd; - private boolean firstTime = true; private boolean notifyingTime = true; + private ListView list; public OpeningHoursView(Context ctx){ this.ctx = ctx; } - public View createOpeningHoursEditView(int[][] t){ - this.time = t; + public View createOpeningHoursEditView(List t){ + this.time = new TimeAdapter(t); + // editing object + time.add(null); LayoutInflater inflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.open_hours_edit, null); timePickerStart = (TimePicker)view.findViewById(R.id.TimePickerStart); timePickerEnd = (TimePicker)view.findViewById(R.id.TimePickerEnd); - final TextView timeText =(TextView)view.findViewById(R.id.TimeText); - - + list = (ListView)view.findViewById(R.id.ListView); + list.setAdapter(time); OnTimeChangedListener onTimeChangedListener = new TimePicker.OnTimeChangedListener(){ @Override public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { - if(selectedDay == -1 || !notifyingTime){ + if(selectedRule == -1 || !notifyingTime || time.getItem(selectedRule) == null){ return; } if(view == timePickerStart ){ - time[selectedDay][0] = hourOfDay * 60 + minute; + time.getItem(selectedRule).setStartTime(hourOfDay * 60 + minute); } else { - time[selectedDay][1] = hourOfDay * 60 + minute; + time.getItem(selectedRule).setEndTime(hourOfDay * 60 + minute); } - - timeText.setText(OpeningHoursParser.toStringOpenedHours(time)); - + time.notifyDataSetChanged(); } }; - Calendar inst = Calendar.getInstance(); - int first = inst.getFirstDayOfWeek(); - int[] ids = new int[]{R.id.Day1, R.id.Day2, R.id.Day3, R.id.Day4, R.id.Day5, R.id.Day6, R.id.Day7}; - for (int i = 0; i < 7; i++) { - int d = (first + i - 1) % 7 + 1; - final CheckBox day = (CheckBox) view.findViewById(ids[i]); - inst.set(Calendar.DAY_OF_WEEK, d); - day.setText(DateFormat.format("E", inst)); //$NON-NLS-1$ - final int pos = (d + 5) % 7; - if(time[pos][0] >= 0 && time[pos][1] >= 0){ - day.setChecked(true); - } else { - day.setChecked(false); - } - day.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){ - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - // try to unselect not current day - if(selectedDay != pos && !isChecked){ - selectedDay = pos; - if(firstTime){ - Toast.makeText(ctx, "Press once to select day, twice to unselect it", Toast.LENGTH_LONG).show(); - firstTime = false; - } - // select it again - day.setChecked(true); - } else { - // uncheck - if(!isChecked){ - time[pos][0] = -1; - time[pos][1] = -1; - selectedDay = -1; - } else { - // check again - if (selectedDay > -1 && pos != selectedDay) { - time[pos][0] = time[selectedDay][0]; - time[pos][1] = time[selectedDay][1]; - } - if (time[pos][0] < 0) { - time[pos][0] = 8 * 60; - } - if (time[pos][1] < 0) { - time[pos][1] = 20 * 60; - } - selectedDay = pos; - } - } - timeText.setText(OpeningHoursParser.toStringOpenedHours(time)); - updateTimePickers(); - - } - - }); - } - - // init - timePickerEnd.setIs24HourView(true); timePickerStart.setIs24HourView(true); timePickerStart.setCurrentHour(8); timePickerStart.setCurrentMinute(0); timePickerEnd.setCurrentHour(20); timePickerEnd.setCurrentMinute(0); - timeText.setText(OpeningHoursParser.toStringOpenedHours(time)); - - + timePickerEnd.setOnTimeChangedListener(onTimeChangedListener); timePickerStart.setOnTimeChangedListener(onTimeChangedListener); + updateTimePickers(); + return view; } + + private class TimeAdapter extends ArrayAdapter { - public void updateTimePickers(){ - if(selectedDay > -1){ - notifyingTime = false; - timePickerStart.setCurrentHour(time[selectedDay][0] / 60); - timePickerStart.setCurrentMinute(time[selectedDay][0] % 60); - timePickerEnd.setCurrentHour(time[selectedDay][1] / 60); - timePickerEnd.setCurrentMinute(time[selectedDay][1] % 60); - notifyingTime = true; + public TimeAdapter(List l ){ + super(ctx, R.layout.open_hours_list_item, l); + } + public View getView(final int position, View convertView, ViewGroup parent) { + View row = convertView; + final BasicDayOpeningHourRule item = getItem(position); + if(item == null){ + TextView text = new TextView(getContext()); + text.setText(ctx.getString(R.string.add_new_rule)); + text.setTextSize(21); + text.setTypeface(null, Typeface.ITALIC); + text.setOnClickListener(new View.OnClickListener(){ + + @Override + public void onClick(View v) { + BasicDayOpeningHourRule r = new BasicDayOpeningHourRule(); + r.setStartTime(9*60); + r.setEndTime(20*60); + boolean[] days = r.getDays(); + Arrays.fill(days, true); + showDaysDialog(r, position); + } + + }); + return text; + } + if(row == null || row instanceof TextView){ + LayoutInflater inflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + row = inflater.inflate(R.layout.open_hours_list_item, parent, false); + } + TextView label = (TextView)row.findViewById(R.id.label); + ImageView icon = (ImageView)row.findViewById(R.id.remove); + if(selectedRule == position){ + label.setTypeface(null, Typeface.BOLD); + label.setTextSize(22); + } else { + label.setTypeface(null); + label.setTextSize(20); + } + + label.setText(item.toRuleString()); + icon.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View v) { + time.remove(item); + selectedRule = time.getPosition(null); + updateTimePickers(); + } + + }); + View.OnClickListener clickListener = new View.OnClickListener(){ + @Override + public void onClick(View v) { + if(selectedRule == position){ + showDaysDialog(item, -1); + } else { + selectedRule = position; + updateTimePickers(); + time.notifyDataSetChanged(); + } + } + + }; + label.setOnClickListener(clickListener); + return row; } } - public int[][] getTime() { - return time; + public void showDaysDialog(final BasicDayOpeningHourRule item, final int positionToAdd) { + Builder b = new AlertDialog.Builder(ctx); + + boolean add = positionToAdd > -1; + Calendar inst = Calendar.getInstance(); + final int first = inst.getFirstDayOfWeek(); + final boolean[] dayToShow = new boolean[7]; + String[] daysToShow = new String[7]; + for (int i = 0; i < 7; i++) { + int d = (first + i - 1) % 7 + 1; + inst.set(Calendar.DAY_OF_WEEK, d); + daysToShow[i] = DateFormat.format("EEEE", inst).toString(); //$NON-NLS-1$ + final int pos = (d + 5) % 7; + dayToShow[i] = item.getDays()[pos]; + } + b.setMultiChoiceItems(daysToShow, dayToShow, new OnMultiChoiceClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + dayToShow[which] = isChecked; + + } + + }); + b.setPositiveButton(add ? ctx.getString(R.string.default_buttons_add) : ctx.getString(R.string.default_buttons_apply), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + boolean[] days = item.getDays(); + for (int i = 0; i < 7; i++) { + days[(first + 5 + i) % 7] = dayToShow[i]; + } + if (positionToAdd != -1) { + time.insert(item, positionToAdd); + selectedRule = positionToAdd; + } else { + time.notifyDataSetChanged(); + } + updateTimePickers(); + + } + + }); + + b.setNegativeButton(ctx.getString(R.string.default_buttons_cancel), null); + + b.show(); + + } + + public void updateTimePickers() { + if (selectedRule > -1) { + BasicDayOpeningHourRule item = time.getItem(selectedRule); + if (item != null) { + notifyingTime = false; + timePickerStart.setCurrentHour(item.getStartTime() / 60); + timePickerStart.setCurrentMinute(item.getStartTime() % 60); + timePickerEnd.setCurrentHour(item.getEndTime() / 60); + timePickerEnd.setCurrentMinute(item.getEndTime() % 60); + notifyingTime = true; + } + } + } + + public List getTime() { + List rules = new ArrayList(); + for (int i = 0; i < time.getCount(); i++) { + BasicDayOpeningHourRule r = time.getItem(i); + if (r != null) { + rules.add(r); + } + } + return rules; } } diff --git a/OsmAnd/src/com/osmand/activities/search/SearchPOIActivity.java b/OsmAnd/src/com/osmand/activities/search/SearchPOIActivity.java index b20c739096..daaaecb324 100644 --- a/OsmAnd/src/com/osmand/activities/search/SearchPOIActivity.java +++ b/OsmAnd/src/com/osmand/activities/search/SearchPOIActivity.java @@ -4,6 +4,7 @@ package com.osmand.activities.search; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; import android.app.ListActivity; @@ -29,6 +30,8 @@ import com.osmand.activities.MapActivity; import com.osmand.data.Amenity; import com.osmand.osm.LatLon; import com.osmand.osm.MapUtils; +import com.osmand.osm.OpeningHoursParser; +import com.osmand.osm.OpeningHoursParser.OpeningHoursRule; /** * @author Maxim Frolov @@ -159,9 +162,28 @@ public class SearchPOIActivity extends ListActivity { String str = amenity.getStringWithoutType(OsmandSettings.usingEnglishNames(SearchPOIActivity.this)); label.setText(str); if (amenity.getOpeningHours() != null) { - icon.setImageResource(R.drawable.poi); + List rs = OpeningHoursParser.parseOpenedHours(amenity.getOpeningHours()); + if(rs == null){ + icon.setImageResource(R.drawable.poi); + } else { + Calendar inst = Calendar.getInstance(); + inst.setTimeInMillis(System.currentTimeMillis()); + boolean work = false; + for(OpeningHoursRule p : rs){ + if(p.isOpenedForTime(inst)){ + work = true; + break; + } + } + if(work){ + icon.setImageResource(R.drawable.opened_poi); + } else { + icon.setImageResource(R.drawable.closed_poi); + } + } + } else { - icon.setImageResource(R.drawable.closed_poi); + icon.setImageResource(R.drawable.poi); } distanceLabel.setText(" " + MapUtils.getFormattedDistance(dist)); //$NON-NLS-1$ diff --git a/OsmAnd/src/com/osmand/activities/search/SearchTransportActivity.java b/OsmAnd/src/com/osmand/activities/search/SearchTransportActivity.java index 8351a50520..c0ad7f1a69 100644 --- a/OsmAnd/src/com/osmand/activities/search/SearchTransportActivity.java +++ b/OsmAnd/src/com/osmand/activities/search/SearchTransportActivity.java @@ -434,9 +434,9 @@ public class SearchTransportActivity extends ListActivity { label.setText(labelW.toString()); // TODO icons if (locationToGo != null && stop.getDistToLocation() < 400) { - icon.setImageResource(R.drawable.poi); + icon.setImageResource(R.drawable.opened_poi); } else { - icon.setImageResource(R.drawable.closed_poi); + icon.setImageResource(R.drawable.poi); } int dist = locationToStart == null ? 0 : (int) (MapUtils.getDistance(stop.getStart().getLocation(), locationToStart)); diff --git a/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java b/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java index f222e76dd1..677bde7871 100644 --- a/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java +++ b/OsmAnd/src/com/osmand/views/AnimateDraggingMapThread.java @@ -67,10 +67,16 @@ public class AnimateDraggingMapThread implements Runnable { while(currentThread != null){} } - public void startDragging(float dTime, float startX, float startY, float endX, float endY){ - stopDraggingSync(); + public void startDragging(float dTime, float startX, float startY, float endX, float endY){ vx = Math.abs((endX - startX)/dTime); vy = Math.abs((endY - startY)/dTime); + startDragging(vx, vy, startX, startY, endX, endY); + } + + public void startDragging(float velocityX, float velocityY, float startX, float startY, float endX, float endY){ + stopDraggingSync(); + vx = velocityX; + vy = velocityY; dirX = (byte) (endX > startX ? 1 : -1); dirY = (byte) (endY > startY ? 1 : -1); ax = vx * a; diff --git a/OsmAnd/src/com/osmand/views/OsmandMapTileView.java b/OsmAnd/src/com/osmand/views/OsmandMapTileView.java index 021e042d97..01c27d4c09 100644 --- a/OsmAnd/src/com/osmand/views/OsmandMapTileView.java +++ b/OsmAnd/src/com/osmand/views/OsmandMapTileView.java @@ -19,14 +19,14 @@ import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.FloatMath; +import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; -import android.view.View; +import android.view.GestureDetector.OnDoubleTapListener; +import android.view.GestureDetector.OnGestureListener; import android.view.SurfaceHolder.Callback; -import android.view.View.OnClickListener; -import android.view.View.OnLongClickListener; import com.osmand.LogUtil; import com.osmand.OsmandSettings; @@ -40,7 +40,7 @@ import com.osmand.osm.MapUtils; import com.osmand.views.AnimateDraggingMapThread.AnimateDraggingCallback; public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, - Callback, AnimateDraggingCallback, OnLongClickListener, OnClickListener{ + Callback, AnimateDraggingCallback, OnGestureListener, OnDoubleTapListener { protected final int emptyTileDivisor = 16; protected final int timeForDraggingAnimation = 300; @@ -92,10 +92,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall private AnimateDraggingMapThread animatedDraggingThread; - private PointF startDragging = null; - private PointF autoStartDragging = null; - private long autoStartDraggingTime = 0; - private boolean wasMapDraggedOnTouch = false; + private GestureDetector gestureDetector; Paint paintGrayFill; Paint paintWhiteFill; @@ -140,13 +137,13 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall setClickable(true); setLongClickable(true); setFocusable(true); - super.setOnLongClickListener(this); - super.setOnClickListener(this); getHolder().addCallback(this); animatedDraggingThread = new AnimateDraggingMapThread(); animatedDraggingThread.setCallback(this); + gestureDetector = new GestureDetector(getContext(), this); + gestureDetector.setOnDoubleTapListener(this); } @@ -594,79 +591,39 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } - public boolean wasMapDraggingAccelerated(MotionEvent event){ - if(autoStartDragging == null){ - return false; - } - if(event.getEventTime() - autoStartDraggingTime < timeForDraggingAnimation){ - float dist = Math.abs(event.getX() - autoStartDragging.x) + Math.abs(event.getY() - autoStartDragging.y); - if(dist > minimumDistanceForDraggingAnimation){ - return true; - } - } - return false; - } - - public boolean isDifferenceSmallToDrag(PointF point, MotionEvent event){ - return Math.abs(point.x - event.getX()) <= 8 && Math.abs(point.y - event.getY()) <= 8; - } - - @Override public boolean onTouchEvent(MotionEvent event) { - if(event.getAction() == MotionEvent.ACTION_DOWN) { - animatedDraggingThread.stopDragging(); - // possibly solution to always reset start dragging ? (in order someone forget to do it) - if(startDragging == null){ - autoStartDragging = new PointF(event.getX(), event.getY()); - autoStartDraggingTime = event.getEventTime(); - startDragging = new PointF(event.getX(), event.getY()); - wasMapDraggedOnTouch = false; - } - // registering long press action - return super.onTouchEvent(event); - } else if(event.getAction() == MotionEvent.ACTION_UP) { - if(startDragging != null){ - if (wasMapDraggedOnTouch || !isDifferenceSmallToDrag(startDragging, event)) { - dragTo(startDragging.x, startDragging.y, event.getX(), event.getY()); - startDragging = null; - if (wasMapDraggingAccelerated(event)) { - float timeDist = (int) (event.getEventTime() - autoStartDraggingTime); - if (timeDist < 20) { - timeDist = 20; - } - animatedDraggingThread.startDragging(timeDist, autoStartDragging.x, autoStartDragging.y, event.getX(), event.getY()); - } - } else { - // Do not forget to reset startDragging = null in performClick() - // give chance to run performClick() or performLongClick() - return super.onTouchEvent(event); - } - } - } else if(event.getAction() == MotionEvent.ACTION_MOVE) { - if(startDragging != null && !isDifferenceSmallToDrag(startDragging, event)){ - // try to not drag map if that first time & diff small - if (wasMapDraggedOnTouch || !isDifferenceSmallToDrag(startDragging, event)) { - if(!wasMapDraggedOnTouch){ - cancelLongPress(); - wasMapDraggedOnTouch = true; - } - dragTo(startDragging.x, startDragging.y, event.getX(), event.getY()); - // save memory do not create new PointF - startDragging.x = event.getX(); - startDragging.y = event.getY(); - if (event.getEventTime() - autoStartDraggingTime > timeForDraggingAnimation) { - autoStartDraggingTime = event.getEventTime(); - autoStartDragging.x = event.getX(); - autoStartDragging.y = event.getY(); - } - } - } - } - +// dumpEvent(event); + /*return */ gestureDetector.onTouchEvent(event); return true; } + private void dumpEvent(MotionEvent event) { + String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" , + "POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" }; + StringBuilder sb = new StringBuilder(); + int action = event.getAction(); + int actionCode = action & 255; + sb.append("event ACTION_" ).append(names[actionCode]); + if (actionCode == 5 + || actionCode == 6) { + sb.append("(pid " ).append(action >> 8); + sb.append(")" ); + } + sb.append("[" ); +// for (int i = 0; i < event.getPointerCount(); i++) { +// sb.append("#" ).append(i); +// sb.append("(pid " ).append(event.getPointerId(i)); +// sb.append(")=" ).append((int) event.getX(i)); +// sb.append("," ).append((int) event.getY(i)); +// if (i + 1 < event.getPointerCount()) +// sb.append(";" ); +// } + sb.append("]" ); + android.util.Log.d("com.osmand", sb.toString()); + + } + @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if(trackBallDelegate != null && keyCode == KeyEvent.KEYCODE_DPAD_CENTER){ @@ -687,26 +644,6 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall this.trackBallDelegate = trackBallDelegate; } - @Override - public boolean onLongClick(View v) { - PointF point = startDragging; - if (point != null) { - startDragging = null; - if(log.isDebugEnabled()){ - log.debug("On long click event "+ point.x + " " + point.y); //$NON-NLS-1$ //$NON-NLS-2$ - } - for (int i = layers.size() - 1; i >= 0; i--) { - if (layers.get(i).onLongPressEvent(point)) { - return true; - } - } - if(onLongClickListener != null && onLongClickListener.onLongPressEvent(point)){ - return true; - } - } - - return false; - } public void setOnLongClickListener(OnLongClickListener l) { this.onLongClickListener = l; @@ -716,23 +653,83 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall this.onClickListener = l; } + @Override - public void onClick(View v) { - PointF point = startDragging; - if (point != null) { - startDragging = null; - if(log.isDebugEnabled()){ - log.debug("On click event "+ point.x + " " + point.y); //$NON-NLS-1$ //$NON-NLS-2$ - } - for (int i = layers.size() - 1; i >= 0; i--) { - if (layers.get(i).onTouchEvent(point)) { - return; - } - } - if(onClickListener != null && onClickListener.onPressEvent(point)){ + public boolean onDown(MotionEvent e) { + animatedDraggingThread.stopDragging(); + return false; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + animatedDraggingThread.startDragging(Math.abs(velocityX/1000), Math.abs(velocityY/1000), e1.getX(), e1.getY(), e2.getX(), e2.getY()); + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + if(log.isDebugEnabled()){ + log.debug("On long click event "+ e.getX() + " " + e.getY()); //$NON-NLS-1$ //$NON-NLS-2$ + } + PointF point = new PointF(e.getX(), e.getY()); + for (int i = layers.size() - 1; i >= 0; i--) { + if (layers.get(i).onLongPressEvent(point)) { return; } } + if(onLongClickListener != null && onLongClickListener.onLongPressEvent(point)){ + return; + } + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + dragTo(e2.getX() + distanceX, e2.getY() + distanceY, e2.getX(), e2.getY()); + return true; + } + + @Override + public void onShowPress(MotionEvent e) { + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + PointF point = new PointF(e.getX(), e.getY()); + if(log.isDebugEnabled()){ + log.debug("On click event "+ point.x + " " + point.y); //$NON-NLS-1$ //$NON-NLS-2$ + } + for (int i = layers.size() - 1; i >= 0; i--) { + if (layers.get(i).onTouchEvent(point)) { + return true; + } + } + if(onClickListener != null && onClickListener.onPressEvent(point)){ + return true; + } + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + float dx = e.getX() - getCenterPointX(); + float dy = e.getY() - getCenterPointY(); + float fy = calcDiffTileY(dx, dy); + float fx = calcDiffTileX(dx, dy); + double latitude = MapUtils.getLatitudeFromTile(getZoom(), getYTile() + fy); + double longitude = MapUtils.getLongitudeFromTile(getZoom(), getXTile() + fx); + setLatLon(latitude, longitude); + setZoom(zoom + 1); + return true; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + return false; + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + return false; } diff --git a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java index 8ee1e6f5cc..53877175d9 100644 --- a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java +++ b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java @@ -148,8 +148,11 @@ public class TransportStopsLayer implements OsmandMapLayer { @Override public boolean onLongPressEvent(PointF point) { - view.getContext().startActivity(new Intent(view.getContext(), SearchTransportActivity.class)); - return true; + if(getFromPoint(point) != null){ + view.getContext().startActivity(new Intent(view.getContext(), SearchTransportActivity.class)); + return true; + } + return false; } }