Search code examples
androidgeolocationlocationandroid-locationlocationlistener

Android LocationManager Location Updates not starting when device has no Internet


I'm working on a device that is entirely offline and has not connected to the Internet since the last time it was updated to Android 6.0.1

For a while this app worked perfectly fine and using Google Play Services FusedLocationApi it would update every second and do what it was supposed to do. Then it suddenly stopped working after I made some changes to other parts of the code and I'm at a loss to as why. I thought it could be a problem with Google Play Services so I switched it out with Android's location services as instructed in this guide.

I have cut out code that should be irrelevant to the problem. I may have cut out some variables by accident but I am working in Android Studio and there aren't any missing variables in there so that's probably not the issue. I am using Butterknife so there are some annotations.

Here is Logcat from app created to turning the location requests on and off with the switch:

08-01 11:04:53.289 11532-11532/mil.navy.nrl.androidrobocontroller W/System: ClassLoader referenced unknown path: /data/app/mil.navy.nrl.androidrobocontroller-1/lib/arm
08-01 11:04:53.534 11532-11532/mil.navy.nrl.androidrobocontroller W/System: ClassLoader referenced unknown path: /data/app/mil.navy.nrl.androidrobocontroller-1/lib/arm
08-01 11:04:53.869 11532-11532/mil.navy.nrl.androidrobocontroller W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
08-01 11:04:54.057 11532-11574/mil.navy.nrl.androidrobocontroller D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
08-01 11:04:54.118 11532-11574/mil.navy.nrl.androidrobocontroller I/Adreno-EGL: <qeglDrvAPI_eglInitialize:379>: QUALCOMM Build: 10/21/15, 369a2ea, I96aee987eb
08-01 11:04:54.120 11532-11574/mil.navy.nrl.androidrobocontroller I/OpenGLRenderer: Initialized EGL, version 1.4
08-01 11:04:56.439 11532-11532/mil.navy.nrl.androidrobocontroller V/TrackActivity: Requested location
08-01 11:04:56.440 11532-11532/mil.navy.nrl.androidrobocontroller V/TrackActivity: Permission android.permission.ACCESS_FINE_LOCATION has been granted.
08-01 11:04:56.440 11532-11532/mil.navy.nrl.androidrobocontroller V/TrackActivity: Location updates requested
08-01 11:04:56.445 11532-11532/mil.navy.nrl.androidrobocontroller V/TrackActivity: Check checked and updated.
08-01 11:04:58.791 11532-11532/mil.navy.nrl.androidrobocontroller V/TrackActivity: Permission android.permission.ACCESS_FINE_LOCATION has been granted.
08-01 11:04:58.794 11532-11532/mil.navy.nrl.androidrobocontroller V/TrackActivity: Check unchecked and updated.

From this I know that the onCheckChanged() is being called and it calls startLocationUpdates() perfectly fine. It even gets to the code that should request location updates but it never seems to respond to them.

Here's all the code:

Lifecycle Methods

package com.example.app;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SwitchCompat;
import android.util.Log;
import android.widget.EditText;
import android.widget.RelativeLayout;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnCheckedChanged;

public class TrackActivity extends AppCompatActivity implements LocationListener,
        ActivityCompat.OnRequestPermissionsResultCallback {

    private static final String LOG_TAG = "TrackActivity";
    private static final boolean SHOW_VERBOSE = true;
    private static final int REQUEST_FINE_LOCATION_UPDATE = 1;
    private static final int REQUEST_END_LOCATION_UPDATES = 2;

    protected LocationManager mLocationManager;

    private Location mBestLocation;

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

        // ButterKnife bind method. REQUIRED FOR ANNOTATIONS TO WORK.
        ButterKnife.bind(this);
    }

    @Override
    protected void onStart() {

        mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

        super.onStart();
    }

    @Override
    protected void onStop() {
        // startLocationUpdates(); <--- Made a typo here
        endLocationUpdates();
        super.onStop();
    }

onCheckedChanged

