Get last position known in foreground Android - java

After several (unsuccessful) attempts to make my applications compliant about the background access to the location, I decided to re-structure my code in order to remove the ACCESS_BACKGROUND_LOCATION permission from the manifest.
My application necessarily needs to get the location of the device at certain times (specifically I need the coordinates), what I'm interested in knowing is:
without using the permission mentioned above, how do I get, in foreground, the location of the device?
is it possible to do it with a one-time call without using services etc?
I thought about using this code, do you think it could be enough?
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mFusedLocationClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if (location != null) {
// Logic to handle location object
}
}
});
or something like this:
LocationManager mLocationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
List<String> providers = mLocationManager.getProviders(true);
Location bestLocation = null;
for (String provider : providers) {
Location location = mLocationManager.getLastKnownLocation(provider);
if (location == null) {
continue;
}
if (bestLocation == null || location.getAccuracy() < bestLocation.getAccuracy()) {
bestLocation = location;
}
}
is there a better way to do this?

To get the last know location you need to have following permissions declared in Manifest file.
"android.permission.ACCESS_FINE_LOCATION"
Then you may use Fused Location Provider as you have used.
Now if you want periodic updates you may want to register for those updates using following callback.
requestLocationUpdates
passed with a request object and callback to receive updates.
Helpful links with exact code: Request Updates

Related

Unable to clear mock location?

This is how I set a mock location in my app:
public void startMockLocation(String latitude, String longitude){
FusedLocationProviderClient locationProvider = new FusedLocationProviderClient(getApplicationContext());
locationProvider.setMockMode(true);
Location loc = new Location("gps");
mockLocation = new Location("gps");
mockLocation.setLatitude(Double.valueOf(latitude));
mockLocation.setLongitude(Double.valueOf(longitude));
mockLocation.setAltitude(loc.getAltitude());
mockLocation.setTime(System.currentTimeMillis());
mockLocation.setAccuracy(1f);
mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mockLocation.setBearingAccuracyDegrees(0.1f);
mockLocation.setVerticalAccuracyMeters(0.1f);
mockLocation.setSpeedAccuracyMetersPerSecond(0.01f);
}
locationProvider.setMockLocation(mockLocation);
}
However I wasn't able to clear the mock location and set the real location back using this code below. What should I write instead?
public void clearMockLocation() {
locationProvider.setMockMode(false);
}
as i see your code , you are calling two methods one to set mock location and other to disable it but in other method i am not sure which object of location provider you are using , i would prefer you to declare a global object of location provider and use it anywhere
FusedLocationProviderClient locationProvider;
public void startMockLocation(String latitude, String longitude){
locationProvider = new FusedLocationProviderClient(getApplicationContext());
locationProvider.setMockMode(true);
Location loc = new Location("gps");
mockLocation = new Location("gps");
mockLocation.setLatitude(Double.valueOf(latitude));
mockLocation.setLongitude(Double.valueOf(longitude));
mockLocation.setAltitude(loc.getAltitude());
mockLocation.setTime(System.currentTimeMillis());
mockLocation.setAccuracy(1f);
mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mockLocation.setBearingAccuracyDegrees(0.1f);
mockLocation.setVerticalAccuracyMeters(0.1f);
mockLocation.setSpeedAccuracyMetersPerSecond(0.01f);
}
locationProvider.setMockLocation(mockLocation);
}
then in other method
public void clearMockLocation() {
if(locationProvider!=null){
locationProvider.setMockMode(false);
if(mockLocation!=null){
mockLocation.setLatitude(real_latitude);
mockLocation.setLongitude(real_longitude);
mockLocation.setAltitude(real_altitude);
mockLocation.setTime(System.currentTimeMillis());
mockLocation.setAccuracy(1f);
mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mockLocation.setBearingAccuracyDegrees(0.1f);
mockLocation.setVerticalAccuracyMeters(0.1f);
mockLocation.setSpeedAccuracyMetersPerSecond(0.01f);
}
locationProvider.setMockLocation(mockLocation);
}
}
}
Update:
As far, your problem is following these steps such as:
Setting your mock location for Gps
Going to GoogleMaps and see your mock location
Turn back your app and want to stop mocking
Here I am giving you some techniques to disable mock locations.
Method.1
Spoofing or faked locations can be avoided by using the Location Manager's API.
For this you first have to import the google play services LocationServices (must visit) API:
You need to import:
import com.google.android.gms.location.LocationServices;
And in App-level build.gradle:
implementation 'com.google.android.gms:play-services-location:17.0.0'
Your Class must implement these interfaces:
public class TestMapsActivity extends FragmentActivity implements OnMapReadyCallback,
LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener { ...}
Then, you need to Override these methods such as:
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
#Override
public void onLocationChanged(Location location) {
}
Now, We can remove the test provider before requesting the location updates from both the providers (Network or Gps):
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
Log.d(TAG ,"Removing Test providers")
locationManager .removeTestProvider(LocationManager.GPS_PROVIDER);
} catch (IllegalArgumentException error) {
Log.d(TAG,"Got exception in removing test provider");
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListener);
Now, If you look into the Android documentation of the LocationManager:
removeTestProvider() throws
IllegalArgumentException if no provider with the given name exists
You will get a better intuition from this android-issue. For that specific thread, You can try using Criteria.ACCURACY_FINE instead of LocationManager.GPS_PROVIDER such as:
LocationManager locationManager = (LocationManager)context.getSystemService( Context.LOCATION_SERVICE );
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
String locationprovider = locationManager.getBestProvider(criteria, true);
if ( locationprovider == null ) {
Log.e(TAG, "location provider is not available.!");
return;
}
Method.2
If the above method still doesn't work for you, you can silently enable /disable mock settings as follows:
// disable mocking.
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION, "0");
You can also get better intuition here and here.
Method.3
There is another way you can do that to get an accurate understanding whether GPS/Network providers are enabled or not:
ContentResolver contentResolver = context.getContentResolver();
boolean gpsEnabled = Settings.Secure.isLocationProviderEnabled(contentResolver, LocationManager.GPS_PROVIDER);
boolean networkEnabled = Settings.Secure.isLocationProviderEnabled(contentResolver, LocationManager.NETWORK_PROVIDER);
Other Steps to follow.
You should have to follow these steps to clear/reset your mock location such as:
Enable mock locations in the development panel in your settings.
Add permissions to your Manifest.xml i.e.
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
Now again open GoogleMaps, and wait until the Gps provider to receive a new realtime-location. It could be a bit of time-consuming i.e. (1-3 minutes).
So while removing the provider, just let the Gps receive a new fresh location, then it will be resolved and fixed. If in case, it is not working again, then you can further do these steps:
Go to the app settings, Clear the App-Cache and Restart the Mobile
Device.
I hope that it would work really fine. You can also visit these references to get better intuition:
Android-mock-location-on-device
Remove-mock-location

