Added animation to follow a route. This way it is easier to test
the voice navigation. Animation menu item is visible when 'Allow mock location' is set in debug menu of android settings.
This commit is contained in:
parent
e0799857cb
commit
a2a1113791
6 changed files with 129 additions and 6 deletions
|
@ -10,6 +10,7 @@
|
||||||
<!-- not visible by default -->
|
<!-- not visible by default -->
|
||||||
<item android:id="@+id/map_navigate_to_point" android:title="@string/stop_navigation" android:visible="false" android:icon="@android:drawable/ic_menu_close_clear_cancel"></item>
|
<item android:id="@+id/map_navigate_to_point" android:title="@string/stop_navigation" android:visible="false" android:icon="@android:drawable/ic_menu_close_clear_cancel"></item>
|
||||||
<item android:id="@+id/map_mute" android:title="@string/menu_mute_off" android:visible="false"></item>
|
<item android:id="@+id/map_mute" android:title="@string/menu_mute_off" android:visible="false"></item>
|
||||||
|
<item android:id="@+id/map_animate_route" android:title="@string/animate_route" android:visible="false"></item>
|
||||||
|
|
||||||
<item android:id="@+id/map_get_directions" android:title="@string/get_directions" android:icon="@android:drawable/ic_menu_directions"></item>
|
<item android:id="@+id/map_get_directions" android:title="@string/get_directions" android:icon="@android:drawable/ic_menu_directions"></item>
|
||||||
<item android:title="@string/map_specify_point" android:id="@+id/map_specify_point" android:icon="@android:drawable/ic_menu_search"></item>
|
<item android:title="@string/map_specify_point" android:id="@+id/map_specify_point" android:icon="@android:drawable/ic_menu_search"></item>
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="animate_route_off">Animate off</string>
|
||||||
|
<string name="animate_route">Animate on</string>
|
||||||
|
|
||||||
<string name="local_index_upload_gpx_description">Upload GPX files to OSM community. They will be used to improve maps.</string>
|
<string name="local_index_upload_gpx_description">Upload GPX files to OSM community. They will be used to improve maps.</string>
|
||||||
<string name="local_index_items_uploaded">%1$d of %2$d item(s) successfully uploaded.</string>
|
<string name="local_index_items_uploaded">%1$d of %2$d item(s) successfully uploaded.</string>
|
||||||
<string name="local_index_mi_upload_gpx">Contribute to OSM...</string>
|
<string name="local_index_mi_upload_gpx">Contribute to OSM...</string>
|
||||||
|
|
34
OsmAnd/src/net/osmand/LatLonUtils.java
Normal file
34
OsmAnd/src/net/osmand/LatLonUtils.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package net.osmand;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
public class LatLonUtils {
|
||||||
|
|
||||||
|
public static Location middleLocation(Location start, Location end,
|
||||||
|
float meters) {
|
||||||
|
double lat1 = toRad(start.getLatitude());
|
||||||
|
double lon1 = toRad(start.getLongitude());
|
||||||
|
double R = 6371; // radius of earth in km
|
||||||
|
double d = meters / 1000; // in km
|
||||||
|
float brng = (float) (toRad(start.bearingTo(end)));
|
||||||
|
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(d / R)
|
||||||
|
+ Math.cos(lat1) * Math.sin(d / R) * Math.cos(brng));
|
||||||
|
double lon2 = lon1
|
||||||
|
+ Math.atan2(Math.sin(brng) * Math.sin(d / R) * Math.cos(lat1),
|
||||||
|
Math.cos(d / R) - Math.sin(lat1) * Math.sin(lat2));
|
||||||
|
Location nl = new Location(start);
|
||||||
|
nl.setLatitude(toDegree(lat2));
|
||||||
|
nl.setLongitude(toDegree(lon2));
|
||||||
|
nl.setBearing(brng);
|
||||||
|
return nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double toDegree(double radians) {
|
||||||
|
return radians * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double toRad(double degree) {
|
||||||
|
return degree * Math.PI / 180;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,12 +28,12 @@ import net.osmand.plus.views.OsmandMapTileView;
|
||||||
import net.osmand.plus.views.PointLocationLayer;
|
import net.osmand.plus.views.PointLocationLayer;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.AlertDialog.Builder;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.app.AlertDialog.Builder;
|
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
@ -58,6 +58,7 @@ import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.provider.Settings.Secure;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -65,8 +66,8 @@ import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.Window;
|
||||||
import android.view.animation.AccelerateInterpolator;
|
import android.view.animation.AccelerateInterpolator;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
import android.view.animation.Transformation;
|
import android.view.animation.Transformation;
|
||||||
|
@ -121,6 +122,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
|
||||||
// Store previous map rotation settings for rotate button
|
// Store previous map rotation settings for rotate button
|
||||||
private Integer previousMapRotate = null;
|
private Integer previousMapRotate = null;
|
||||||
|
|
||||||
|
private RouteAnimation routeAnimation = new RouteAnimation();
|
||||||
|
|
||||||
private boolean isMapLinkedToLocation = false;
|
private boolean isMapLinkedToLocation = false;
|
||||||
private ProgressDialog startProgressDialog;
|
private ProgressDialog startProgressDialog;
|
||||||
|
|
||||||
|
@ -436,6 +439,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
savingTrackHelper.close();
|
savingTrackHelper.close();
|
||||||
|
routeAnimation.close();
|
||||||
if(mNotificationManager != null){
|
if(mNotificationManager != null){
|
||||||
mNotificationManager.cancel(APP_NOTIFICATION_ID);
|
mNotificationManager.cancel(APP_NOTIFICATION_ID);
|
||||||
}
|
}
|
||||||
|
@ -948,6 +952,15 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
|
||||||
muteMenu.setVisible(false);
|
muteMenu.setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MenuItem animateMenu = menu.findItem(R.id.map_animate_route);
|
||||||
|
if (animateMenu != null) {
|
||||||
|
animateMenu.setTitle(routeAnimation.isRouteAnimating() ? R.string.animate_route_off
|
||||||
|
: R.string.animate_route);
|
||||||
|
animateMenu.setVisible("1".equals(Secure.getString(
|
||||||
|
getContentResolver(), Secure.ALLOW_MOCK_LOCATION))
|
||||||
|
&& settings.getPointToNavigate() != null
|
||||||
|
&& routingHelper.isRouteCalculated());
|
||||||
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,6 +1016,10 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
|
||||||
case R.id.map_show_point_options:
|
case R.id.map_show_point_options:
|
||||||
contextMenuPoint(mapView.getLatitude(), mapView.getLongitude());
|
contextMenuPoint(mapView.getLatitude(), mapView.getLongitude());
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.map_animate_route:
|
||||||
|
//animate moving on route
|
||||||
|
routeAnimation.startStopRouteAnimation(routingHelper, this);
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
@ -1104,9 +1121,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
ApplicationMode mode = getAppMode(buttons);
|
ApplicationMode mode = getAppMode(buttons);
|
||||||
Location location = new Location("map"); //$NON-NLS-1$
|
Location location = getLocationToStartFrom(lat, lon);
|
||||||
location.setLatitude(lat);
|
|
||||||
location.setLongitude(lon);
|
|
||||||
routingHelper.setAppMode(mode);
|
routingHelper.setAppMode(mode);
|
||||||
settings.FOLLOW_TO_THE_ROUTE.set(false);
|
settings.FOLLOW_TO_THE_ROUTE.set(false);
|
||||||
routingHelper.setFollowingMode(false);
|
routingHelper.setFollowingMode(false);
|
||||||
|
|
65
OsmAnd/src/net/osmand/plus/activities/RouteAnimation.java
Normal file
65
OsmAnd/src/net/osmand/plus/activities/RouteAnimation.java
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package net.osmand.plus.activities;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.osmand.LatLonUtils;
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
public class RouteAnimation {
|
||||||
|
|
||||||
|
private Thread routeAnimation;
|
||||||
|
|
||||||
|
public boolean isRouteAnimating() {
|
||||||
|
return routeAnimation != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startStopRouteAnimation(final RoutingHelper routingHelper,
|
||||||
|
final MapActivity ma) {
|
||||||
|
if (!isRouteAnimating()) {
|
||||||
|
routeAnimation = new Thread() {
|
||||||
|
public void run() {
|
||||||
|
final List<Location> directions = new ArrayList<Location>(
|
||||||
|
routingHelper.getCurrentRoute());
|
||||||
|
Location current = null;
|
||||||
|
float meters = 20.0f;
|
||||||
|
while (!directions.isEmpty() && routeAnimation != null) {
|
||||||
|
if (current == null) {
|
||||||
|
current = new Location(directions.remove(0));
|
||||||
|
} else {
|
||||||
|
if (current.distanceTo(directions.get(0)) > meters) {
|
||||||
|
current = LatLonUtils.middleLocation(current,
|
||||||
|
directions.get(0), meters);
|
||||||
|
} else {
|
||||||
|
current = new Location(directions.remove(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.setSpeed(meters);
|
||||||
|
current.setTime(System.currentTimeMillis());
|
||||||
|
ma.setLocation(current);
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RouteAnimation.this.stop();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
routeAnimation.start();
|
||||||
|
} else {
|
||||||
|
// stop the animation
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stop() {
|
||||||
|
routeAnimation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (isRouteAnimating()) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -117,13 +117,18 @@ public class RoutingHelper {
|
||||||
this.lastFixedLocation = null;
|
this.lastFixedLocation = null;
|
||||||
this.isFollowingMode = false;
|
this.isFollowingMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Location> getCurrentGPXRoute() {
|
public List<Location> getCurrentGPXRoute() {
|
||||||
return currentGPXRoute;
|
return currentGPXRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Location> getCurrentRoute() {
|
||||||
|
return currentGPXRoute == null || currentGPXRoute.isEmpty() ? Collections
|
||||||
|
.unmodifiableList(routeNodes) : Collections
|
||||||
|
.unmodifiableList(currentGPXRoute);
|
||||||
|
}
|
||||||
|
|
||||||
public void setAppMode(ApplicationMode mode){
|
public void setAppMode(ApplicationMode mode){
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
voiceRouter.updateAppMode();
|
voiceRouter.updateAppMode();
|
||||||
|
|
Loading…
Reference in a new issue