I'm intercepting the click from the mylocation button like so:
map.setMyLocationEnabled(true);
map.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
#Override
public boolean onMyLocationButtonClick() {
bMyLocationClicked = true;
return false;
}
});
and then making it zoom in like so:
// Acquire a reference to the system Location Manager
LocationManager locationManager = (LocationManager) parentActivity.getSystemService(Context.LOCATION_SERVICE);
LocationListener myLocationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
if (bMyLocationClicked) {
bMyLocationClicked = false;
map.moveCamera(CameraUpdateFactory.zoomTo(18f));
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
};
// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, myLocationListener);
Everything works fine, except that there's an extremely long delay between pressing the mylocation button and the zoom in actually happening. The centering of the map finishes and about 2 seconds later the map actually zooms in. How can I speed this up?
I'm looking for an answer where I stay using the mylocation button rather than implementing my own.. although, if there's absolutely no alternative, I'll build a custom center/zoom button.
Related
I am making an app that will draw a user's path on a map. This is all working fine. However, I also want to continue to collect the path (when requested by the user) when the app is in the background. This also is working pretty good, but occasionally I get "glitches". See this map for an example:
GoogleMap
I believe what is happening is that when I start the service, I am getting the "last known location" instead of the current location. I have tried several techniques without much luck so far. Some other ideas that I have have had:
Passing the GoogleApiClient from the Activity to the Service, already connected (is this possible?)
Ignoring the first "X" location updates in the service (how big does "X" need to be?)
Cleansing the path, to remove "bad" locations (what kind of algorithm would I need to figure out what a "bad" location is...it is easy to see in the attached map, but I am not sure how to do it in code).
Any other suggestions on the best way to handle this scenario?
Here is some code to give you an idea of the current functionality:
Activity.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
setUpMapIfNeeded();
buildGoogleApiClient();
createLocationRequest();
...
}
private void buildGoogleApiClient() {
googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this).addOnConnectionFailedListener(this).addApi(LocationServices.API).build();
}
private void createLocationRequest() {
locationRequest = new LocationRequest();
locationRequest.setInterval(Utils.LOCATION_UPDATE_MS);
locationRequest.setFastestInterval(Utils.FASTEST_LOCATION_UPDATE_MS);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
#Override
public void onConnected(Bundle bundle) {
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
location = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
Log.v("Connected", String.format("Location: %f, %f, %f", location.getLatitude(), location.getLongitude(), location.getAccuracy()));
if (location != null) {
updateMap();
}
}
#Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(pathReceiver);
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
if (isNavigatingTo()) {
Preferences.save(Preferences.PREFERENCE_NAVIGATE_TO, navigateToWaypoint.getId());
} else {
Preferences.remove(Preferences.PREFERENCE_NAVIGATE_TO);
}
if(isRecordingTrack()){
// Start the PathSevice to continue recording the track.
Intent pathServiceIntent = new Intent(this, PathService.class);
pathServiceIntent.putExtra(Utils.PATH_EXTRA, path);
startService(pathServiceIntent);
}
}
...
PathService.java:
#Override
public void onCreate() {
buildGoogleApiClient();
createLocationRequest();
googleApiClient.connect();
super.onCreate();
}
private void buildGoogleApiClient() {
googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this).addOnConnectionFailedListener(this).addApi(LocationServices.API).build();
}
private void createLocationRequest() {
locationRequest = new LocationRequest();
locationRequest.setInterval(Utils.LOCATION_UPDATE_MS);
locationRequest.setFastestInterval(Utils.FASTEST_LOCATION_UPDATE_MS);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
#Override
public void onConnected(Bundle bundle) {
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
}
#Override
public void onLocationChanged(Location location) {
path.addPoint(location.getLatitude(), location.getLongitude());
}
So the solution was in my question...as it seemed I was adding the "last known location" when returning back to the activity from the service (see onConnected in the Activity class above).
I now only start the location requests in the onConnected.
So I have implemented the new Fused Location Provider API to get a location of the user but for some reason, I cannot get any location unless the GPS is on. Not always, will users have their GPS on and I would like to not have to ask them to turn their GPS on every time the load the app.
How can I tell the API to give me a location with whatever provider it has available?
Here is my code:
public class FusedLocationService implements
LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
public interface OnLocationChangedListener {
public void OnLocationChanged(Location location);
}
private final String TAG = "SE8611";
private boolean mRequestingLocationUpdates = true;
private OnLocationChangedListener mCallBack;
Service locationService;
private LocationRequest locationRequest;
private GoogleApiClient googleApiClient;
private Location mCurrentLocation;
private FusedLocationProviderApi fusedLocationProviderApi = LocationServices.FusedLocationApi;
public FusedLocationService(Service locationService, final long INTERVAL, final long FASTEST_INTERVAL) {
Logger.log(TAG, "FusedLocationService called");
this.mCallBack = (OnLocationChangedListener)locationService;
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(INTERVAL);
locationRequest.setFastestInterval(FASTEST_INTERVAL);
this.locationService = locationService;
googleApiClient = new GoogleApiClient.Builder(locationService)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
if (googleApiClient != null) {
googleApiClient.connect();
}
}
#Override
public void onConnected(Bundle connectionHint) {
if(mRequestingLocationUpdates) {
startLocationUpdates();
}else{
Logger.log(TAG, "Location updates are already running.");
}
}
protected void startLocationUpdates() {
this.fusedLocationProviderApi.requestLocationUpdates(
googleApiClient, locationRequest, this);
this.mRequestingLocationUpdates = false;
}
#Override
public void onLocationChanged(Location mCurrentLocation) {
Logger.log(TAG, "onLocationChanged called");
this.mCurrentLocation = mCurrentLocation;
this.mCallBack.OnLocationChanged(this.mCurrentLocation);
}
public void startLocationUpdatesAfterResume(){
if (googleApiClient.isConnected() && !mRequestingLocationUpdates) {
Logger.log(TAG, "startLocationUpdatesAfterResume called");
this.startLocationUpdates();
}
}
public void stopLocationUpdates() {
Logger.log(TAG, "stopping Location Updates");
LocationServices.FusedLocationApi.removeLocationUpdates(
googleApiClient, this);
}
public Location getLocation() {
return this.mCurrentLocation;
}
#Override
public void onConnectionSuspended(int i) {
this.mRequestingLocationUpdates = true;
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
this.mRequestingLocationUpdates = true;
}
}
I had the same issue like you, which is not an issue at all.
Android used to have a GPS button that let you control it directly, but they replaced it with a Location button which works different.
In order to get any type of location, you must turn it on.
Like you, I thought the Location button turns on and off the GPS only, but that's not the case.
You can control the GPS by changing the location mode:
1. High accuracy (GPS, Wi-Fi and mobile networks)
2. Power Saving (Wi-Fi and mobile networks)
3. GPS only
I think I have found the solution of the problem.
If you go to Settings -> Privacy and Safety -> Location, you would notice that Location is not only GPS, but it actually lets user decide which providers can be used. For example, you can set that only WiFi and Cellular should be used to obtain any locations
Disabling Location option will disable all providers at once, not only GPS.
To test that app for users with only WiFi can get a location – change setting to "Wifi+Cellular".
you seem to be using LocationRequest.PRIORITY_HIGH_ACCURACY, which prefers using GPS above and over other methods. You might want to use PRIORITY_BALANCED_POWER_ACCURACY, which will use WiFi and cell towers before using GPS.
Also, since PRIORITY_BALANCED_POWER_ACCURACY is a "coarse" level of accuracy, you might want to change the location permission in your manifest appropriately.
The training documentation details more information about the priority flags, you might also want to go through it.
We have a strong GPS signal I have an app for testing how fast a GPS lock can be established I even tested the navigation app on my phone they all get pretty much insta lock bellow 10 if I just turned on the GPS.
But with this app I just never get a lock, the wile loop just keeps going on and on and the Log never shows I let it run for 10min and nothing. Am I missing something here I know there is a possible > 10min delay but other apps have no problem that are not using lastKnownLocation but pulling an actual location.
Also the gpsLocation() method is in a static Locator class.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Location location = Locator.gpsLocation(this);
while(location == null)
{
location = Locator.gpsLocation(this);
}
Log.w("GPS LOCATION", "LOCATION FOUND");
}
/**
* Finds users location using gps
* satelite network.
*
* #param FragmentActivity
* #return Location
*/
public static Location gpsLocation(FragmentActivity context)
{
// Fetch LocationManager service & instance of UserLocator
LocationManager provider = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
UserLocator locator = new UserLocator();
// Attempt to get a fix on users location
provider.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locator);
// Return users location using GPS Service
return locator.location;
}
/**
* Use to listen for user location updates
* TODO: Reimplement as a anonymous class
*/
class UserLocator implements LocationListener
{
// Public accessor
public Location location;
public void onLocationChanged(Location location)
{
if(location != null)
{
// We have a fix on users location
this.location = location;
}
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
I think your problem is that your location updates listener is updating the location variable inside your UserLocation class, however the variable you are checking is the one you get back from the method gpsLocation, which is null.
What you are doing is you are signing up for location updates and immediately returning the value of null and checking it over and over again, while your real location is being updated in another place.
All this is also assuming that you have declares the proper permission in your manifest file.
Try this:
Location location;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
gpsLocation(this);
while(location == null)
continue;
Log.w("GPS LOCATION", "LOCATION FOUND");
}
public static void gpsLocation(FragmentActivity context)
{
LocationManager provider = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
UserLocator locator = new UserLocator();
provider.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locator);
}
class UserLocator implements LocationListener
{
public void onLocationChanged(Location loc)
{
if(loc != null)
location = loc;
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
With this implementation, both the listener and the while loop address the same instance of Location, so once this changes from null you will get the log message.
I would however strongly discourage putting such a while(true) loop in your onCreate method, you will be blocking the main thread, and this is generally a bad thing to do.
Is there any code that lets you show your active coordinates in a self-made app? I am having troubles with my google maps and want to see what my active coordinates are. I prefer to show it as I am looking at the google maps in my app ie like a second layer of information. Thanks all !
You can set up a LocationListener.
In the onLocationChanged callback, you will receive a Location. You can then .getLatitude() and .getLongitude() from the location, and apply it to your layout.
public class MyActivity extends MapActivity implements LocationListener {
private LocationManager locationManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
requestFineAccuracyLocationUpdates();
}
private void requestFineAccuracyLocationUpdates() {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
List<String> providers = locationManager.getProviders(true);
for (String provider : providers) {
locationManager.requestLocationUpdates(provider, 5000, 1000, this);
}
}
#Override
public void onLocationChanged(Location location) {
/* You can get your latitude and longitude here, and do whatever. */
}
}
private class ExecuteLocations extends AsyncTask<String, Void, Void>{
private final ProgressDialog dialog = new ProgressDialog(ListProfiles.this);
protected void onPreExecute() {
//this.dialog.setMessage("Starting pre-execute...");
//this.dialog.show();
}
#Override
protected Void doInBackground(String... arg0) {
check_profiles_lm=(LocationManager) ListProfiles.this.getSystemService(LOCATION_SERVICE);
myLocListen = new LocationListener(){
#Override
public void onLocationChanged(Location location) {
HashMap params = new HashMap();
params.put("lat", Double.toString(location.getLatitude()));
params.put("long", Double.toString(location.getLongitude()));
postData("http://mydomain.com",params);
}
#Override
public void onStatusChanged(String provider, int status,Bundle extras) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
};
check_profiles_lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 0, myLocListen);
return null;
}
protected void onPostExecute(final Void unused) {
if (this.dialog.isShowing()) {
//this.dialog.dismiss();
}
//Do something else here
}
}
Basically, my objective is:
Constantly post the latitude/longitude to my website every minute or so.
Of course, I want to do this in another thread, so it doesn't mess up my UI thread. This AsyncTask is supposed to solve that...but it's bringing up an error and I don't know why.
What can you do to accomplish my objective? It's very simple...just scan location and post to web every minute, in the background.
By the way, my onCreate method is like this. That's basically it.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new ExecuteLocations().execute();
setContentView(R.layout.main_list);
}
Break it into 3 steps:
Make whatever you want to work work w/o AsyncTask
Make sure you've figured out AsyncTask, do a simple Log.e("haha", "gotcha") in doInBackground(...) and check that it shows
Combine the two.
For this case, I'd probably go with a Service triggered by AlarmManager. Guess you'll need the latter anyways.
Create this as a Service. No need to have a Timer or AlarmManager as you register for location events and act appropriately.
An example of listening to location events can be found at
http://code.google.com/p/android-bluetooth-on-motion/source/browse/#svn/trunk/BluetoothOnMotion/src/com/elsewhat
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_NETWORK, MIN_DISTANCE_NETWORK,locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_GPS, MIN_DISTANCE_GPS,locationListener);