3

I'm trying to use Firebase Cloud Messaging to send a command to my Android app that prompts it to determine its current location. This is done in the class "FCMService".

The class "SingleShotLocationProvider" then performs the actual work by requesting location updates using the FusedLocationProviderClient. However, the callback "fusedTrackerCallback" is never called ever, although the necessary permissions are granted and GPS is switched on. Why?

FCMService class

public class FCMService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.d("FCMService", "Message received");

            if(remoteMessage.getData().containsKey("command") && remoteMessage.getData().get("command").equalsIgnoreCase("locate")) {
                // get current location
                SingleShotLocationProvider locProv = new SingleShotLocationProvider(getApplicationContext());
                locProv.requestSingleUpdate();
                System.out.println("Requested single location update.");
            }

        }
    }

}

SingleShotLocationProvider class

public class SingleShotLocationProvider {

    private Context context;

    final LocationManager locManager;
    private FusedLocationProviderClient mFusedLocationClient;
    private LocationCallback fusedTrackerCallback;

    public SingleShotLocationProvider(Context context) {
        this.context = context;
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context);

        locManager = (LocationManager) context.getSystemService( Context.LOCATION_SERVICE );
        if (locManager != null && !locManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
            Log.e("SiSoLocProvider", "GPS IS NOT enabled.");
        } else {
            Log.d("SiSoLocProvider", "GPS is enabled.");
        }
    }

    @TargetApi(16)
    public void requestSingleUpdate() {
        Looper.prepare();

        // only works with SDK Version 23 or higher
        if (android.os.Build.VERSION.SDK_INT >= 23) {
            if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // permission is not granted
                Log.e("SiSoLocProvider", "Permission not granted.");
                return;
            } else {
                Log.d("SiSoLocProvider", "Permission granted.");
            }
        } else {
            Log.d("SiSoLocProvider", "SDK < 23, checking permissions should not be necessary");
        }

        final long startTime = System.currentTimeMillis();
        fusedTrackerCallback = new LocationCallback(){
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if((locationResult.getLastLocation() != null) && (System.currentTimeMillis() <= startTime+30*1000)) {
                    System.out.println("LOCATION: " + locationResult.getLastLocation().getLatitude() + "|" + locationResult.getLastLocation().getLongitude());
                    System.out.println("ACCURACY: " + locationResult.getLastLocation().getAccuracy());
                    mFusedLocationClient.removeLocationUpdates(fusedTrackerCallback);
                } else {
                    System.out.println("LastKnownNull? :: " + (locationResult.getLastLocation() == null));
                    System.out.println("Time over? :: " + (System.currentTimeMillis() > startTime+30*1000));
                }
            }
        };

        LocationRequest req = new LocationRequest();
        req.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        req.setFastestInterval(2000);
        req.setInterval(2000);
        mFusedLocationClient.requestLocationUpdates(req, fusedTrackerCallback, null);
    }
}

Output is:

//Message received
//GPS is enabled.
//Permission granted.
//Requested single location update.

But nothing more then. Why? Is it perhaps because the permission was granted via the service application context?

Ryan M
  • 18,333
  • 31
  • 67
  • 74
DeBukkIt
  • 86
  • 1
  • 7
  • did you add location permission access in manifest ? – droidev Aug 24 '18 at 12:41
  • 1
    Did you try to add `Looper.loop();` at the end of `requestSingleUpdate`? – Levon Petrosyan Aug 24 '18 at 12:41
  • @droidev of course I added ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION to the manifest, inside the `manifest` tag, but outside the `application` tag. – DeBukkIt Aug 24 '18 at 16:09
  • that’s correct. still not working? check if there’s anything in your logcat – droidev Aug 24 '18 at 16:10
  • @LevonPetrosyan The third argument is expected to be of type `Looper`, but `Looper.loop()` is of type `void`. The documentation also says that if you leave the third argument `null`, the calling thread handles it. That should not be the problem, unless the calling thread (the FCM service) somehow doesn't stay alive long enough to wait for the callback. – DeBukkIt Aug 24 '18 at 16:13

1 Answers1

7

From your code I guess you want to get location and receive result in a background thread. Here is a solution.

