Unable to get current (real time) location using FusedLocationProviderClient - android

After 2 days of struggle, I have been forced to ask this question. I have been searching StackOverflow but no solution seems to work for me. I don't know why.
I am trying to get the current device location once:
(a) the permissions have been granted
(b) user has enabled location service by tapping 'Okay' in the dialogue box that appears in the fragment (SelectLocationFragment.kt)
The issue: Once the permissions are granted, location is enabled by pressing 'okay' in the dialogue - If I use hard coded values like here:
//currentLatLng = LatLng(51.000, -0.0886)
instead of :
currentLatLng = LatLng(mCurrentLocation.latitude,mCurrentLocation.longitude)
It works fine. But this is not ideal of course. I'd like real time device location using onLocationCallBack's onLocationResult using:
fusedLocationClient.requestLocationUpdates(locationRequest,locationCallback, Looper.getMainLooper())
The problem is when I try to use:
currentLatLng = LatLng(mCurrentLocation.latitude,mCurrentLocation.longitude)
a) onLocationCallBack does not seem to be called. And therefore no call to onLocationResult happens, and therefore, mCurrentLocation remains uninitialised. And hence I get the error :
kotlin.UninitializedPropertyAccessException: lateinit property mCurrentLocation has not been initialized
This my onMapReady()
#SuppressLint("MissingPermission")
override fun onMapReady(gMap: GoogleMap) {
isPoiSelected = false
map = gMap
**checkPermissionsAndDeviceLocationSettings()**
setMapStyle(map)
setPoiClick(map)
setMapLongClick(map)
}
This is checkPermissionAndDeviceLocationSettings():
#SuppressLint("MissingPermission")
private fun checkPermissionsAndDeviceLocationSettings() {
if (isPermissionGranted()) {
checkDeviceLocationSettings()
} else {
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_PERMISSION_LOCATION
)
}
}
This is checkDeviceLocationSettings:
private fun checkDeviceLocationSettings(resolve: Boolean = true) {
val locationRequest = LocationRequest.create().apply {
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 10000L
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
val settingsClient = LocationServices.getSettingsClient(requireActivity())
val locationSettingsResponseTask =
settingsClient.checkLocationSettings(builder.build())
locationSettingsResponseTask.addOnFailureListener { exception ->
if (exception is ResolvableApiException && resolve) {
try {
startIntentSenderForResult(
exception.resolution.intentSender,
REQUEST_TURN_DEVICE_LOCATION_ON, null, 0, 0, 0, null
)
} catch (sendEx: IntentSender.SendIntentException) {
Log.d(
SaveReminderFragment.TAG,
"Error getting location settings resolution: " + sendEx.message
)
}
} else {
Snackbar.make(
activity!!.findViewById<CoordinatorLayout>(R.id.myCoordinatorLayout),
R.string.location_required_error, Snackbar.LENGTH_INDEFINITE
).setAction(android.R.string.ok) {
checkDeviceLocationSettings()
}.show()
}
}
locationSettingsResponseTask.addOnSuccessListener {
Log.e(TAG, "SUCCESSFUL!")
Toast.makeText(requireContext(), "TOAST", Toast.LENGTH_SHORT).show()
enableLocation(locationRequest)
showSnackBar(getString(R.string.select_location))
}
}
This is enableLocation:
#SuppressLint("MissingPermission")
private fun enableLocation(locationRequest: LocationRequest) {
Log.e(TAG, "Inside Enable Location Start")
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
Log.e(TAG, "Inside on Location Result")
val locationList = locationResult.locations
Log.e(TAG, "${locationResult.locations}")
if(locationList.isNotEmpty()){
val location = locationList.last()
Log.i("MapsActivity", "Location: " + location.latitude + " " + location.longitude)
mCurrentLocation = location
}
fusedLocationClient.removeLocationUpdates(locationCallback)
}
}
map.isMyLocationEnabled = true
val locationResult: Task<Location> = fusedLocationClient.lastLocation
locationResult.addOnCompleteListener(OnCompleteListener<Location?> { task ->
if (task.isSuccessful) {
Log.e(TAG, locationResult.result?.latitude.toString())
// Set the map's camera position to the current location of the device.
if (task.result != null) {
mCurrentLocation = task.result!!
val latLng = LatLng(
mCurrentLocation.latitude,
mCurrentLocation.longitude
)
val update = CameraUpdateFactory.newLatLngZoom(
latLng,
18f
)
Toast.makeText(requireContext(), "CAMERA MOVING", Toast.LENGTH_SHORT).show()
map.animateCamera(update)
} else {
Log.e(TAG, " Task result is null")
//Need to do something here to get the real time location
fusedLocationClient.requestLocationUpdates(locationRequest,locationCallback, Looper.getMainLooper())
currentLatLng = LatLng(mCurrentLocation.latitude,mCurrentLocation.longitude)
//currentLatLng = LatLng(51.000, -0.0886)
val update = CameraUpdateFactory.newLatLngZoom(currentLatLng, 18f)
map.animateCamera(update)
}
} else {
Log.e(TAG, "Unsuccessful Task result")
Toast.makeText(requireContext(), "ENABLE LOCATION ELSE", Toast.LENGTH_LONG).show()
}
})
}
Is there any God sent man out there to help me resolve this issue. I am beginning to fade away.
If you guys need to see the entire file:
Thank you in advance.

