Search code examples
androidandroid-servicegoogle-api-clientandroid-gpsandroid-googleapiclient

GoogleApiClient: hide GPS icon on status bar when a service is in the background


I'm using GoogleApiClient to implement a location listener service, but the GPS icon is always being displayed, even with the service in background. How to disable the GPS icon when the service is in the background?

enter image description here

Follow the sources below:

Activity

public class ShowDistanceActivity extends AppCompatActivity implements ILocationConstants {

protected static final String TAG = ShowDistanceActivity.class.getSimpleName();


@Bind(R.id.tvLocationData)
TextView tvLocationData;

@Bind(R.id.toolbar)
Toolbar toolbar;


/**
 * Receiver listening to Location updates and updating UI in activity
 */
private LocationReceiver locationReceiver;

/**
 * Permission util with callback mechanism to avoid boilerplate code
 * <p/>
 * https://github.com/kayvannj/PermissionUtil
 */
private PermissionUtil.PermissionRequestObject mBothPermissionRequest;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_show_distance);

    ButterKnife.bind(this);

    setSupportActionBar(toolbar);

    locationReceiver = new LocationReceiver();


}


private void startLocationService() {

    Intent serviceIntent = new Intent(this, LocationService.class);
    startService(serviceIntent);

}

@Override
protected void onStart() {
    super.onStart();

    LocalBroadcastManager.getInstance(this).registerReceiver(locationReceiver, new IntentFilter(LOACTION_ACTION));


    /**
     * Runtime permissions are required on Android M and above to access User's location
     */
    if (AppUtils.hasM() && !(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
            && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {

        askPermissions();

    } else {

        startLocationService();

    }

}

/**
 * Ask user for permissions to access GPS location on Android M
 */
public void askPermissions() {

    mBothPermissionRequest =
            PermissionUtil.with(this).request(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION).onResult(
                    new Func2() {
                        @Override
                        protected void call(int requestCode, String[] permissions, int[] grantResults) {

                            if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {

                                startLocationService();

                            } else {

                                Toast.makeText(ShowDistanceActivity.this, R.string.permission_denied, Toast.LENGTH_LONG).show();
                            }
                        }

                    }).ask(PERMISSION_ACCESS_LOCATION_CODE);

}


@Override
protected void onStop() {
    super.onStop();

    LocalBroadcastManager.getInstance(this).unregisterReceiver(locationReceiver);
}

private class LocationReceiver extends BroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {


        if (null != intent && intent.getAction().equals(LOACTION_ACTION)) {

            String locationData = intent.getStringExtra(LOCATION_MESSAGE);

            tvLocationData.setText(locationData);
        }

    }
}


@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    if (null != mBothPermissionRequest) {
        mBothPermissionRequest.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

}

Service

public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener, ILocationConstants, IPreferenceConstants {


private static final String TAG = LocationService.class.getSimpleName();

/**
 * Provides the entry point to Google Play services.
 */
protected GoogleApiClient mGoogleApiClient;

/**
 * Stores parameters for requests to the FusedLocationProviderApi.
 */
protected LocationRequest mLocationRequest;

/**
 * Represents a geographical location.
 */
protected Location mCurrentLocation;


private String mLatitudeLabel;
private String mLongitudeLabel;
private String mLastUpdateTimeLabel;
private String mDistance;


/**
 * Time when the location was updated represented as a String.
 */
protected String mLastUpdateTime;

private Location oldLocation;

private Location newLocation;


private AppPreferences appPreferences;

/**
 * Total distance covered
 */
private float distance;


@Override
public void onCreate() {
    super.onCreate();

    Log.d(TAG, "onCreate() called");

    appPreferences = new AppPreferences(this);

    oldLocation = new Location("Point A");
    newLocation = new Location("Point B");

    mLatitudeLabel = getString(R.string.latitude_label);
    mLongitudeLabel = getString(R.string.longitude_label);
    mLastUpdateTimeLabel = getString(R.string.last_update_time_label);
    mDistance = getString(R.string.distance);

    mLastUpdateTime = "";

    distance = appPreferences.getFloat(PREF_DISTANCE, 0);

    Log.d(TAG, "onCreate Distance: " + distance);


}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    Log.d(TAG, "onStartCommand called");

    buildGoogleApiClient();

    mGoogleApiClient.connect();

    if (mGoogleApiClient.isConnected()) {
        startLocationUpdates();
    }

    return Service.START_STICKY;

}


/**
 * Builds a GoogleApiClient. Uses the {@code #addApi} method to request the
 * LocationServices API.
 */
protected synchronized void buildGoogleApiClient() {

    Log.d(TAG, "buildGoogleApiClient() called");

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


protected void createLocationRequest() {

    Log.d(TAG, "createLocationRequest() called");

    mLocationRequest = new LocationRequest();

    mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);

    mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);

    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
}

/**
 * Requests location updates from the FusedLocationApi.
 */
protected void startLocationUpdates() {

    try {

        Log.d(TAG, "startLocationUpdates called");

        LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, mLocationRequest, this);

    } catch (SecurityException ex) {

        ex.printStackTrace();
    }
}


/**
 * Updates the latitude, the longitude, and the last location time in the UI.
 */
