Android google maps v2 execute asynctask when user stops moving the camera - android

I am making an app which is using google maps api v2. My problem is that when I register camera change listener, it executes each time the camera moves not when the moving stops. I want to achieve the latter. How can I modify my code to register only when moving stops ?
Here is my code:
mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition position) {
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
ne = bounds.northeast;
sw = bounds.southwest;
ne1 = ne.latitude;
ne2 = ne.longitude;
sw1 = sw.latitude;
sw2 = sw.longitude;
new DownloadJSON().execute();
}
});

I implemented custom timer to execute after certain time only when conditions are met. Here is my code:
mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition position) {
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
ne = bounds.northeast;
sw = bounds.southwest;
if(t!=null){
t.purge();
t.cancel();
}
t = new Timer();
t.schedule(new TimerTask() {
public void run() {
if(ne1 != ne.latitude && ne2 != ne.longitude && sw1 != sw.latitude && sw2 != sw.longitude){
ne1 = ne.latitude;
ne2 = ne.longitude;
sw1 = sw.latitude;
sw2 = sw.longitude;
Log.d("Tag","Refreshing data");
new DownloadJSON().execute();
t.cancel();
}
else{
ne1 = ne.latitude;
ne2 = ne.longitude;
sw1 = sw.latitude;
sw2 = sw.longitude;}
t.cancel(); // also just top the timer thread, otherwise, you may receive a crash report
}
}, 1000);
//new DownloadJSON().execute();
}
});

I think you can not do this "oncamerachange" triggers an event at the end of the movement, but may trigger, or not, others during the movement.
Google docs :
" Called after the camera position has changed . During an animation , listener May not be Notified of intermediate camera positions . It is always called for the end position in the animation . "
And there are no listeners for start or end camera movement.
One way to do something similar, is to save the last point ( latlang ) where you loaded the json for the last time and every time there is a " oncamerachange " event, compare the current point to the previous point and decide if you want to reload the json again or not.
Edited to post workaround from google gmaps-api-issues.
Maybe this is what are you loocking for. From the discusion on google issues.
https://code.google.com/p/gmaps-api-issues/issues/attachmentText?id=4636&aid=46360026000&name=sample_inner_listener.java&token=ABZ6GAdaQesCaWQ_IU8t9J222IBYzPceUA%3A1431366784956
new GoogleMap.OnCameraChangeListener() {
private static int CAMERA_MOVE_REACT_THRESHOLD_MS = 500;
private long lastCallMs = Long.MIN_VALUE;
#Override
public void onCameraChange(CameraPosition cameraPosition) {
final long snap = System.currentTimeMillis();
if (lastCallMs + CAMERA_MOVE_REACT_THRESHOLD_MS > snap) {
lastCallMs = snap;
return;
}
;; // here the actual call whatever you want to do on camera change
lastCallMs = snap;
}
};
Comment from the author:
hi, I've attached a sample of anon listener wrapper implementation, you can play with CAMERA_MOVE_REACT_THRESHOLD_MS to evaluate best value for you, if its not enough, you could also add kinda autosensing though, which would adapt that threshold value to the onCameraChange() call frequency

Related

GIF type animation for marker in google map api ANDROID