How to run onLocationChanged method when app is closed (with onStop method)

I have this method, which manages my location:
#Override
public void onLocationChanged(final Location location) {
if(inRun){
Clocation myLocation = new Clocation(location, this.useMetricUnits());
this.updateSpeed(myLocation);
this.updateDistance(myLocation);
this.updateAverageSpeed(this.distance, this.chronometer);
if (activity.getText().toString() != ActivityRecongnizedService.getActivity()) {
activity.setText(ActivityRecongnizedService.getActivity());
activityTimer.start();
}
switch (activity.getText().toString()) {
case "STILL": still = true; break;
case "IN VEHICLE": inVehicle = true; break;
case "ON BICYCLE": onBicycle = true; break;
case "ON FOOT": onFoot = true; break;
case "RUNNING": running = true; break;
case "WALKING": walking = true;break;
case "TILTING": tilting = true; break;
}
setTimeActivity();
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String[] key;
key = user.getEmail().split("#");
reference.child("Position").child(key[0]).child("latlng").setValue(new LatLng(location.getLatitude(),location.getLongitude()));
}
}
But I want this method to run when MainActivity is down. I read about this thing and there are two ways to do the same: with service or with onStop method.
I prefer the second way, with onStop method, but I don't know how to call onLocationChanged in onStop method.
I hope this could help you.
In Android, there are location manager, which provides access to system location service.
You can fire onLocationChanged event by using this class.
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
// Get the best provider for the criteria
Criteria criteria = new Criteria();
criteria.setPowerRequirement(Criteria.POWER_LOW);
criteria.setAccuracy(Criteria.ACCURACY_FINE);
String bestprovider = locationManager.getBestProvider(criteria,false);
locationManager.requestLocationUpdates(bestprovider,0, 0, this);
If you insert some of this code in onStop method, the onLocationChanged event will be fired.
Override your onStop method and get the user's last known location in this manner
fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
// Got last known location. In some rare situations this can be null.
}
refer this link for more detail https://developer.android.com/training/location/retrieve-current now that you got the user's location under the call back of onSuccessListener you can update the location to your Firebase Database/Server
Note Depending on your requirements you can run this code on a main thread or on a different thread

Android - Fused Location: How to get force fresh location