FusedLocationProvider can return a null Location, especially the first time it fetches a Location. So, as you have found, simply returning when the result is null will force requestLocationUpdates() to fire again and actually fetch a valid Location the second time.
I think you can also force FusedLocationProvider to have a Location the first time if you just open the Google Maps app and wait for it to obtain a GPS lock. Of course, you wouldn't want your users to have to perform this process in order for your app to work, but it's still good to know for debugging.
The cause of this seems to be that FusedLocationProvider tries to return the most recently fetched Location for the device. If this Location is too old, has never been fetched, or the user toggled on/off the Location option on their phone, it just returns null.

Related

How to get location using "fusedLocationClient.getCurrentLocation" method in Kotlin?

To request the last known location of the user's device, we can use the fused location provider to retrieve the device's last known location using getLastLocation(), but using getCurrentLocation() gets a refresher, and more accurate location.
so, how to use the fusedLocationClient.getCurrentLocation() in Kotlin as there is no example illustrated in the documentation?
According to the documentation, the getCurrentLocation() takes two parameters.
The 1st parameter it takes is the priority (e.g. PRIORITY_HIGH_ACCURACY) to request the most accurate locations available, or any other priority that can be found here.
The 2nd parameter it takes is a cancellation token that can be used to cancel the current location request.
From the Google play services reference, a CancellationToken can only be created by creating a new instance of CancellationTokenSource.
so here is the code you need to use when using getCurrentLocation()
class YourActivity : AppCompatActivity() {
private lateinit var fusedLocationClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.your_layout)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
fusedLocationClient.getCurrentLocation(LocationRequest.PRIORITY_HIGH_ACCURACY, object : CancellationToken() {
override fun onCanceledRequested(p0: OnTokenCanceledListener) = CancellationTokenSource().token
override fun isCancellationRequested() = false
})
.addOnSuccessListener { location: Location? ->
if (location == null)
Toast.makeText(this, "Cannot get location.", Toast.LENGTH_SHORT).show()
else {
val lat = location.latitude
val lon = location.longitude
}
}
}
}
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, object : CancellationToken() {
override fun onCanceledRequested(listener: OnTokenCanceledListener) = CancellationTokenSource().token
override fun isCancellationRequested() = false
})
.addOnSuccessListener {
if (it == null)
Toast.makeText(this, "Cannot get location.", Toast.LENGTH_SHORT).show()
else {
val lat = it.latitude
val lon = it.longitude
}
}
I know the questio is about Kotlin but I want to add for those searching for Java :
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, cancellationTokenSource.getToken())
.addOnSuccessListener(MyActivity.this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
//do your thing
}
Log.w(TAG, "No current location could be found");
}
});

Kotlin: FusedLocationProviderClient.removeLocationUpdates always return false

