Here, you can use this...
Example usage:
public void foo(Context context) {
// when you need location
// if inside activity context = this;
SingleShotLocationProvider.requestSingleUpdate(context,
new SingleShotLocationProvider.LocationCallback() {
@Override public void onNewLocationAvailable(GPSCoordinates location) {
Log.d("Location", "my location is " + location.toString());
}
});
}
You might want to verify the lat/long are actual values and not 0 or something. If I remember correctly this shouldn't throw an NPE but you might want to verify that.
public class SingleShotLocationProvider {
public static interface LocationCallback {
public void onNewLocationAvailable(GPSCoordinates location);
}
// calls back to calling thread, note this is for low grain: if you want higher precision, swap the
// contents of the else and if. Also be sure to check gps permission/settings are allowed.
// call usually takes <10ms
public static void requestSingleUpdate(final Context context, final LocationCallback callback) {
final LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (isNetworkEnabled) {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
locationManager.requestSingleUpdate(criteria, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
callback.onNewLocationAvailable(new GPSCoordinates(location.getLatitude(), location.getLongitude()));
}
@Override public void onStatusChanged(String provider, int status, Bundle extras) { }
@Override public void onProviderEnabled(String provider) { }
@Override public void onProviderDisabled(String provider) { }
}, null);
} else {
boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (isGPSEnabled) {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
locationManager.requestSingleUpdate(criteria, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
callback.onNewLocationAvailable(new GPSCoordinates(location.getLatitude(), location.getLongitude()));
}
@Override public void onStatusChanged(String provider, int status, Bundle extras) { }
@Override public void onProviderEnabled(String provider) { }
@Override public void onProviderDisabled(String provider) { }
}, null);
}
}
}
// consider returning Location instead of this dummy wrapper class
public static class GPSCoordinates {
public float longitude = -1;
public float latitude = -1;
public GPSCoordinates(float theLatitude, float theLongitude) {
longitude = theLongitude;
latitude = theLatitude;
}
public GPSCoordinates(double theLatitude, double theLongitude) {
longitude = (float) theLongitude;
latitude = (float) theLatitude;
}
}
}
Answer from Ashton Engberg on Stack OverflowHere, you can use this...
Example usage:
public void foo(Context context) {
// when you need location
// if inside activity context = this;
SingleShotLocationProvider.requestSingleUpdate(context,
new SingleShotLocationProvider.LocationCallback() {
@Override public void onNewLocationAvailable(GPSCoordinates location) {
Log.d("Location", "my location is " + location.toString());
}
});
}
You might want to verify the lat/long are actual values and not 0 or something. If I remember correctly this shouldn't throw an NPE but you might want to verify that.
public class SingleShotLocationProvider {
public static interface LocationCallback {
public void onNewLocationAvailable(GPSCoordinates location);
}
// calls back to calling thread, note this is for low grain: if you want higher precision, swap the
// contents of the else and if. Also be sure to check gps permission/settings are allowed.
// call usually takes <10ms
public static void requestSingleUpdate(final Context context, final LocationCallback callback) {
final LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (isNetworkEnabled) {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
locationManager.requestSingleUpdate(criteria, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
callback.onNewLocationAvailable(new GPSCoordinates(location.getLatitude(), location.getLongitude()));
}
@Override public void onStatusChanged(String provider, int status, Bundle extras) { }
@Override public void onProviderEnabled(String provider) { }
@Override public void onProviderDisabled(String provider) { }
}, null);
} else {
boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (isGPSEnabled) {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
locationManager.requestSingleUpdate(criteria, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
callback.onNewLocationAvailable(new GPSCoordinates(location.getLatitude(), location.getLongitude()));
}
@Override public void onStatusChanged(String provider, int status, Bundle extras) { }
@Override public void onProviderEnabled(String provider) { }
@Override public void onProviderDisabled(String provider) { }
}, null);
}
}
}
// consider returning Location instead of this dummy wrapper class
public static class GPSCoordinates {
public float longitude = -1;
public float latitude = -1;
public GPSCoordinates(float theLatitude, float theLongitude) {
longitude = theLongitude;
latitude = theLatitude;
}
public GPSCoordinates(double theLatitude, double theLongitude) {
longitude = (float) theLongitude;
latitude = (float) theLatitude;
}
}
}
For anyone interested in retrieving a single location update, in the best, idiomatic way, using the latest APIs, and the magic of Kotlin, here you go:
Gradle dependency:
dependencies {
...
implementation "com.google.android.gms:play-services-location:18.0.0"
...
}
Manifest permissions:
<manifest>
...
<!-- required only for LocationRequest.PRIORITY_HIGH_ACCURACY -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- required for all other priorities -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
...
</manifest>
Somewhere in your Extensions file:
// To use PRIORITY_HIGH_ACCURACY, you must have ACCESS_FINE_LOCATION permission.
// Any other priority will require just ACCESS_COARSE_LOCATION,
// but will not guarantee a location update
@SuppressLint("MissingPermission")
suspend fun FusedLocationProviderClient.awaitCurrentLocation(priority: Int): Location? {
return suspendCancellableCoroutine {
// to use for request cancellation upon coroutine cancellation
val cts = CancellationTokenSource()
getCurrentLocation(priority, cts.token)
.addOnSuccessListener {location ->
// remember location is nullable, this happens sometimes
// when the request expires before an update is acquired
it.resume(location)
}.addOnFailureListener {e ->
it.resumeWithException(e)
}
it.invokeOnCancellation {
cts.cancel()
}
}
}
In your fragment:
// need to register this anywhere before onCreateView, idealy as a field
private val permissionRequester = registerForActivityResult(
// you can use RequestPermission() contract if you only need 1 permission
ActivityResultContracts.RequestMultiplePermissions()
) { map ->
// If you requested 1 permission, change `map` to `isGranted`
// Keys are permissions Strings, values are isGranted Booleans
// An easy way to check if "any" permission was granted is map.containsValue(true)
// You can use your own logic for multiple permissions,
// but they have to follow the same checks here:
val response = map.entries.first()
val permission = response.key
val isGranted = response.value
when {
isGranted -> onPermissionGranted()
ActivityCompat.shouldShowRequestPermissionRationale(requireContext(), permission) -> {
// permission denied but not permanently, tell user why you need it.
// Idealy provide a button to request it again and another to dismiss
AlertDialog.Builder(requireContext())
.setTitle(R.string.perm_request_rationale_title)
.setMessage(R.string.perm_request_rationale)
.setPositiveButton(R.string.request_perm_again) { _, _ ->
requirePermission()
}
.setNegativeButton(R.string.dismiss, null)
.create()
.show()
}
else -> {
// permission permanently denied
// 1) tell user the app won't work as expected, or
// 2) take him to your app's info screen to manually change permissions, or
// 3) silently and gracefully degrade user experience
// I'll leave the implementation to you
}
}
}
onPermissionGranted function:
private fun onPermissionGranted() {
val lm = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
if(LocationManagerCompat.isLocationEnabled(lm)) {
// you can do this your own way, eg. from a viewModel
// but here is where you wanna start the coroutine.
// Choose your priority based on the permission you required
val priority = LocationRequest.PRIORITY_HIGH_ACCURACY
lifecycleScope.launch {
val location = LocationServices
.getFusedLocationProviderClient(requireContext())
.awaitCurrentLocation(priority)
// do whatever with this location, notice that it's nullable
}
} else {
// prompt user to enable location or launch location settings check
}
}
Now all you have to do is add this to MyLocation button click listener:
private fun requirePermission() {
val permissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
// optional: Manifest.permission.ACCESS_COARSE_LOCATION
)
permissionRequester.launch(permissions)
}
Note that this has the beauty of checking if the permission was already given implicitly, and not show a dialog/request if that was the case. Ergo, always start your flow by launching the requester and only do your checks in its callback.
Dont use the getLastKnownLocation because that could be returning null or old data.
This code Only fetches the location once a button is pressed and not every time. People use to leave the location listener listen in every instance and that kills the battery life so Use the code snippet I have posted by doing lots of research:
// get the text view and buttons from the xml layout
Button button = (Button) findViewById(R.id.btnGetLocation);
final TextView latitude = (TextView) findViewById(R.id.textview4);
final TextView longitude = (TextView) findViewById(R.id.textview5);
final LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
mlocation = location;
Log.d("Location Changes", location.toString());
latitude.setText(String.valueOf(location.getLatitude()));
longitude.setText(String.valueOf(location.getLongitude()));
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.d("Status Changed", String.valueOf(status));
}
@Override
public void onProviderEnabled(String provider) {
Log.d("Provider Enabled", provider);
}
@Override
public void onProviderDisabled(String provider) {
Log.d("Provider Disabled", provider);
}
};
// Now first make a criteria with your requirements
// this is done to save the battery life of the device
// there are various other other criteria you can search for..
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setPowerRequirement(Criteria.POWER_LOW);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setSpeedRequired(false);
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);
// Now create a location manager
final LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
// This is the Best And IMPORTANT part
final Looper looper = null;
// Now whenever the button is clicked fetch the location one time
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
locationManager.requestSingleUpdate(criteria, locationListener, looper);
}
});
First check if the last know location is recent. If not, I believe you must to set up onLocationChanged listener, but once you get your first valid location you can always stop the stream of updates.
Addition
public class Example extends Activity implements LocationListener {
LocationManager mLocationManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Location location = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(location != null && location.getTime() > Calendar.getInstance().getTimeInMillis() - 2 * 60 * 1000) {
// Do something with the recent location fix
// otherwise wait for the update below
}
else {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}
}
public void onLocationChanged(Location location) {
if (location != null) {
Log.v("Location Changed", location.getLatitude() + " and " + location.getLongitude());
mLocationManager.removeUpdates(this);
}
}
// Required functions
public void onProviderDisabled(String arg0) {}
public void onProviderEnabled(String arg0) {}
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {}
}
Videos
You can get the last know location using the code below. It gets the location providers and loops over the array backwards. i.e starts with GPS, if no GPS then gets network location. You can call this method whenever you need to get the location.
private double[] getGPS() {
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
List<String> providers = lm.getProviders(true);
/* Loop over the array backwards, and if you get an accurate location, then break out the loop*/
Location l = null;
for (int i=providers.size()-1; i>=0; i--) {
l = lm.getLastKnownLocation(providers.get(i));
if (l != null) break;
}
double[] gps = new double[2];
if (l != null) {
gps[0] = l.getLatitude();
gps[1] = l.getLongitude();
}
return gps;
}
You can do this with LocationManager.getLastKnownLocation
Although requestSingleUpdate() is the technically correct answer, you should use
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, mLocationListener);
Wait for a while after getting your first location. The location tends to wobble for a few seconds. To determine if the fix is stable use, location.getAccuracy(). Once the accuracy stabilizes, call locationManager.removeUpdates(mLocationListener);
public LocationManager locationManager;
try {
locationManager.requestSingleUpdate( LocationManager.GPS_PROVIDER, new MyLocationListenerGPS(), null );
} catch ( SecurityException e ) { e.printStackTrace(); }
public class MyLocationListenerGPS implements LocationListener {
@Override
public void onLocationChanged(Location location) {
// your code and required methods go here...
}
Using 'null' in 'requestSingleUpdate' keeps the update check on the same thread (for use in a service). The method allows for you to put it in a Looper if you're running it from an activity. I would think that using an async task would be the better approach.
So here's how you can get your current location. I am skipping the permission part as you can do that yourself.
private fun isLocationEnabled(): Boolean {
val locationManager: LocationManager =
getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(
LocationManager.NETWORK_PROVIDER
)
}
private fun getLastLocation() {
if (isLocationEnabled()) {
fusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->
val location: Location? = task.result
if (location != null) {
//use the location latitude and logitude as per your use.
val latitude = location.latitude
val longitude = location.longitude
}
}
}
you need to request location updates try the following for instance
val fusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(activity)
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val location = locationResult.lastLocation
if (location != null) {
val latitude = location.latitude
val longitude = location.longitude
// Use latitude and longitude for your purposes
Log.d("Location", "Latitude: $latitude, Longitude: $longitude")
// Remove location updates after receiving one
fusedLocationProviderClient.removeLocationUpdates(this)
} else {
Log.w("Location", "Failed to get location update")
}
}
}
if (ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// Request permissions if not granted
val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
activity.requestPermissions(permissions, LOCATION_PERMISSION_CODE)
return
}
val locationRequest =
LocationRequest.Builder(0L).setPriority(Priority.PRIORITY_HIGH_ACCURACY).build()
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, null)
First you need to define a LocationListener to handle location changes.
int LOCATION_REFRESH_TIME = 15000; // 15 seconds to update
int LOCATION_REFRESH_DISTANCE = 500; // 500 meters to update
....
private final LocationListener mLocationListener = new LocationListener() {
@Override
public void onLocationChanged(final Location location) {
//your code here
}
};
Then get the LocationManager and ask for location updates
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_REFRESH_TIME,
LOCATION_REFRESH_DISTANCE, mLocationListener);
}
And finally make sure that you have added the permission on the Manifest,
For using only network based location use this one
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
For GPS based location, this one
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
You need to write code in the OnLocationChanged method, because this method is called when the location has changed. I.e. you need to save the new location to return it if getLocation is called.
If you don't use the onLocationChanged it always will be the old location.
I have created a small application with step by step description to get current location's GPS coordinates.
Complete example source code is in Get Current Location coordinates , City name - in Android.
See how it works:
All we need to do is add this permission in the manifest file:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />And create a LocationManager instance like this:
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);Check if GPS is enabled or not.
And then implement LocationListener and get coordinates:
LocationListener locationListener = new MyLocationListener(); locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 5000, 10, locationListener);Here is the sample code to do so
/*---------- Listener class to get coordinates ------------- */
private class MyLocationListener implements LocationListener {
@Override
public void onLocationChanged(Location loc) {
editLocation.setText("");
pb.setVisibility(View.INVISIBLE);
Toast.makeText(
getBaseContext(),
"Location changed: Lat: " + loc.getLatitude() + " Lng: "
+ loc.getLongitude(), Toast.LENGTH_SHORT).show();
String longitude = "Longitude: " + loc.getLongitude();
Log.v(TAG, longitude);
String latitude = "Latitude: " + loc.getLatitude();
Log.v(TAG, latitude);
/*------- To get city name from coordinates -------- */
String cityName = null;
Geocoder gcd = new Geocoder(getBaseContext(), Locale.getDefault());
List<Address> addresses;
try {
addresses = gcd.getFromLocation(loc.getLatitude(),
loc.getLongitude(), 1);
if (addresses.size() > 0) {
System.out.println(addresses.get(0).getLocality());
cityName = addresses.get(0).getLocality();
}
}
catch (IOException e) {
e.printStackTrace();
}
String s = longitude + "\n" + latitude + "\n\nMy Current City is: "
+ cityName;
editLocation.setText(s);
}
@Override
public void onProviderDisabled(String provider) {}
@Override
public void onProviderEnabled(String provider) {}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
There are already many answers there but I want to show latest way to get location using Google API, so new programmers can use new method:
I have written detailed tutorial on current location in android at my blog demonuts.com You can also find full source code developed with android studio.
First of all, put this in gradle file
compile 'com.google.android.gms:play-services:9.0.2'
then implement necessary interfaces
public class MainActivity extends BaseActivitiy implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener
declare instances
private GoogleApiClient mGoogleApiClient;
private Location mLocation;
private LocationManager locationManager;
private LocationRequest mLocationRequest;
put this in onCreate()
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
At last, override necessary methods
@Override
public void onConnected(Bundle bundle) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
} startLocationUpdates();
mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if(mLocation == null){
startLocationUpdates();
}
if (mLocation != null) {
double latitude = mLocation.getLatitude();
double longitude = mLocation.getLongitude();
} else {
// Toast.makeText(this, "Location not Detected", Toast.LENGTH_SHORT).show();
}
}
protected void startLocationUpdates() {
// Create the location request
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(UPDATE_INTERVAL)
.setFastestInterval(FASTEST_INTERVAL);
// Request location updates
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
Log.d("reque", "--->>>>");
}
@Override
public void onConnectionSuspended(int i) {
Log.i(TAG, "Connection Suspended");
mGoogleApiClient.connect();
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.i(TAG, "Connection failed. Error: " + connectionResult.getErrorCode());
}
@Override
public void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
@Override
public void onStop() {
super.onStop();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
@Override
public void onLocationChanged(Location location) {
}
Don't forget to start GPS in your device before running app.