@OnCheckedChanged(R.id.track_send_location_switch)
void onCheckChanged(boolean checked) {
    if (checked) {
        // Unrelated code cut.

        startLocationUpdates();

        if (SHOW_VERBOSE) Log.v(LOG_TAG, "Check checked and updated.");
    } else {
        endLocationUpdates();

        // Unrelated code cut

        if (SHOW_VERBOSE) Log.v(LOG_TAG, "Check unchecked and updated.");
    }
}

startLocationUpdates

protected void startLocationUpdates() {
    if (SHOW_VERBOSE) Log.v(LOG_TAG, "Requested location");

    if (checkRequestPermission(Manifest.permission.ACCESS_FINE_LOCATION, REQUEST_FINE_LOCATION_UPDATE)) {
        // TODO: Add high accuracy 1 Hz Location Updates

        if (SHOW_VERBOSE) Log.v(LOG_TAG, "Location updates requested");

        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
        mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
    }
}

endLocationUpdates

protected void endLocationUpdates() {
    if (checkRequestPermission(
            Manifest.permission.ACCESS_FINE_LOCATION, REQUEST_END_LOCATION_UPDATES)) {
        mLocationManager.removeUpdates(this);
    }
}

checkRequestPermission

protected boolean checkRequestPermission(String permission, int requestCode) {
    int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
    if(permissionCheck == PackageManager.PERMISSION_GRANTED) {
        if(SHOW_VERBOSE) Log.v(LOG_TAG, "Permission " + permission + " has been granted.");
        return true;
    } else {
        ActivityCompat.requestPermissions(this,
                new String[]{permission},
                requestCode);
    }

    return false;
}

onRequestPermissionsResult

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_FINE_LOCATION_ONCE:
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
                startLocationUpdates();
            break;
        case REQUEST_FINE_LOCATION_UPDATE:
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
                startLocationUpdates();
            break;
        case REQUEST_END_LOCATION_UPDATES:
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
                endLocationUpdates();
            break;
    }
}

onLocationChanged

@Override
public void onLocationChanged(Location location) {
    if(SHOW_VERBOSE) Log.v(LOG_TAG, "Location request returned");

    Location locationToUse;

    if(isBetterLocation(location, mBestLocation)) {
        locationToUse = location;
        mBestLocation = location;
    } else {
        locationToUse = mBestLocation;
    }

    if(SHOW_VERBOSE) Log.v(LOG_TAG, "onLocationChanged()");

    // Rest of app's code is ommited.
    doThingWithLocation(locationToUse);
}

LocationListener Methods

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

}

@Override
public void onProviderEnabled(String provider) {
    if(SHOW_VERBOSE) Log.v(LOG_TAG, "Provider enabled");
}

@Override
public void onProviderDisabled(String provider) {
    if(SHOW_VERBOSE) Log.v(LOG_TAG, "Provider disabled");
}

Methods from Location Strategies Guide

protected boolean isBetterLocation(Location location, Location currentBestLocation) {
    if(currentBestLocation == null)
        return true;

    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > 10000;
    boolean isSignificantlyOlder = timeDelta < -10000;
    boolean isNewer = timeDelta > 0;

    if (isSignificantlyNewer) {
        return true;
        // If the new location is more than two minutes older, it must be worse
    } else if (isSignificantlyOlder) {
        return false;
    }

    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;

    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());

    // Determine location quality using a combination of timeliness and accuracy
    if (isMoreAccurate) {
        return true;
    } else if (isNewer && !isLessAccurate) {
        return true;
    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
        return true;
    }
    return false;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
        return provider2 == null;
    }
    return provider1.equals(provider2);
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_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="com.example.app.TrackActivity"
            android:windowSoftInputMode="stateHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name=".TrackActivity" />

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

</manifest>

Any help would really be appreciated! Thanks!


Solution

  • Turns out I didn't have a very good GPS signal and that was what was causing my issues. I thought I was alright sitting next to a window but it was taking forever to connect to GPS (>15minutes as suggested by @cYrixmorten). Given enough time it would connect to GPS but I found that once I walked outside, it would connect almost instantly.

    In case anyone needs to test GPS Services, I used this GPS Test app.