I want to achieve a marker animation such as GIF animation. I got two images which should be blinking simultaneously. I found nothing which can acheive this in android. I am trying to do is , creating a handler which run every 1 second , and I am trying to set icon for marker. But it doesnt work. Please guide me in right direction.
my code as of now is as follows.
Handler handler = new Handler();
Boolean marker_color_bool = true;
//adding marker and sending the marker instance to marker_animation() method where handler is called.
MarkerOptions marker = new MarkerOptions()
.title(delivery_center_name)
.snippet("This is the " + delivery_center_name + " location")
.position(location)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.red_marker));
google_map.addMarker(marker);
marker_animation(marker);
marker_animation() method
private final int ONE_SECONDS = 1000;
public void marker_animation(final MarkerOptions marker ) {
handler.postDelayed(new Runnable() {
public void run() {
Log.e("running",""+marker_color_bool);
if(marker_color_bool == true)
{
marker.icon(BitmapDescriptorFactory.fromResource(R.drawable.green_marker));
marker_color_bool = false;
}
else
{
marker.icon(BitmapDescriptorFactory.fromResource(R.drawable.red_marker));
marker_color_bool = true;
}
handler.postDelayed(this, ONE_SECONDS);
}
}, ONE_SECONDS);
}
this approach doesnt work..Please help me what I am doing wrong.
Please help me what I am doing wrong
You are modifying an object that is no longer being used. Once addMarker() is called, the MarkerOptions object has no further meaning, yet this is what you are modifying via your postDelayed() logic.
(BTW, you don't need a Handler, as postDelayed() is available on any View)
addMarker() returns a Marker. You will need to work with that Marker to affect your changes, via setIcon().
Also, since your bitmaps are not changing, I suggest caching your two BitmapDescriptor objects, rather than re-creating them on every pass.

Skobbler: centerMapOnCurrentPosition() centers me in Aftrica even though blue dot is correct

I am using the skobbler sdk for maps.
I use this code to center map view on current position, but even though the blue dot is at my correct current position in Los Angeles (I verify by manually going there), the map centers me at gps(0,0).
public void onCurrentPositionUpdate(SKPosition currentPosition) {
this.currentPosition = currentPosition;
mapView.reportNewGPSPosition(this.currentPosition);
if(firstPositionUpdate){
firstPositionUpdate = false;
mapView.centerMapOnCurrentPosition();
}
}
and heres my code for initializing the map:
private void initializeMapView() {
currentPositionProvider = new SKCurrentPositionProvider(getActivity());
currentPositionProvider.setCurrentPositionListener(this);
if (DemoUtils.hasGpsModule(getActivity()) && ((LocationManager)getActivity().getSystemService(Context.LOCATION_SERVICE)).isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
currentPositionProvider.requestLocationUpdates(true, true, true);
}
SKMapViewHolder mapViewGroup = (SKMapViewHolder) getView().findViewById(R.id.map_surface_holder);
mapView = mapViewGroup.getMapSurfaceView();
mapView.setMapSurfaceListener(this);
mapView.getMapSettings().setFollowerMode(SKMapSettings.SKMapFollowerMode.NONE);
mapView.getMapSettings().setMapRotationEnabled(true);
mapView.getMapSettings().setMapZoomingEnabled(true);
mapView.getMapSettings().setMapPanningEnabled(true);
mapView.getMapSettings().setZoomWithAnchorEnabled(true);
mapView.getMapSettings().setInertiaRotatingEnabled(true);
mapView.getMapSettings().setInertiaZoomingEnabled(true);
mapView.getMapSettings().setInertiaPanningEnabled(true);
SKVersioningManager.getInstance().setMapUpdateListener(this);
mapView.centerMapOnPosition(new SKCoordinate( -118.123,34.123));
//launchRouteCalculation();
}
It seems that reportNewGPSPosition(this.currentPosition); does not work instantly.
I noticed that if I simply delay the call to centerMapOnCurrentPosition(), the map is centered correctly. There are two work arounds:
-set location manually:
mapView.centerMapOnPosition(new SKCoordinate( currentPosition.getLongitude(), currentPosition.getLatitude()));
-or create a delayed runnable to call to perform the centering at a future time:
android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mapView.centerMapOnCurrentPosition();
mapView.setZoom(10);
}
},100);
note: This issue only becomes evident because of the if statement I put in to only center the map on the first update of location. Another work around would be to put a use a counter and center map on the second time the location is updated.
Also if implementing this with the mentioned if statement, don't forget to consider accuracy; it would be bad to center when the location is not accurate, and not center on a future location update which IS accurate.

Track an user with location based App