private void updateUI() {

    if (null != mCurrentLocation) {

        StringBuilder sbLocationData = new StringBuilder();
        sbLocationData.append(mLatitudeLabel)
                .append(" ")
                .append(mCurrentLocation.getLatitude())
                .append("\n")
                .append(mLongitudeLabel)
                .append(" ")
                .append(mCurrentLocation.getLongitude())
                .append("\n")
                .append(mLastUpdateTimeLabel)
                .append(" ")
                .append(mLastUpdateTime)
                .append("\n")
                .append(mDistance)
                .append(" ")
                .append(getUpdatedDistance())
                .append(" meters");


        /*
         * update preference with latest value of distance
         */
        appPreferences.putFloat(PREF_DISTANCE, distance);

        Log.d(TAG, "Location Data:\n" + sbLocationData.toString());

        sendLocationBroadcast(sbLocationData.toString());
    } else {

        Toast.makeText(this, R.string.unable_to_find_location, Toast.LENGTH_SHORT).show();
    }
}


/**
 * Send broadcast using LocalBroadcastManager to update UI in activity
 *
 * @param sbLocationData
 */
private void sendLocationBroadcast(String sbLocationData) {

    Log.d(TAG, "sendLocationBroadcast() called");

    Intent locationIntent = new Intent();
    locationIntent.setAction(LOACTION_ACTION);
    locationIntent.putExtra(LOCATION_MESSAGE, sbLocationData);

    LocalBroadcastManager.getInstance(this).sendBroadcast(locationIntent);

}

/**
 * Removes location updates from the FusedLocationApi.
 */
protected void stopLocationUpdates() {

    Log.d(TAG, "stopLocationUpdates() called");

    LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}


@Override
public void onDestroy() {

    Log.d(TAG, "onDestroy() called");

    appPreferences.putFloat(PREF_DISTANCE, distance);

    stopLocationUpdates();

    mGoogleApiClient.disconnect();

    Log.d(TAG, "onDestroy Distance " + distance);

    super.onDestroy();
}


/**
 * Runs when a GoogleApiClient object successfully connects.
 */
@Override
public void onConnected(Bundle connectionHint) throws SecurityException {
    Log.i(TAG, "Connected to GoogleApiClient");


    if (mCurrentLocation == null) {
        mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
        updateUI();
    }

    startLocationUpdates();

}

/**
 * Callback that fires when the location changes.
 */
@Override
public void onLocationChanged(Location location) {

    Log.d(TAG, "onLocationChanged() called");

    mCurrentLocation = location;
    mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
    updateUI();
}

@Override
public void onConnectionSuspended(int cause) {

    Log.d(TAG, "onConnectionSuspended() called");

    mGoogleApiClient.connect();
}

@Override
public void onConnectionFailed(ConnectionResult result) {

    Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
}


private float getUpdatedDistance() {

    /**
     * There is 68% chance that user is with in 100m from this location.
     * So neglect location updates with poor accuracy
     */


    if (mCurrentLocation.getAccuracy() > ACCURACY_THRESHOLD) {

        Log.d(TAG, "getUpdatedDistance() called");

        return distance;
    }


    if (oldLocation.getLatitude() == 0 && oldLocation.getLongitude() == 0) {

        oldLocation.setLatitude(mCurrentLocation.getLatitude());
        oldLocation.setLongitude(mCurrentLocation.getLongitude());

        newLocation.setLatitude(mCurrentLocation.getLatitude());
        newLocation.setLongitude(mCurrentLocation.getLongitude());

        return distance;
    } else {

        oldLocation.setLatitude(newLocation.getLatitude());
        oldLocation.setLongitude(newLocation.getLongitude());

        newLocation.setLatitude(mCurrentLocation.getLatitude());
        newLocation.setLongitude(mCurrentLocation.getLongitude());

    }


    /**
     * Calculate distance between last two geo locations
     */
    distance += newLocation.distanceTo(oldLocation);

    return distance;
}


@Override
public IBinder onBind(Intent intent) {

    throw new UnsupportedOperationException("Not yet implemented");
}

}

Manifest

Android Manifest declaration

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.technosavy.showmedistance">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ShowDistanceActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <service
            android:name=".service.LocationService"
            android:enabled="true"
            android:exported="true"></service>

    </application>

</manifest>

Please, any help is welcome.


Solution

  • To remove the GPS icon you need to use cached and wi-fi location settings (from memory). In your code make the following changes.

    In Manifest, remove:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    

    In Service, change the following:

    mLocationRequest.setInterval(10000L);
    mLocationRequest.setFastestInterval(5000L);
    // Do NOT use LocationRequest.PRIORITY_HIGH_ACCURACY here
    // Instead use one of the other option.
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
    //mLocationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);
    //mLocationRequest.setPriority(LocationRequest.PRIORITY_NO_POWER);
    // Remove setSmallestDisplacement() as it should not be used
    // unless you are using a GPS / PRIORITY_HIGH_ACCURACY
    //mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
    

    Pretty sure this is will remove the GPS icon but you will also only get the FusedLocationApi.getLastLocation() values when the onConnected() method is called (very possible this will be low accuracy).

    The onLocationChanged() method may never trigger until another application makes a location request at a higher priority or with more accuracy.