This question seems like a duplicate of this one but trust me I've tried the solutions but couldn't figure out the reason behind my problem. I'm a newbie in Kotlin and I'm requesting location updates with the FusedLocationProvider.
Relevant Code:
private lateinit var mLocationCallback: LocationCallback? = null
private lateinit var mFusedLocationProviderClient: FusedLocationProviderClient
private lateinit var mLocationRequest: LocationRequest
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(mContext)
// Create a LocationRequest
mLocationRequest = LocationRequest.create().apply {
interval = java.util.concurrent.TimeUnit.SECONDS.toMillis(1000)
smallestDisplacement = 0f
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates more frequently than this value.
fastestInterval = java.util.concurrent.TimeUnit.SECONDS.toMillis(1000)
// Sets the maximum time when batched location updates are delivered. Updates may be
// delivered sooner than this interval.
maxWaitTime = java.util.concurrent.TimeUnit.MINUTES.toMillis(1000)
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
// Initialize the LocationCallback.
mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
// Normally, you want to save a new location to a database. We are simplifying
// things a bit and just saving it as a local variable, as we only need it again
mCurrentLocation = locationResult.lastLocation
//Get Date Time
// saveLatLngData(this,mCurrentLocation.latitude.toString(),mCurrentLocation.longitude.toString())
mTimeStamp = Calendar.getInstance().time
Log.d(TAG, "LocationCallback=> TimeStamp: " + mDateFormat!!.format(mTimeStamp) + " Latitude: " + mCurrentLocation!!.latitude + " - Longitude: " + mCurrentLocation!!.longitude + " - Altitude: " + mCurrentLocation!!.altitude)
mNotificationText = mDateFormat!!.format(mTimeStamp) + "\nLat: ${locationResult.lastLocation.latitude} | Long: ${locationResult.lastLocation.longitude}"
createForegroundInfo()
}
}
// Subscribe to location changes.
if (ActivityCompat.checkSelfPermission(mContext,Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext,Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if(mFusedLocationProviderClient != null)
{
mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest,mLocationCallback,Looper.getMainLooper())
Log.d(TAG, "startMonitoring: mFusedLocationProviderClient is registered now.")
}
else
{
Log.d(TAG, "startMonitoring: mFusedLocationProviderClient is not initialized!")
}
return
}
//STOPPING LOCATION UPDATES (AFTER GETTING SOME VALUES) USING THE FOLLOWING CODE
if (mFusedLocationProviderClient != null) {
try {
val voidTask: Task<Void> = mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback)
if (voidTask.isSuccessful)
{
Log.d(TAG, "stopMonitoring: removeLocationUpdates successful.")
}
else
{
Log.d(TAG, "stopMonitoring: removeLocationUpdates updates unsuccessful! " + voidTask.toString())
}
}
catch (exp: SecurityException)
{
Log.d(TAG, "stopMonitoring: Security exception.")
}
}
Expected Behavior:
Upon calling the mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback) method, the location updates should be removed and stopMonitoring: removeLocationUpdates successful. should be printed on the logger.
Problem:
Location updates are not being removed as I see stopMonitoring: removeLocationUpdates updates unsuccessful! on the logger which means that the else condition is executed.
I tried to find out the reason and applied some solutions as well. Please guide me a little about the possible options I have.
A Task takes some time to complete. You expect it to be finished right away, which is very unlikely to happen. You should do the check like this:
try {
val voidTask: Task<Void> = mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback)
voidTask.addOnCompleteListener {
if(it.isSuccessful) {
Log.d("Location", "stopMonitoring: removeLocationUpdates successful.")
} else {
Log.d("Location", "stopMonitoring: removeLocationUpdates updates unsuccessful! " + voidTask.toString())
}
}
} catch (exp: SecurityException){
Log.d("Location", "stopMonitoring: Security exception.")
}

The last known location is lost when we are not in getLastLocation() - Android

