package net.osmand.plus.routepointsnavigation; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.UUID; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.plus.ApplicationMode; import net.osmand.plus.GPXUtilities; import net.osmand.plus.GPXUtilities.GPXFile; import net.osmand.plus.GPXUtilities.Route; import net.osmand.plus.GPXUtilities.WptPt; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; 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.views.MapInfoLayer; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.mapwidgets.TextInfoWidget; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.text.format.DateFormat; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.FrameLayout; import android.widget.TextView; /** * Created by Barsik on 10.06.2014. */ public class RoutePointsPlugin extends OsmandPlugin { public static final String ID = "osmand.route.stepsPlugin"; public static final String ROUTE_POINTS_PLUGIN_COMPONENT = "net.osmand.routePointsPlugin"; private static final String VISITED_KEY = "VISITED_KEY"; private static final String DELIVERED_KEY = "DELIVERED_KEY"; private OsmandApplication app; private TextInfoWidget routeStepsControl; private SelectedRouteGpxFile currentRoute; private MapActivity mapActivity; private RoutePointsLayer routePointsLayer; public RoutePointsPlugin(OsmandApplication app) { ApplicationMode.regWidgetVisibility("route_steps", ApplicationMode.CAR, ApplicationMode.DEFAULT); this.app = app; } public SelectedRouteGpxFile getCurrentRoute() { return currentRoute; } @Override public int getLogoResourceId() { // TODO return super.getLogoResourceId(); } @Override public int getAssetResourceName() { return R.drawable.trip_recording; } public void setCurrentRoute(GPXFile gpx) { if (gpx == null) { currentRoute = null; } else { currentRoute = new SelectedRouteGpxFile(gpx); } } private View createDeliveredView(RoutePoint point) { final LayoutInflater vi = (LayoutInflater) app.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View deliveredView = vi.inflate(R.layout.package_delivered, null); TextView name = (TextView) deliveredView.findViewById(R.id.point_name); name.setText(point.getName()); TextView id = (TextView) deliveredView.findViewById(R.id.point_id); id.setText(point.id.toString()); Button btnY = (Button) deliveredView.findViewById(R.id.delivered_yes); btnY.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { setPointDelivered(view, true); } }); Button btnN = (Button) deliveredView.findViewById(R.id.delivered_no); btnN.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { setPointDelivered(view, false); } }); return deliveredView; } private void setPointDelivered(View child, boolean delivered) { if (child == null || child.getParent() == null) { return; } View parent = (View) child.getParent().getParent(); if (parent == null) { return; } TextView id = (TextView) parent.findViewById(R.id.point_id); if (id != null) { RoutePoint point = getPointById(UUID.fromString(id.getText().toString())); if (point != null) { point.setDelivered(delivered); } } FrameLayout layout = (FrameLayout) mapActivity.getLayout(); if (layout != null) { layout.removeView(parent); } } private RoutePoint getPointById(UUID id) { if (currentRoute == null) { return null; } for (RoutePoint p : currentRoute.currentPoints) { if (p.id.compareTo(id) == 0) { return p; } } return null; } @Override public boolean destinationReached() { if (currentRoute != null) { //Check EVERYTHING if (currentRoute.currentPoints != null && currentRoute.currentPoints.size() > 0 && currentRoute.currentPoints.get(0).isNextNavigate) { FrameLayout layout = (FrameLayout) mapActivity.getLayout(); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; View deliveredView = createDeliveredView(currentRoute.currentPoints.get(0)); if (deliveredView != null) { layout.addView(deliveredView, params); } } //if it's possible to navigate to next point - navigation continues return !currentRoute.navigateToNextPoint(); } return true; } @Override public String getId() { return ID; } @Override public String getDescription() { return app.getString(R.string.route_plugin_descr); } @Override public String getName() { return app.getString(R.string.route_plugin_name); } private void registerWidget(MapActivity activity) { MapInfoLayer mapInfoLayer = activity.getMapLayers().getMapInfoLayer(); if (mapInfoLayer != null) { routeStepsControl = createRouteStepsInfoControl(activity); mapInfoLayer.registerSideWidget(routeStepsControl, R.drawable.ic_action_signpost_dark, R.string.map_widget_route_points, "route_steps", false, 12); mapInfoLayer.recreateControls(); } } @Override public void registerLayers(MapActivity activity) { super.registerLayers(activity); mapActivity = activity; if (routePointsLayer != null) { activity.getMapView().removeLayer(routePointsLayer); } routePointsLayer = new RoutePointsLayer(activity, this); activity.getMapView().addLayer(routePointsLayer, 5.5f); registerWidget(activity); } @Override public void updateLayers(OsmandMapTileView mapView, MapActivity activity) { if (routePointsLayer == null) { registerLayers(activity); } if (routeStepsControl == null) { registerWidget(activity); } } public String getVisitedAllString() { if (currentRoute != null) { return String.valueOf(currentRoute.getVisitedCount()) + "/" + String.valueOf(currentRoute.getCount()); } else { return app.getString(R.string.route_points_no_gpx); } } public void saveCurrentRoute() { if (currentRoute != null) { currentRoute.saveGPXAsync(); } } private TextInfoWidget createRouteStepsInfoControl(final MapActivity map) { TextInfoWidget routeStepsControl = new TextInfoWidget(map) { @Override() public boolean updateInfo(OsmandMapLayer.DrawSettings drawSettings) { if (currentRoute != null) { setText(getVisitedAllString(), ""); } else { setText("", app.getString(R.string.route_points_no_gpx)); } return true; } }; routeStepsControl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // FavouritesDbHelper fp = map.getMyApplication().getFavorites(); // app.getTargetPointsHelper().addVisibleLocationPoint(fp.getFavouritePoints().get(new Random().nextInt(fp.getFavouritePoints().size()))); Intent intent = new Intent(app, RoutePointsActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); app.startActivity(intent); } }); routeStepsControl.setText(null, null); routeStepsControl.setImageDrawable(R.drawable.widget_signpost); return routeStepsControl; } public class RoutePoint { boolean isNextNavigate; int gpxOrder; long visitedTime = 0; // 0 not visited WptPt wpt; boolean delivered; public UUID id; public String getName() { return wpt.name == null ? "" : wpt.name; } public WptPt getWpt() { return wpt; } public boolean isNextNavigate() { return isNextNavigate; } public boolean isVisited() { return visitedTime != 0; } public boolean isDelivered() { return delivered; } public int getGpxOrder() { return gpxOrder; } public RoutePoint(WptPt point) { id = UUID.randomUUID(); this.wpt = point; if (wpt != null) { String delivered = wpt.getExtensionsToRead().get(DELIVERED_KEY); this.delivered = Boolean.parseBoolean(delivered); String time = wpt.getExtensionsToRead().get(VISITED_KEY); try { visitedTime = Long.parseLong(time); } catch (NumberFormatException e) { } } } public String getDistance(RoutePoint rp) { double d = MapUtils.getDistance(rp.getPoint(), getPoint()); return OsmAndFormatter.getFormattedDistance((float) d, app); } public String getTime() { if (visitedTime == 0) { return ""; } String dateString; Date date = new Date(visitedTime); if (DateFormat.is24HourFormat(app)) { dateString = DateFormat.format("MM/dd k:mm", date).toString(); } else { dateString = DateFormat.format("MM/dd h:mm", date).toString() + DateFormat.format("aa", date).toString(); } return dateString; } public LatLon getPoint() { return new LatLon(wpt.lat, wpt.lon); } public void setDelivered(boolean d) { wpt.getExtensionsToWrite().put(DELIVERED_KEY, String.valueOf(d)); this.delivered = d; saveCurrentRoute(); } public void setVisitedTime(long currentTimeMillis) { visitedTime = currentTimeMillis; wpt.getExtensionsToWrite().put(VISITED_KEY, visitedTime + ""); saveCurrentRoute(); } } public class SelectedRouteGpxFile { private GPXUtilities.GPXFile gpx; private List<RoutePoint> currentPoints = new ArrayList<RoutePointsPlugin.RoutePoint>(); public SelectedRouteGpxFile(GPXUtilities.GPXFile gpx) { this.gpx = gpx; parseGPXFile(gpx); } public List<RoutePoint> getCurrentPoints() { return currentPoints; } public int getVisitedCount() { int k = 0; for (RoutePoint rp : currentPoints) { if (rp.isVisited()) { k++; } } return k; } public int getCount() { return currentPoints.size(); } public GPXUtilities.Route getRoute() { if (gpx.routes.isEmpty()) { return null; } return gpx.routes.get(0); } public String saveFile() { return GPXUtilities.writeGpxFile(new File(gpx.path), gpx, app); } public void markPoint(RoutePoint point, boolean visited) { if (point.isNextNavigate() && visited) { navigateToNextPoint(); return; } if (visited) { point.setVisitedTime(System.currentTimeMillis()); } else { point.setVisitedTime(0); } sortPoints(); } public boolean navigateToNextPoint() { if (currentPoints.isEmpty()) { return false; } RoutePoint rp = currentPoints.get(0); if (rp.isNextNavigate) { rp.setVisitedTime(System.currentTimeMillis()); rp.isNextNavigate = false; sortPoints(); } RoutePoint first = currentPoints.get(0); if (!first.isVisited()) { app.getTargetPointsHelper().navigateToPoint(first.getPoint(), true, -1, new PointDescription(PointDescription.POINT_TYPE_WPT, first.getName())); first.isNextNavigate = true; return true; } else { app.getTargetPointsHelper().clearPointToNavigate(true); } return false; } private void sortPoints() { Collections.sort(currentPoints, new Comparator<RoutePoint>() { @Override public int compare(RoutePoint lhs, RoutePoint rhs) { if (lhs.isNextNavigate || rhs.isNextNavigate) { return lhs.isNextNavigate ? -1 : 1; } if (!lhs.isVisited() || !rhs.isVisited()) { if (lhs.isVisited()) { return 1; } if (rhs.isVisited()) { return -1; } return lcompare(lhs.gpxOrder, rhs.gpxOrder); } return -lcompare(lhs.visitedTime, rhs.visitedTime); } public int lcompare(long lhs, long rhs) { return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1); } }); } private void parseGPXFile(GPXFile gpx) { this.gpx = gpx; Route rt = getRoute(); currentPoints.clear(); if (rt != null) { TargetPointsHelper targetPointsHelper = app.getTargetPointsHelper(); TargetPoint pointToNavigate = targetPointsHelper.getPointToNavigate(); String locName = pointToNavigate == null ? null : pointToNavigate.getOnlyName(); for (int i = 0; i < rt.points.size(); i++) { WptPt wptPt = rt.points.get(i); RoutePoint rtp = new RoutePoint(wptPt); rtp.gpxOrder = i; rtp.isNextNavigate = rtp.visitedTime == 0 && locName != null && locName.equals(wptPt.name); if (rtp.isNextNavigate) { locName = null; } currentPoints.add(rtp); } sortPoints(); } } public String getName() { return gpx.path.substring(gpx.path.lastIndexOf("/") + 1, gpx.path.lastIndexOf(".")); } public void navigateToPoint(RoutePoint rp) { if (!currentPoints.isEmpty()) { if (currentPoints.get(0).isNextNavigate()) { currentPoints.get(0).isNextNavigate = false; } } rp.isNextNavigate = true; sortPoints(); app.getTargetPointsHelper().navigateToPoint(rp.getPoint(), true, -1, new PointDescription(PointDescription.POINT_TYPE_WPT, rp.getName())); } public void updateCurrentTargetPoint() { TargetPointsHelper targetPointsHelper = app.getTargetPointsHelper(); TargetPoint tp = targetPointsHelper.getPointToNavigate(); for (int i = 0; i < currentPoints.size(); i++) { RoutePoint rtp = currentPoints.get(i); rtp.isNextNavigate = rtp.visitedTime == 0 && tp != null && !Algorithms.isEmpty(tp.getOnlyName()) && tp.getOnlyName().equals(rtp.getName()); } sortPoints(); } public boolean getPointStatus(WptPt p) { RoutePoint point = getRoutePointFromWpt(p); return point != null && (point.isVisited()); } public void markPoint(WptPt point, boolean visited) { RoutePoint routePoint = getRoutePointFromWpt(point); if (routePoint != null) { markPoint(routePoint, visited); } } public void navigateToPoint(WptPt point) { RoutePoint routePoint = getRoutePointFromWpt(point); if (routePoint != null) { navigateToPoint(routePoint); } } public RoutePoint getRoutePointFromWpt(WptPt point) { if (currentPoints != null) { for (RoutePoint find : currentPoints) { WptPt itemToFind = find.getWpt(); if (itemToFind.equals(point)) { return find; } } } return null; } public void saveGPXAsync() { new AsyncTask<RoutePointsPlugin.SelectedRouteGpxFile, Void, Void>() { @Override protected Void doInBackground(RoutePointsPlugin.SelectedRouteGpxFile... params) { saveFile(); return null; } }.execute(getCurrentRoute()); } } @Override public Class<? extends Activity> getSettingsActivity() { return null; } }