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?
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.