I'm working on a Location based App. Here my requirement is I want to track a user location from my device like uber or Ly ft App.
Example: If a pizza guy(User B) wants to deliver pizza to me(User A) then he can share his user id, so using that id, I can enter in my device and see his exact precise location. But I want to track him until I required so how to achieve this in code. Help me with the architecture if you have come across such scenario.
I can also achieve the above scenario, but my doubt is how can I show in map without refreshing each time when the user moves say for example two minutes once I want to check the Latitude and Longitude and update it in my map for the same user
Create a Thread, that asks a Handler to get the position a few times. You need to use a Handler, because the getMyLocation method can only be called from GUI Thread:
private class MyLocationThread extends Thread{
#Override
public void run() {
int loops = 0;
// we give the location search a minute
while(loops < 60){
// we have to try it over a handler, because getMyLocation() has to be called from GUI Thread -_-
_getMyLocationHandler.sendEmptyMessage(0);
if(isInterrupted()){
return;
}
// take a short nap before next try
try {Thread.sleep(1000);} catch(Exception e){}
loops++;
}
}
}
Here's what the Handler does:
private Handler _getMyLocationHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
if(getMap().isMyLocationEnabled() && getMap().getMyLocation() != null){
_locationWatcher.interrupt();
drawCurrentImagePositions();
}
}
};

Android Google Maps v2 Camera Animation

So im not sure if this is a bug or not yet... might be or I may have missed something.
Anyway so here is the link to Google Maps V2 Camera Controls. https://developers.google.com/maps/documentation/android/views#moving_the_camera
The issue :
Animate to a location already animated to does not call onFinish();
How to replicate:
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mLocation.getLatLng(), zoomLevel), 200, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
//DO some stuff here!
Log.d("animation", "onFinishCalled");
}
#Override
public void onCancel() {
Log.d("animation", "onCancel");
}
});
This issue may well come about when a user double taps something which called the same animation even if there is a long time between, onFinish will only be called for a successful animation. When the camera is already positioned the onFinish method will not be called!
I could go around doing checks before I do any camera animation but I don't like that as its wasteful.
Any help would be appreciated.
Thanks.
I have the same problem when i want to move camera to the same position, it seems like a bug.
Even if old and new position are not the same and the difference is so small , ex: old position.latitude = 94.54284009112, new position.latitude = 94.54284003451, it dosen't work.
my solution is to truncate values to get only old_position.latitude = new_position.latitude = 94.54, then i do a test.
There is another problem with moving camera and scroll the map in the same time, for that i disable scroll gesture before moving and enable it on the onFinish() and the onCancel().
public void animateCameraTo(final double lat, final double lng)
{
_googleMap = getMap();
CameraPosition camPosition = _googleMap.getCameraPosition();
if (!((Math.floor(camPosition.target.latitude * 100) / 100) == (Math.floor(lat * 100) / 100) && (Math.floor(camPosition.target.longitude * 100) / 100) == (Math.floor(lng * 100) / 100)))
{
_googleMap.getUiSettings().setScrollGesturesEnabled(false);
_googleMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(lat, lng)), new CancelableCallback()
{
#Override
public void onFinish()
{
_googleMap.getUiSettings().setScrollGesturesEnabled(true);
}
#Override
public void onCancel()
{
_googleMap.getUiSettings().setAllGesturesEnabled(true);
}
});
}
}
Hope this helps you ;)
Your should check the pixel distance, not the geo distance:
LatLngBounds myBounds = YOUR_BOUNDS;
LatLngBounds visibleBounds = map.getProjection().getVisibleRegion().latLngBounds;
Point myCenter = map.getProjection().toScreenLocation(myBounds.getCenter());
Point visibleCenter = map.getProjection().toScreenLocation(visibleBounds.getCenter());
int dist = (int) Math.sqrt(Math.pow(myCenter.x - visibleCenter.x, 2) + Math.pow(myCenter.y - visibleCenter.y, 2));
if (dist > YOUR_THRESHOLD) {
map.animateCamera(CameraUpdateFactory.newLatLngBounds(myBounds, YOUR_PADDING), new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
// do something
}
#Override
public void onCancel() {
// do something
}
});
} else {
// do something
}
The only solution I have found is doing the wasteful pixel value checks, setting up a bogus (but close) CameraUpdate object and making a nested animateCamera() call using the bogus CameraUpdate first and then calling another cameraUpdate() inside the first onFinish() with the correct CameraUpdate. I installed the Google MAP v2 update today but it didn't help. This is a problem on device rotation for me. I am displaying bounding rectangles and the centroid doesn't change on rotation so the animateCamera() doesn't work and when the device is rotated the selected rectangle may be partially off the screen.
Did you find another solution yet?
Thanks
Here's another workaround:
You do know the animation duration of your map animations. So you can post a delayed runnable with a delay of the animation duration + some offset and there you can check if the finished/canceld listener was called... if not you can fetch it there and call the apropriate listener