I am trying to create a method that will get the last known location and assign that value to a global variable that I will use later. I am following this training.
In the end, this is the code that I've wrote:
public void getCurrentLocation() {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
fusedLocationClient.getLastLocation().addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
// Logic to handle location object
mCurrentLocation = new Location(location);
System.out.println("In method getCurrentLocation: Latitude = " + location.getLatitude() + " Longitude = " + location.getLongitude());
System.out.println("In method getCurrentLocation for mCurrentLocation: Latitude = " + mCurrentLocation.getLatitude() + " Longitude = " + mCurrentLocation.getLongitude());
} else {
Toast.makeText(MainActivity.this, "Can't get the current location", Toast.LENGTH_SHORT).show();
}
}
});
}
The getCurrentLocation() method I will call in the onCreate() method of MainActivity and I can see the prints from the method so my variable got a location, but if I try to use the mCurrentLocation in some other context (for example I want to go to the last known location using this variable) the variable will be null. I am not sure that I need new Location(location), I added it because I thought that my variable will point to a null place in the memory after we exit the method, but it looks that it is not the case (I get the same outcome).
According to official documentation, Last Known Location could be Null in case of:
Location is turned off in the device settings. As it clears the
cache.
The device never recorded its location. (New device)
Google Play services on the device has restarted.
In this case, you should requestLocationUpdates and receive the new location on the LocationCallback.
By the following steps your last known Location never null.
Pre-requisite:
EasyPermission library
Step 1:
In manifest file add this permission
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Step 2:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Create location callback when it's ready.
createLocationCallback()
//createing location request, how mant request would be requested.
createLocationRequest()
//Build check request location setting request
buildLocationSettingsRequest()
//FusedLocationApiClient which includes location
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
//Location setting client
mSettingsClient = LocationServices.getSettingsClient(this)
//Check if you have ACCESS_FINE_LOCATION permission
if (!EasyPermissions.hasPermissions(
this#MainActivity,
Manifest.permission.ACCESS_FINE_LOCATION)) {
requestPermissionsRequired()
}
else{
//If you have the permission we should check location is opened or not
checkLocationIsTurnedOn()
}
}
Step 3:
Create required functions to be called in onCreate()
private fun requestPermissionsRequired() {
EasyPermissions.requestPermissions(
this,
getString(R.string.location_is_required_msg),
LOCATION_REQUEST,
Manifest.permission.ACCESS_FINE_LOCATION
)
}
private fun createLocationCallback() {
//Here the location will be updated, when we could access the location we got result on this callback.
mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
mCurrentLocation = locationResult.lastLocation
}
}
}
private fun buildLocationSettingsRequest() {
val builder = LocationSettingsRequest.Builder()
builder.addLocationRequest(mLocationRequest!!)
mLocationSettingsRequest = builder.build()
builder.setAlwaysShow(true)
}
private fun createLocationRequest() {
mLocationRequest = LocationRequest.create()
mLocationRequest!!.interval = 0
mLocationRequest!!.fastestInterval = 0
mLocationRequest!!.numUpdates = 1
mLocationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
public fun checkLocationIsTurnedOn() { // Begin by checking if the device has the necessary location settings.
mSettingsClient!!.checkLocationSettings(mLocationSettingsRequest)
.addOnSuccessListener(this) {
Log.i(TAG, "All location settings are satisfied.")
startLocationUpdates()
}
.addOnFailureListener(this) { e ->
val statusCode = (e as ApiException).statusCode
when (statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
try {
val rae = e as ResolvableApiException
rae.startResolutionForResult(this#MainActivity, LOCATION_IS_OPENED_CODE)
} catch (sie: IntentSender.SendIntentException) {
}
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
mRequestingLocationUpdates = false
}
}
}
}
private fun startLocationUpdates() {
mFusedLocationClient!!.requestLocationUpdates(
mLocationRequest,
mLocationCallback, null
)
}
Step 4:
Handle callbacks in onActivityResult() after ensuring the location is opened or the user accepts to open it in.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
LOCATION_IS_OPENED_CODE -> {
if (resultCode == AppCompatActivity.RESULT_OK) {
Log.d(TAG, "Location result is OK")
} else {
activity?.finish()
}
}
}
Step 5:
Get last known location from FusedClientApi
override fun onMapReady(map: GoogleMap) {
mMap = map
mFusedLocationClient.lastLocation.addOnSuccessListener {
if(it!=null){
locateUserInMap(it)
}
}
}
private fun locateUserInMap(location: Location) {
showLocationSafetyInformation()
if(mMap!=null){
val currentLocation = LatLng(location.latitude,location.longitude )
addMarker(currentLocation)
}
}
private fun addMarker(currentLocation: LatLng) {
val cameraUpdate = CameraUpdateFactory.newLatLng(currentLocation)
mMap?.clear()
mMap?.addMarker(
MarkerOptions().position(currentLocation)
.title("Current Location")
)
mMap?.moveCamera(cameraUpdate)
mMap?.animateCamera(cameraUpdate)
mMap?.setMinZoomPreference(14.0f);
}
I hope this would help.
Happy Coding 🤓
A few weeks later I came up with some sort of a explanation to what is happening and how I managed to get the result that I want. I only had to do the first 3 steps that #MustafaKhaled wrote in his answer, this will make the mFusedLocationClient global variable to have its getLastLocation() method called (I think this happens because I request location updates, see #MustafaKhaled post).
Now, what we do with the last location? The response is nothing, because we do not handle what will happen if we successfully retrieve the location.
That being said, we will need to add what will happen onSuccess() with the location. In my case, I had to retain the value of the mCurrentLocation so I still need the method from the initial post to be implemented:
public void AddFusedLocationClientOnSuccessListener() {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mFusedLocationClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
// Logic to handle location object
mCurrentLocation = location;
}
}
});
}
Here I will be able to do some other things with the location if needed (for example if I want the application to animate to the current location), but later, if I when I use buttons or other graphical elements, I will be able to use the mCurrentLocation variable.
Now, I will be able to call the mCurrentLocation directly in the onCreate()? The answer is NO, because we will have to wait for mFusedLocationClient to successfully get the last known location, otherwise themCurrentLocation will be null, that is the reason in the initial post it did not work, we were not calling the getLastLocation() method until we will get success.
I hope this answer will be helpful for other peoples that encounter this problem and they don't fully understand what happens in the onCreate().

