Android: Google Maps location with low battery usage
A

4

13

My app is currently using Maps by Google Play Services

speficying:

mMap.setMyLocationEnabled(true);

I realize each time I am displaying the map in my app:

  • the location is indicated on the map by a blue dot
  • a location icon is displaying in the top bar
  • if I go into Settings/Location of the phone, my app is reported as "High battery use"

However, I can see there are apps that use Maps and still show the location blue dot, but the location icon doesn't appear in top bar and their battery usage is low.

My app currently grants both permissions:

  • android.permission.ACCESS_COARSE_LOCATION
  • android.permission.ACCESS_FINE_LOCATION

My question is:

how can I show the location blue dot with low battery usage?

is it possible to specify the accuracy/battery usage by code?

UPDATE

Actually I realized that the way to do it is to use the GoogleApiClient's FusedLocationApi

    mGoogleApiClient = new GoogleApiClient.Builder(context)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

I have configured the GoogleApiClient inside my Activity, calling:

  • GoogleApiClient.connect() on the Activity's start
  • GoogleApiClient.disconnect() on the Activity's stop

on the onConnected callback I set the criteria for the location updates: fastest interval of 1 minute with low power priority:

    private static final LocationRequest REQUEST = LocationRequest.create()
        .setFastestInterval(60000)   // in milliseconds
        .setInterval(180000)         // in milliseconds
        .setPriority(LocationRequest.PRIORITY_LOW_POWER);

    @Override
    public void onConnected(Bundle bundle) {
        LocationServices.FusedLocationApi.requestLocationUpdates(
            mGoogleApiClient,
            REQUEST,
            this);  // LocationListener
    }

I have tested that the GoogleApiClient connects correctly at start, but for some reasons whenever I visit the fragment with the embedded MapView, I still get the high battery use for my app on the Settings/Location screen!

It seems the MapView is ignoring these low power criterias!

Alumroot answered 23/1, 2015 at 10:55 Comment(6)
you don't need both of those permissions, choose eitherUnruly
I can see apps which requested both permissions and still are able to show the location blue dots with low battery use. I wonder if there is a method to programmatically set the accuracy in the code.Alumroot
So what's your question then?Unruly
my question is how to still show the location with low battery useAlumroot
so please edit your questionUnruly
@MarianPaździoch, I made an update to the question, and I will award a bounty of +50 for the correct answerAlumroot
A
29

FINALLY FOUND THE SOLUTION!!! thanks to Tristan for his answer!

By default, GoogleMap uses its on location provider, which is not the Fused Location Provider. In order to use the Fused Location Provider (which allows you to control the location accuracy and power consumption) you need to explicitely set the map location source with GoogleMap.setLocationSource() (documentation)

I am reporting here a sample activity to do that:

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;