knowing when map has stopped scrolling (like "moveend" in javascript API)

I need to detect when a MapView has been scrolled or zoomed, like the "moveend" event in the javascript API. I'd like to wait until the view has stopped moving, so I can then detect if I need to query my server for items withing the viewing rectangle, and if so send out a request. (actually I send a request for a slightly larger area than the viewing rectangle)
Obviously, I'd rather not send out a request for data if the view is still moving. But even worse is that I don't know that I need to send another request, leaving areas of the map missing markers.
Currently I am subclassing MapView and handling the onTouchEvent as follows:
public boolean onTouchEvent(android.view.MotionEvent ev) {
super.onTouchEvent (ev);
if (ev.getAction() == MotionEvent.ACTION_UP) {
GeoPoint center = getMapCenter();
int latSpan = getLatitudeSpan(), lngSpan = getLongitudeSpan();
/* (check if it has moved enough to need a new set of data) */
}
return true;
}
Problem is, I don't know if the view has stopped, since scrolling tends to have inertia and can keep going past the "ACTION_UP" event.
Is there some event I can tap into that will alert me when a mapview is done moving (or zooming)? If not, has anyone written logic to detect this? In theory I could make a guess by looking at all the actions, and set something to come along bit later and check it...but...that seems messy and a PITA. But if someone has already written it.... :)
This is the method I am using at the moment, I have used this and tested it, works well.
Just make sure you make your draw() method efficient. (Avoid GC in it).
//In map activity
class MyMapActivity extends MapActivity {
protected void onCreate(Bundle savedState){
setContent(R.layout.activity_map);
super.onCreate(savedSate);
OnMapMoveListener mapListener = new OnMapMoveListener(){
public void mapMovingFinishedEvent(){
Log.d("MapActivity", "Hey look! I stopped scrolling!");
}
}
// Create overlay
OnMoveOverlay mOnMoveOverlay = new OnMoveOverlay(mapListener);
// Add overlay to view.
MapView mapView = (MapView)findViewById(R.id.map_view);
// Make sure you add as the last overlay so its on the top.
// Otherwise other overlays could steal the touchEvent;
mapView.getOverlays().add(mOnMoveOverlay);
}
}
This is your OnMoveOverlay class
//OnMoveOverlay
class OnMoveOverlay extends Overlay
{
private static GeoPoint lastLatLon = new GeoPoint(0, 0);
private static GeoPoint currLatLon;
// Event listener to listen for map finished moving events
private OnMapMoveListener eventListener = null;
protected boolean isMapMoving = false;
public OnMoveOverlay(OnMapMoveListener eventLis){
//Set event listener
eventListener = eventLis;
}
#Override
public boolean onTouchEvent(android.view.MotionEvent ev)
{
super.onTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_UP)
{
// Added to example to make more complete
isMapMoving = true;
}
//Fix: changed to false as it would handle the touch event and not pass back.
return false;
}
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow)
{
if (!shadow)
{
if (isMapMoving)
{
currLatLon = mapView.getProjection().fromPixels(0, 0);
if (currLatLon.equals(lastLatLon))
{
isMapMoving = false;
eventListener.mapMovingFinishedEvent();
}
else
{
lastLatLon = currLatLon;
}
}
}
}
public interface OnMapMoveListener{
public void mapMovingFinishedEvent();
}
}
Just implement your own listener eventListener.mapMovingFinishedEvent(); and fire the map moving bool by another method like above and your sorted.
The idea is when the map is moving the pixel projection to the coords will be changing, once they are the same, you have finished moving.
I have updated this with newer more complete code, there was an issue with it double drawing.
We don't do anything on the shadow pass as we would just double calculate per draw pass which is a waste.
Feel Free to ask any questions :)
Thanks,
Chris
I had the same problem and "solved" it in a similar way, but I think less complicated:
As overriding computeScroll() didn't work for me, I overrode onTouchEvent, too. Then I used a Handler, that invokes a method call after 50ms, if the map center changed, the same happens again, if the map center didn't change, the listener is called. The method I invoke in onTouchEvent looks like this:
private void refreshMapPosition() {
GeoPoint currentMapCenter = getMapCenter();
if (oldMapCenter==null || !oldMapCenter.equals(currentMapCenter)) {
oldMapCenter = currentMapCenter;
handler.postDelayed(new Runnable() {
#Override
public void run() {
refreshMapPosition();
}
}, 50);
}
else {
if (onScrollEndListener!=null)
onScrollEndListener.onScrollEnd(currentMapCenter);
}
}
But I'm waiting for a real solution for this, too ...
I don't really have a satisfactory solution to this problem, but I can tell what I did to partially solve it.
I subclassed MapView and overrode the computeScroll() method, which gets the current centre-point of the map and compares it with the last-known centre-point (stored as a volatile field in the subclass). If the centre-point has changed, it fires an event to the listener of the map (I defined a custom listener interface for this).
The listener is an activity that instantiates a subclass of AsyncTask and executes it. This task pauses for 100ms in its doInBackGround() method, before performing the server data fetch.
When the listener activity receives a second map-move event (which it will do because of the stepping effect of the map movement), it checks the status of the just-executed AsyncTask. If that task is still running, it will cancel() it. It then creates a new task, and executes that.
The overall effect is that when the listeners get the flurry of map-moved events a few milliseconds apart, the only one that actually triggers the task to perform the server-fetch is the last one in the sequence. The downside is that it introduces a slight delay between the map movement happening, and the server fetch occurring.
I'm not happy with it, it's ugly, but it mitigates the problem. I would love to see a better solution to this.
I solved it using a thread and it seems to work quite good. It not only detects center changes but also zoom changes. Well, the detection is done after zooming and scrolling ends. If you need to detect zooming changes when you move up the first finger then you can modify my code a bit to detect different pointers. But I didn't need it, so didn't include it and left some homework for you :D
public class CustomMapView extends MapView {
private GeoPoint pressGP;
private GeoPoint lastGP;
private int pressZoom;
private int lastZoom;
public boolean onTouchEvent( MotionEvent event ) {
switch( event.getAction() ) {
case MotionEvent.ACTION_DOWN:
pressGP = getMapCenter();
pressZoom = getZoomLevel();
break;
case MotionEvent.ACTION_UP:
lastGP = getMapCenter();
pressZoom = getZoomLevel();
if( !pressGP.equals( lastGP ) ) {
Thread thread = new Thread() {
public void run() {
while( true ) {
try {
Thread.sleep( 100 );
} catch (InterruptedException e) {}
GeoPoint gp = getMapCenter();
int zl = getZoomLevel();
if( gp.equals( lastGP ) && zl == lastZoom)
break;
lastGP = gp;
lastZoom = zl;
}
onMapStop( lastGP );
}
};
thread.start();
}
break;
}
return super.onTouchEvent( event );
}
public void onMapStop( GeoPoint point , int zoom ){
// PUT YOUR CODE HERE
}
}
With the latest version of google maps API (V2) there is a listener to do this, i.e. GoogleMap.OnCameraChangeListener.
mGoogleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener()
{
#Override
public void onCameraChange(CameraPosition cameraPosition)
{
Toast.makeText(mActivity, "Longitude : "+cameraPosition.target.longitude
+", Latitude : "+cameraPosition.target.latitude, Toast.LENGTH_SHORT).show();
}
});

Categories

Resources