I've an app which allows usage if user is in certain area, meaning I don't need to track the user, but I need to make sure location that's received is fresh (even if it requires waiting).
What's the best way to do this? Currently using:
getLastLocation()
but it can sometimes get previous location, understandably.
You can register a location listener and proceed to the code if the location condition is met:
locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
return;
}
for (Location location : locationResult.getLocations()) {
//if(this is the location) jump to the code which makes the app usable
}
//stay unusable
};
};
getting the location might take some time. If by force you mean holding the ui thread until the precise-enough location is available then force getting the location is a bad but feasible idea.

When to use LocationManager/LocationListener VS. LocationServices/LocationCallback to track user location?

I've implemented user geolocation tracking with the following:
#1
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
mMap.clear();
repaintUserLocationPin(location);
}
#2
locationProviderClient = LocationServices.getFusedLocationProviderClient(this);
locationRequest = new LocationRequest();
locationRequest.setInterval(3000);
locationRequest.setFastestInterval(3000);
locationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
LocationCallback locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
List<Location> locationList = locationResult.getLocations();
if (locationList.size() > 0) {
//The last location in the list is the newest
Location location = locationList.get(locationList.size() - 1);
mLastLocation = location;
if (mCurrLocationMarker != null) {
mCurrLocationMarker.remove();
}
repaintUserLocationPin(mLastLocation);
}
}
};
To summarize IMHO:
These both seem to achieve the same,track and update the current user's location.
At the end of the day, they are both equally verbose and have the same complexity.
#2 needs new 'implementation' libraries but, this isn't much of an overhead
On the other hand, there is a 3rd solution but this doesn't allow me to get the updated current location unless, I click on the Geolocate button in the map. I'm talking about:
map.setOnMyLocationButtonClickListener(new GeolocateBtnClickListener(MapsActivity.this));
map.setOnMyLocationClickListener(new GeolocateClickListener(MapsActivity.this));
This has one BIG advantage, and it's that the marker is automatically created and it is much more responsive to the user's movements. For example, it points in the directo that the user is pointing at.
So, Why use one over the other or in what cases would you recommend using one over the other?
All the below is just my opinions
These both seem to achieve the same,track and update the current
user's location.
The difference can be realized with their location obtain ways. LocationManager gives us chance of choosing the location source. So then we can pick any source from gps, network and passive alternatives. But LocationServices does not give this chance, instead gives taste of location, the momentum of between quality and battery usage.
At the end of the day, they are both equally verbose and have the same
complexity.
Configuring them seems a bit same, but LocationServices could be first option. Deciding quality/power seems more reasonable than dealing with providers.
#2 needs new 'implementation' libraries but, this isn't much of an overhead
LocationManager is available from version 1, while LocationServices is available if device supports play services.

`locationManager.isProviderEnabled(locationManager.GPS_PROVIDER);` always returns true

I am testing an app which uses Location services. And it returns null causing NPE (Null Pointer Exception) whenever my Location Services are turned off. So after some searching I found that
isGPSEnabled = locationManager.isProviderEnabled(locationManager.GPS_PROVIDER);
should return the correct boolean value but when testing my app it seems to always return true causing a NPE (Null Pointer Exception) and crashing my app.
I also read this question. Which has the exact opposite problem apparently and tried that solution. That also didn't work. I am testing on a Samsung G5. Why is this happening. Is something wrong in the code or is there another solution to my problem.
Here is the code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isGPSEnabled = false;
intentThatCalled = getIntent();
String m2txt = intentThatCalled.getStringExtra("v2txt");
getLocation(m2txt);
}
public void getLocation(String n2txt) {
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
criteria = new Criteria();
bestProvider = String.valueOf(locationManager.getBestProvider(criteria, true)).toString();
location = locationManager.getLastKnownLocation(bestProvider);
isGPSEnabled = locationManager.isProviderEnabled(locationManager.GPS_PROVIDER);
if (isGPSEnabled==true) {
Log.e("TAG", "GPS is on");
latitude = location.getLatitude();
longitude = location.getLongitude();
Toast.makeText(PlacesDecoder.this, "latitude:" + latitude + " longitude:" + longitude, Toast.LENGTH_SHORT).show();
searchNearestPlace(n2txt);
}
}
I am still a beginner in android.
Please do help.
EDIT:
This question seems to have the same problem on the same Hardware model. Is this a Hardware Bug? If it is, is there any other possibility. I will also test on another device Note-4 and let you know.
The solution to your problem is to NEVER assume you have a location, Just because your GPS is enabled does not mean you will get a location.
At any time you can get a null location so instead of worrying about if the GPS is enabled or not you should worry about if your location returned is null or not.
getLastKnownLocation() can return a null if there is no last location

Categories

Resources