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
Related
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
My application works perfectly but now I have to test a class which gets the Location via the LocationManager so I decided to activate Mock Location in the Manifest:
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
tools:ignore="ProtectedPermissions" />
and I also activated Mock Location on the Emulator (Dev Settings).
This is my method which uses the LocationManager:
public Location getCurrentLocation() {
try {
LocationManager locationManager = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
if (ActivityCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return null;
}
return locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
} catch (NullPointerException e) {
return null;
}
}
And this is my test class:
public class LocationTest {
Context context;
#Rule
public GrantPermissionRule permissionRule = GrantPermissionRule.grant(Manifest.permission.ACCESS_FINE_LOCATION);
#Before
public void init() {
context = InstrumentationRegistry.getInstrumentation().getTargetContext();
Location mockLocation = new Location(android.location.LocationManager.GPS_PROVIDER);
android.location.LocationManager locationManager = (android.location.LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
locationManager.addTestProvider(android.location.LocationManager.GPS_PROVIDER, false, false,
false, false, true, true, true, 0, 5);
locationManager.setTestProviderEnabled(android.location.LocationManager.GPS_PROVIDER, true);
mockLocation.setLatitude(-20.000);
mockLocation.setLongitude(10.000);
mockLocation.setAltitude(10);
mockLocation.setBearing(0);
mockLocation.setAccuracy(5);
mockLocation.setSpeed(0);
mockLocation.setTime(System.currentTimeMillis());
mockLocation.setElapsedRealtimeNanos(System.nanoTime());
locationManager.setTestProviderStatus(android.location.LocationManager.GPS_PROVIDER, LocationProvider.AVAILABLE, null, System.currentTimeMillis());
locationManager.setTestProviderLocation(android.location.LocationManager.GPS_PROVIDER, mockLocation);
}
#After
public void tearDown() {
android.location.LocationManager locationManager = (android.location.LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
locationManager.removeTestProvider(android.location.LocationManager.GPS_PROVIDER);
}
#Test
public void getCurrentLocationTest() {
LocationClass locClass = new LocationClass(context);
Location loc = locClass.getCurrentLocation();
assertFalse(loc == null);
}
}
For some reason, "return locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);" in getCurrentLocation() returns null always (no Exception thrown, and Permissions are granted).
When I start my app it works but when I try to mock the Location in the tests or even try it without mocking the Location, no Location is found by the LocationManager (Activating Mock Location works though -> No Exceptions thrown, just the Manager won't detect any Location).
Can I even Mock the Location for an Instrumented Test and return it with getLastKnownLocation? What could I try to make it work?
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.
I’m trying to write a utility class to wrap the Google Play Services FusedLocationProviderClient API and location permissions request as I’m sick of writing all that boilerplate every time I want to add location functionality to an app. The problem I’m having though is I’m unable to remove location updates once I’ve started them. Here’s the relevant bits of my utility class:
public class UserLocationUtility extends LocationCallback
{
// Hold a WeakReference to the host activity (allows it to be garbage-collected to prevent possible memory leak)
private final WeakReference<Activity> weakActivity;
// Debug tag
private static final String TAG = "UserLocationUtility";
public static class RequestCodes
{
static final int CURRENT_LOCATION_ONE_TIME = 0;
static final int CURRENT_LOCATION_UPDATES = 1;
static final int LAST_KNOWN_LOCATION = 2;
static final int SMART_LOCATION = 3;
}
private FusedLocationProviderClient mLocationClient;
private Context mContext;
private LocationRequest mLocationRequest;
/* Constructor */
UserLocationUtility(Activity activity){
// assign the activity to the weak reference
this.weakActivity = new WeakReference<>(activity);
// Hold a reference to the Application Context
this.mContext = activity.getApplicationContext();
// Instantiate our location client
this.mLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
// Set up the default LocationRequest parameters
this.mLocationRequest = new LocationRequest();
setLocationRequestParams(2000, 500, LocationRequest.PRIORITY_HIGH_ACCURACY);
// Sets up the LocationRequest with an update interval of 30 seconds, a fastest
// update interval cap of 5 seconds and using balanced power accuracy priority.
} /* Note: values for testing only. Will be dialed back for better power management when testing complete */
/* Stripped out other methods for brevity */
#SuppressLint("MissingPermission")
public void getCurrentLocationOneTime(final UserLocationCallback callback){
mLocationClient.requestLocationUpdates(mLocationRequest, new LocationCallback()
{
#Override
public void onLocationResult(LocationResult locationResult){
if (locationResult == null){
callback.onFailedRequest("getCurrentLocationOneTime(): Request failed: returned null");
return;
}
callback.onLocationResult(locationResult.getLastLocation());
stopLocationUpdates(); /* Stopping location updates here just for testing (NOT WORKING!!) */
}
}, null);
}
public void stopLocationUpdates(){
mLocationClient.removeLocationUpdates(new LocationCallback(){});
Log.i(TAG, "stopLocationUpdates(): Location updates removed");
}
}
Here’s how I’m trying to use it (from MainActivity):
UserLocationUtility locationUtility;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationUtility = new UserLocationUtility(this);
if (locationUtility.checkPermissionGranted()){
Log.i(TAG, "Permissions are granted.");
getLocationUpdates();
} else {
Log.i(TAG, "Permissions are not granted. Attempting to request...");
locationUtility.requestPermissions(UserLocationUtility.RequestCodes.CURRENT_LOCATION_UPDATES);
}
}
public void getLocationUpdates(){
locationUtility.getCurrentLocationOneTime(new UserLocationCallback() {
#Override
public void onLocationResult(Location location) {
Log.i(TAG, "getLocationUpdates result: " + location.toString());
}
#Override
public void onFailedRequest(String result) {
Log.e(TAG, "LocationUpdates result: " + result);
}
});
}
And here's a sample from the log:
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=731 et=+2h10m52s694ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=739 et=+2h10m57s697ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=763 et=+2h11m5s723ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
etc...
As you can see I’m receiving the location updates correctly but the call to stopLocationUpdates() isn’t working. I have a feeling it’s something to do with the fact that I’m passing a new LocationCallback to the removeUpdates() method, but I’m not sure what the alternative is, or even if there is an alternative. This being a non-activity class I can’t exactly initialise LocationCallback as a member in onCreate() then pass it around as needed. The google docs on this aren’t much help at all. Whether that’s because I lack the necessary understanding to decipher them or because they’re just not very good I don’t know but either way I’m stumped and having searched around a lot I can’t seem to find an existing answer elsewhere.
Thanks.
Posting my solution as an answer in case it helps anyone else.
I got it working by declaring a LocationCallback as a member variable and then initialising (or re-initialising) it in each method that requires it...
public void getCurrentLocationUpdates(final UserLocationCallback callback){
if (mIsReceivingUpdates){
callback.onFailedRequest("Device is already receiving updates");
return;
}
// Set up the LocationCallback for the request
mLocationCallback = new LocationCallback()
{
#Override
public void onLocationResult(LocationResult locationResult){
if (locationResult != null){
callback.onLocationResult(locationResult.getLastLocation());
} else {
callback.onFailedRequest("Location request returned null");
}
}
};
// Start the request
mLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null);
// Update the request state flag
mIsReceivingUpdates = true;
}
I check at the beginning of the method whether or not location updates are already being received and get out early if so. This prevents duplicate (and thus unstoppable) location update requests being initiated.
Calling the stopLocationUpdates (below for reference) method now works as it should.
public void stopLocationUpdates(){
mLocationClient.removeLocationUpdates(mLocationCallback);
mIsReceivingUpdates = false;
Log.i(TAG, "Location updates removed");
}
I've never used the android packages before, and I just want to get my current position. After looking online a bit, I've gotten this far.
import android.location.Location;
import android.location.LocationManager;
import android.content.*;
public class CurPosGetter{
public static double[] getPosition(){
LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
Location location = (Location) lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
double longitude = location.getLongitude();
double latitude = location.getLatitude();
double[] ans = new double[2];
ans[0] = latitude;
ans[1] = longitude;
return ans;
}
public static void main (String [] args){
double[] pos = getPosition();
System.out.println(pos[0] + " , " + pos[1]);
}
}
The problem is at the 'getSystemService' line: by reading the javadocs for Context I understand that by calling this method in conjunction with Context.LOCATION_SERVICE I can get my current position, but I don't really understand how to call getSystemService. Any help would be appreciated, I'm sure this is a simple issue and I just don't understand the classes I'm using.
getSystemService() is a method of the Context class. Your class does not subclass Context. Generally you would use getSystemService() in an Activity (which is a subclass of Context).
I think your referring to a compiler error saying something like "method getSystemService not found". The getSystemService is held with the Context class which is obtained in your Application. Check out this post to see how to get Context.
Static way to get 'Context' on Android?
Here is the overview of location services in Android. Here is the LocationManager class at the heart of location services in Android.
Here is an Android developer tutorial on "Location Strategies". Here is another tutorial that might be usable.
Also, you cannot just ask Android for your current position, since GPS may take quite some time to get a fix. Instead, you need to request location updates and use the first update you get, or similar patterns.
Try this working code, tested in SEMC Xperia Play
public class CLASSNAME extends Activity //Use your class name(It will done automatically in eclipse when you start a project)
{
private LocationManager
locationManagerNetwork;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
locationManagerNetwork = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
android.location.Location location2 = locationManagerNetwork.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location2 != null)
{
String message = String
.format("Yout location : \n Longitude: %1$s \n Latitude: %2$s",
location2.getLongitude(), location2.getLatitude());
File sdcard = Environment.getExternalStorageDirectory();
{
try
{
FileWriter filenew = new FileWriter(sdcard + "/FOLDERNAME/network.txt");
//Use your folder name
BufferedWriter bw = new BufferedWriter(filenew);
bw.write(message);
bw.close();
}
catch (IOException e)
{
}}}}}