@TargetApi(16)
public void requestSingleUpdate() {
    // TODO: Comment-out this line.
    // Looper.prepare();

    // only works with SDK Version 23 or higher
    if (android.os.Build.VERSION.SDK_INT >= 23) {
        if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // permission is not granted
            Log.e("SiSoLocProvider", "Permission not granted.");
            return;
        } else {
            Log.d("SiSoLocProvider", "Permission granted.");
        }
    } else {
        Log.d("SiSoLocProvider", "SDK < 23, checking permissions should not be necessary");
    }

    // TODO: Start a background thread to receive location result.
    final HandlerThread handlerThread = new HandlerThread("RequestLocation");
    handlerThread.start();

    final long startTime = System.currentTimeMillis();
    fusedTrackerCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            // TODO: Those lines of code will run on the background thread.
            if ((locationResult.getLastLocation() != null) && (System.currentTimeMillis() <= startTime + 30 * 1000)) {
                System.out.println("LOCATION: " + locationResult.getLastLocation().getLatitude() + "|" + locationResult.getLastLocation().getLongitude());
                System.out.println("ACCURACY: " + locationResult.getLastLocation().getAccuracy());
                mFusedLocationClient.removeLocationUpdates(fusedTrackerCallback);
            } else {
                System.out.println("LastKnownNull? :: " + (locationResult.getLastLocation() == null));
                System.out.println("Time over? :: " + (System.currentTimeMillis() > startTime + 30 * 1000));
            }
            // TODO: After receiving location result, remove the listener.
            mFusedLocationClient.removeLocationUpdates(this);

            // Release the background thread which receive the location result.
            handlerThread.quit();
        }
    };

    LocationRequest req = new LocationRequest();
    req.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    req.setFastestInterval(2000);
    req.setInterval(2000);
    // TODO: Pass looper of background thread indicates we want to receive location result in a background thread instead of UI thread.
    mFusedLocationClient.requestLocationUpdates(req, fusedTrackerCallback, handlerThread.getLooper());
}

If you want to receive location result on UI thread.

@TargetApi(16)
public void requestSingleUpdate() {
    // TODO: Comment-out this line.
    // Looper.prepare();

    // only works with SDK Version 23 or higher
    if (android.os.Build.VERSION.SDK_INT >= 23) {
        if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // permission is not granted
            Log.e("SiSoLocProvider", "Permission not granted.");
            return;
        } else {
            Log.d("SiSoLocProvider", "Permission granted.");
        }
    } else {
        Log.d("SiSoLocProvider", "SDK < 23, checking permissions should not be necessary");
    }

    final long startTime = System.currentTimeMillis();
    fusedTrackerCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            // TODO: These lines of code will run on UI thread.
            if ((locationResult.getLastLocation() != null) && (System.currentTimeMillis() <= startTime + 30 * 1000)) {
                System.out.println("LOCATION: " + locationResult.getLastLocation().getLatitude() + "|" + locationResult.getLastLocation().getLongitude());
                System.out.println("ACCURACY: " + locationResult.getLastLocation().getAccuracy());
                mFusedLocationClient.removeLocationUpdates(fusedTrackerCallback);
            } else {
                System.out.println("LastKnownNull? :: " + (locationResult.getLastLocation() == null));
                System.out.println("Time over? :: " + (System.currentTimeMillis() > startTime + 30 * 1000));
            }

            // TODO: After receiving location result, remove the listener.
            mFusedLocationClient.removeLocationUpdates(this);
        }
    };

    LocationRequest req = new LocationRequest();
    req.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    req.setFastestInterval(2000);
    req.setInterval(2000);
    // Receive location result on UI thread.
    mFusedLocationClient.requestLocationUpdates(req, fusedTrackerCallback, Looper.getMainLooper());
}

I add each comment that start with TODO: keyword to explain my intended.

Son Truong
  • 13,661
  • 5
  • 32
  • 58
  • 3
    Thank you very much! In fact, the thread on which the request for the location was running was the problem. Your solution worked immediately without any further adjustments. – DeBukkIt Aug 25 '18 at 11:39
  • 2
    Thank you. I've been struggling with location issue for quite sometime. Thread is the answer! – Abrar Mar 26 '19 at 07:32
  • 1
    Hello. Thank you very very much! You saved my time. – HF_ Jul 24 '19 at 11:45