Kotlin - Issue in Location tracking

I must have to take latitude and longitude of the user when user first time open an application.
So far, I have done as below :
Necessary Variables :
//Location Utils below :
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var lastLocation: Location
private lateinit var currentLatLng: LatLng
On Button Click : Checking the permission for the location, if has permission calling method named onLocationGranted() else asking for the location permission.
if (EasyPermissions.hasPermissions(
mContext,
FilePickerConst.PERMISSIONS_FINE_LOCATION
)
) {
onLocationGranted()
} else {
// Ask for one permission
EasyPermissions.requestPermissions(
this,
getString(R.string.permission_location),
Constant.MultiMediaRequestCode.LOCATION_PERMISSION,
FilePickerConst.PERMISSIONS_FINE_LOCATION
)
}
Below is the method onLocationGranted() : Here, Initializing LocationManager, Checking that GPS is on or not. If GPS on or enabled, taking location which is done in method named : getAndSaveCurrentLocation(). If, GPS is off or not enabled calling the method named buildAlertMessageNoGps()
val manager = mContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
//If GPS is not Enabled..
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
buildAlertMessageNoGps()
} else {
getAndSaveCurrentLocation()
}
Method getAndSaveCurrentLocation() is as below : In which I am taking location using fusedapi.
private fun getAndSaveCurrentLocation() {
try {
fusedLocationClient =
LocationServices.getFusedLocationProviderClient(mContext as AppBaseActivity)
fusedLocationClient.lastLocation.addOnSuccessListener(mContext as AppBaseActivity) { location ->
// Got last known location. In some rare situations this can be null.
if (location != null) {
lastLocation = location
currentLatLng = LatLng(location.latitude, location.longitude)
if (currentLatLng != null &&
currentLatLng.latitude != null &&
currentLatLng.longitude != null
) {
sharedPreferenceManager?.setStringData(
Constant.PrefKey.userLatitude,
"" + currentLatLng.latitude
)
sharedPreferenceManager?.setStringData(
Constant.PrefKey.userLongitude,
"" + currentLatLng.longitude
)
}
}
(mContext as AppBaseActivity).supportFragmentManager.popBackStack()
(activity as AppBaseActivity).addFragmentToRoot(
DashboardFragmentNew.newInstance(),
false
)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Method buildAlertMessageNoGps() is as below : If GPS is off then, I am calling this method.
private fun buildAlertMessageNoGps() {
val builder = AlertDialog.Builder(mContext)
builder.setMessage("Your GPS seems to be disabled, do you want to enable it?")
.setCancelable(false)
.setPositiveButton("Yes", object : DialogInterface.OnClickListener {
override fun onClick(dialog: DialogInterface, id: Int) {
startActivity(Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS))
}
})
.setNegativeButton("No", object : DialogInterface.OnClickListener {
override fun onClick(dialog: DialogInterface, id: Int) {
dialog.cancel()
}
})
val alert = builder.create()
alert.show()
}
Now, the above method opens the settings to turn on GPS.
My question is : Suppose user turns on GPS, and coming back to screen, How can I get location then ?
Or If user not turning on GPS there, in that case How can I take location?
Thanks.
Suppose user turns on GPS, and coming back to screen, How can I get location then ?
You will get a call back to onActivityReenter similar to onActivityResult. So Override that method and call getAndSaveCurrentLocation() there.

getLastKnownLocation null - how to get data?

I'm trying to get Location information using getLastKnownLocation but I often get null.
Here is what I do:
Turn OFF location in Settings
Run my app - getLastKnownLocation returns null - this is expected
Turn ON location in Settings
Run my app again - getLastKnownLocation returns null !?
Run Google Maps
Run my app again - getLastKnownLocation returns a valid Location !?
I found this question where the accepted answer suggests to launch Maps first in order to get the location:
FusedLocationApi.getLastLocation always null
The questions is: what is Google Maps doing to get the location? And how can I do the same in my code?
Thanks!
Thanks everyone for the help!
I was missing uses-features declarations in my manifest - thanks #CommonsWare for the link: https://developer.android.com/guide/topics/location/strategies.html#java
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.location.network" />
Also, requesting updates like this is needed as well:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
In my case the GPS provider needs a few minutes to connect and start sending data - the Network provider seems to be faster but does not always work - so I'll use both of them.
Update: a few key points
It's not really intuitive, but in order to get data from getLastKnownLocation some app on the device - be it Google Maps or our own app - needs to call requestLocationUpdates.
Some providers might not be enabled on a particular device - so it's a good idea to use both the GPS_PROVIDER and the NETWORK_PROVIDER - or use all enabled providers: locationManager.getProviders(true)
Here is the code I ended up using:
for ( String provider : locationManager.getProviders(true) ) {
setLocation( locationManager.getLastKnownLocation(provider) );
locationManager.requestLocationUpdates(provider, Tools.MIN_5, 0, locationListener);
}
// call setLocation again in locationListener.onLocationChanged
here is a code chunk which may help you.
if (!::mFusedLocationProviderClient.isInitialized) {
mFusedLocationProviderClient = FusedLocationProviderClient(mContext)
mLocationRequest = LocationRequest()
mLocationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
mLocationRequest.interval = 10000
mLocationRequest.fastestInterval = 5000
mLocationRequest.smallestDisplacement = 1000 * 100f
}
if (ActivityCompat.checkSelfPermission(
AppName.appContext,
android.Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
AppName.appContext,
android.Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// not enough permission
return
}
mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest, object : LocationCallback() {
override fun onLocationResult(p0: LocationResult?) {
super.onLocationResult(p0)
mFusedLocationProviderClient.removeLocationUpdates(this)
if (p0 != null) {
//here is your location
} else {
// location is null
}
}
override fun onLocationAvailability(p0: LocationAvailability?) {
if(p0?.isLocationAvailable!= true){
//location not available.
}
}
}, Looper.getMainLooper())
make sure you give all permission and if you run app above 6.1 device then handle permission..
after that used this code..
add below dependency into app level gradle file..
//Place API
implementation 'com.google.android.gms:play-services-places:16.0.0'
implementation 'com.google.android.gms:play-services-location:16.0.0'
define this into class level as global deceleration.
private lateinit var fusedLocationClient: FusedLocationProviderClient
private var context: Context? = null
private var locationCallback: LocationCallback? = null
private var locationRequest: LocationRequest? = null
private var googleApiClient: GoogleApiClient? = null
after that onCreate method..
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
make method for getting location..
fun getCurrentLocation() {
// Get Current location and do reverse geocoding
ProgressUtils.showOldProgressDialog(this)
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
ProgressUtils.closeOldProgressDialog()
for (location in locationResult.locations) {
// here you get current lat ,long,etc value..
stopLocationUpdates()
}
} catch (e: Exception) {
e.message.loggerError()
stopLocationUpdates()
}
}
}
}
locationRequest = LocationRequest().apply {
interval = 10000
fastestInterval = 5000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
startLocationUpdates()
}
make location update..
fun startLocationUpdates() {
googleApiClient = GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.build()
googleApiClient!!.connect()
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest!!)
builder.setAlwaysShow(true)
val result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build())
result.setResultCallback { result ->
val status = result.status
when (status.statusCode) {
LocationSettingsStatusCodes.SUCCESS -> {
Log.i(TAG, "All location settings are satisfied.")
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
}
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
Log.i(TAG, "Location settings are not satisfied. Show the user a dialog to upgrade location settings ")
try {
// Show the dialog by calling startResolutionForResult(), and check the result
// in onActivityResult().
status.startResolutionForResult(this, LOCATION_SETTING_REQUEST_CODE)
} catch (e: IntentSender.SendIntentException) {
Log.i(TAG, "PendingIntent unable to execute request.")
}
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> Log.i(TAG, "Location settings are inadequate, and cannot be fixed here. Dialog not created.")
}
}
}
make method for stop location..
private fun stopLocationUpdates() {
if (locationCallback != null) {
fusedLocationClient?.removeLocationUpdates(locationCallback)
}
}
override fun onStop() {
super.onStop()
stopLocationUpdates()
}
override fun onPause() {
super.onPause()
stopLocationUpdates()
}
if handle permission than permission is grant then call back getting onActivity Result method there put below code..
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)

Categories

Resources