import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends FragmentActivity
    implements
        ConnectionCallbacks,
        OnConnectionFailedListener,
        LocationSource,
        LocationListener,
        OnMyLocationButtonClickListener,
        OnMapReadyCallback {

    private GoogleApiClient mGoogleApiClient;
    private TextView mMessageView;
    private OnLocationChangedListener mMapLocationListener = null;

    // location accuracy settings
    private static final LocationRequest REQUEST = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMessageView = (TextView) findViewById(R.id.message_text);

        SupportMapFragment mapFragment =
                (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

    }

    @Override
    protected void onResume() {
        super.onResume();
        mGoogleApiClient.connect();
    }

    @Override
    public void onPause() {
        super.onPause();
        mGoogleApiClient.disconnect();
    }

    @Override
    public void onMapReady(GoogleMap map) {
        map.setLocationSource(this);
        map.setMyLocationEnabled(true);
        map.setOnMyLocationButtonClickListener(this);
    }

    public void showMyLocation(View view) {
        if (mGoogleApiClient.isConnected()) {
            String msg = "Location = "
                    + LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Implementation of {@link LocationListener}.
     */
    @Override
    public void onLocationChanged(Location location) {
        mMessageView.setText("Location = " + location);
        if (mMapLocationListener != null) {
            mMapLocationListener.onLocationChanged(location);
        }
    }


    @Override
    public void onConnected(Bundle connectionHint) {
        LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient,
                REQUEST,
                this);  // LocationListener
    }


    @Override
    public void onConnectionSuspended(int cause) {
        // Do nothing
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        // Do nothing
    }

    @Override
    public boolean onMyLocationButtonClick() {
        Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show();
        // Return false so that we don't consume the event and the default behavior still occurs
        // (the camera animates to the user's current position).
        return false;
    }

    @Override
    public void activate(OnLocationChangedListener onLocationChangedListener) {
        mMapLocationListener = onLocationChangedListener;
    }

    @Override
    public void deactivate() {
        mMapLocationListener = null;
    }
}
Alumroot answered 26/1, 2015 at 19:42 Comment(3)
Funny this is exactly the MyLocationDemo from google-play-services samples that comes with the SDK. You just have the interface LocationSource. Good stuff.Haywire
I have a question when is deactive calledHallvard
I assigned the bounty to Tristan, but I reported this question as the answer as it provides a full working example.Alumroot
P
14

You will want to make your activity (or better a separate object for this purpose) implement the LocationSource interface.

It is pretty simple you need to store the listener passed in the activate() method and call it when the location is updated and forget it when deactivate() is called. See this answer for an example, you will probably want to update it to use the FusedLocationProvider.

Once you have this set up you can pass your activity as the LocationSource for the map like so mMap.setLocationSource(this) (documentation).

This will stop the map from using its default LocationSource which uses the high battery use location services.

Perfuse answered 26/1, 2015 at 5:5 Comment(3)
See my answer, it says that default location source is indeed FusedLocationProvider so there's no need to set it again.Unruly
It is but not set to LocationRequest.PRIORITY_LOW_POWERPerfuse
Thanks so much Tristan, that's the right answer!!!! You won the 50 points bounty!!!! it seems Google Maps by default is using it's own location source, which is not the FusedLocationProvider. So it is needed to explicitely set the FusedLocationProvider if you want to have control of the accuracy.Alumroot
C
0

It's stated here that

FusedLocationProviderApi provides improved location finding and power usage and is used by the "My Location" blue dot.

So "My Location" dot on map is fed by FusedLocationProviderApi. And as you grant permission android.permission.ACCESS_FINE_LOCATION you allow FusedLocationProviderApi for your app to get data from GPS which may cause high battery use.

So add only android.permission.ACCESS_COARSE_LOCATION permission to manifest and Android should not blame you for battery usage.

Cantata answered 23/1, 2015 at 11:21 Comment(2)
unfortunately it's not possible to remove the android.permission.ACCESS_FINE_LOCATION permission: #17089641Alumroot
the location dot on the map is actually not fed by FusedLocationProvider by default. You are required to explicitely setthe map location source to get data from the FusedLocationProviderAlumroot
T
-2

You can do so by using Network provider classes You can use below code AppLocationService.java // Special for getting current location with low battery usage (same like Battery saver mode in nexus 5 ,5.0)

package coreclass;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;

public class AppLocationService extends Service implements LocationListener {

protected LocationManager locationManager;
Location location;

private static final long MIN_DISTANCE_FOR_UPDATE = 10;
private static final long MIN_TIME_FOR_UPDATE = 1000 * 60 * 2;

public AppLocationService(Context context) {
    locationManager = (LocationManager) context
            .getSystemService(LOCATION_SERVICE);
}

public Location getLocation(String provider) {
    if (locationManager.isProviderEnabled(provider)) {
        locationManager.requestLocationUpdates(provider,
                MIN_TIME_FOR_UPDATE, MIN_DISTANCE_FOR_UPDATE, this);
        if (locationManager != null) {
            location = locationManager.getLastKnownLocation(provider);
            return location;
        }
    }
    return null;
}

@Override
public void onLocationChanged(Location location) {
}

@Override
public void onProviderDisabled(String provider) {
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

@Override
public IBinder onBind(Intent arg0) {
    return null;
}

}

Usage of above class MainActivity.java

AppLocationService appLocationService;
appLocationService = new AppLocationService(getActivity());
Location nwLocation = appLocationService.getLocation(LocationManager.NETWORK_PROVIDER);
                        if (nwLocation != null) {
                            Lat = nwLocation.getLatitude();
                            Longi = nwLocation.getLongitude();

                        }

In this way you can get the current location with GPS mode in high bettery usage mode, after you can set the blue dot or whatever you want

Hope it helps you and all

Tragedienne answered 23/1, 2015 at 11:5 Comment(2)
this is not the answer for the question askedUnruly
@How can you said that ? Is there anything you know ?Tragedienne

© 2022 - 2024 — McMap. All rights reserved.