implement open hours editor

fix small issues

git-svn-id: https://osmand.googlecode.com/svn/trunk@287 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-07-06 06:19:15 +00:00
parent e372693c2e
commit b411d48e3a
18 changed files with 510 additions and 326 deletions

View file

@ -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 !
@ -62,6 +61,7 @@ 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)
// 50. Invent opening hours editor in order to edit POI hours better on device
// DONE SWING

View file

@ -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<OpeningHoursRule> parseOpenedHours(String format){
String[] rules = format.split(";"); //$NON-NLS-1$
List<OpeningHoursRule> rs = new ArrayList<OpeningHoursRule>();
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<Integer, List<Integer>> groups = new LinkedHashMap<Integer, List<Integer>>();
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<Integer>());
}
groups.get(uniqueInt).add(k);
}
}
public static String toStringOpenedHours(List<? extends OpeningHoursRule> 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<Integer> 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<OpeningHoursRule> 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));
}

View file

@ -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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,27 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
<LinearLayout android:layout_width="wrap_content" android:layout_height="fill_parent" android:orientation="vertical">
<CheckBox android:text="" android:id="@+id/Day1" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
<CheckBox android:text="" android:id="@+id/Day2" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
<CheckBox android:text="" android:id="@+id/Day3" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
<CheckBox android:text="" android:id="@+id/Day4" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
<CheckBox android:text="" android:id="@+id/Day5" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
<CheckBox android:text="" android:id="@+id/Day6" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
<CheckBox android:text="" android:id="@+id/Day7" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
</LinearLayout>
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:gravity="center" android:layout_marginLeft="5dp">
<TextView android:text="" android:id="@+id/TimeText" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<!-- <ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> -->
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
<ListView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/ListView" />
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp">
<TimePicker android:id="@+id/TimePickerStart" android:layout_width="wrap_content" android:layout_height="wrap_content"></TimePicker>
<TimePicker android:id="@+id/TimePickerEnd" android:layout_width="wrap_content" android:layout_height="wrap_content"></TimePicker>
<TimePicker android:layout_marginLeft="5dp" android:id="@+id/TimePickerEnd" android:layout_width="wrap_content" android:layout_height="wrap_content"></TimePicker>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:id="@+id/label" android:layout_weight="1" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textSize="20px" />
<ImageButton android:id="@+id/remove" android:layout_width="wrap_content" android:background="@drawable/reset"
android:paddingLeft="2px" android:paddingRight="2px"
android:paddingTop="2px" android:layout_height="wrap_content" />
</LinearLayout>

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="opening_hours_not_supported">Формат времени работы не поддерживается для редактирования</string>
<string name="add_new_rule">Новое правило</string>
<string name="transport_Routes">Маршруты</string>
<string name="transport_Stop">Остановка</string>
<string name="transport_stops">остановок</string>

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="opening_hours_not_supported">Opening hours format is not supported for editing</string>
<string name="add_new_rule">Add new rule</string>
<string name="transport_Routes">Routes</string>
<string name="transport_Stop">Stop</string>
<string name="transport_stops">stops</string>

View file

@ -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<OpeningHoursRule> time = OpeningHoursParser.parseOpenedHours(openingHours.getText().toString());
List<BasicDayOpeningHourRule> simple = null;
if(time != null){
simple = new ArrayList<BasicDayOpeningHourRule>();
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) {

View file

@ -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()));

View file

@ -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<BasicDayOpeningHourRule> 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;
}
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;
private class TimeAdapter extends ArrayAdapter<BasicDayOpeningHourRule> {
public TimeAdapter(List<BasicDayOpeningHourRule> 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<OpeningHoursRule> getTime() {
List<OpeningHoursRule> rules = new ArrayList<OpeningHoursRule>();
for (int i = 0; i < time.getCount(); i++) {
BasicDayOpeningHourRule r = time.getItem(i);
if (r != null) {
rules.add(r);
}
}
return rules;
}
}

View file

@ -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<OpeningHoursRule> 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$

View file

@ -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));

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}
}