I've been working on a map based app, and so far the application gets the data of markers from a Room database as a LiveData object and draws the markers on the map and gets the device's location through a FusedLocationProviderClient.
Now I have tried to create a method that would update a column in the database from 0 to 1 if the device reaches a marker, making the marker "active" and then displaying the marker's name as a toast if that marker's "active" column equals to 1.
So far I have tried to use SphericalUtil.computeDistanceBetween(LatLng1, LatLng2) < distance and if the condition is met, then it calls a method to update the column, but I have not managed to get it to work, as the devices location keeps changing and the markers come from a LiveData List object which are both checked for changes and I don't know how to use these in the computeDistanceBetween method. I have gone through the documents related to markers and other map based objects but so far I have not found a solution.
Here is the method that retrieves and draws the markers on the map.
markerViewModel.getAllMarkers().observe(this, new Observer<List<MarkerObject>>() {
#Override
public void onChanged(List<MarkerObject> markerObjects) {
for (MarkerObject markerObject : markerObjects) {
LatLng latLng = new LatLng(markerObject.getLatitude(), markerObject.getLongitude());
mMap.addMarker(new MarkerOptions()
.title(markerObject.getTitle())
.position(latLng)
.visible(true));
}
}
});
the methods that get and draw the device's location on the map.
/**
* Updates the map's UI settings based on whether the user has granted location permission.
*/
private void updateLocationUI() {
if (mMap == null) {
return;
}
getLocationPermission();
try {
if (locationPermissionGranted) {
mMap.setMyLocationEnabled(true);
mMap.getUiSettings().setMyLocationButtonEnabled(true);
} else {
mMap.setMyLocationEnabled(false);
mMap.getUiSettings().setMyLocationButtonEnabled(false);
lastKnownLocation = null;
}
} catch (SecurityException e) {
Log.e("Exception: %s", e.getMessage());
}
}
/**
* Gets the current location of the device, and positions the map's camera.
*/
public void getDeviceLocation() {
/*
* Get the best and most recent location of the device, which may be null in rare
* cases when a location is not available.
*/
try {
if (locationPermissionGranted) {
Task<Location> locationResult = fusedLocationProviderClient.getLastLocation();
locationResult.addOnCompleteListener(this, new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful()) {
// Set the map's camera position to the current location of the device.
lastKnownLocation = task.getResult();
if (lastKnownLocation != null) {
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(lastKnownLocation.getLatitude(),
lastKnownLocation.getLongitude()), DEFAULT_ZOOM));
}
} else {
Log.d(TAG, "Current location is null. Using defaults.");
Log.e(TAG, "Exception: %s", task.getException());
mMap.moveCamera(CameraUpdateFactory
.newLatLngZoom(defaultLocation, DEFAULT_ZOOM));
mMap.getUiSettings().setMyLocationButtonEnabled(false);
}
}
});
}
} catch (SecurityException e) {
Log.e("Exception: %s", e.getMessage(), e);
}
}
I have tried to reach a solution for quite a while through many trials but to no success, I really hope someone can help because I am out of ideas. Any help is well appreciated. Also I don't ask for help in just any case but with this I really am struggling, so literally any documentation or piece of info that would help would be great.
This is something I have previously used to check the distance between location objects, you can use it as it is or modify it to your needs, the code is pretty straight forward.
public final boolean isLocationCloseEnough(Location currentLocation, Location markerLocation, double distance) {
// this is where the method stores the distance between the two locations
float[] distanceInMeters = new float[1];
Location.distanceBetween(currentLocation.getLatitude(), currentLocation.getLongitude(), markerLocation.getLatitude(), markerLocation.getLongitude(), distanceInMeters);
return (double)distanceInMeters[0] < distance;
}
To be able to request location updates you need a location Request like so and request location updates
LocationRequest locationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY).setInterval(5);
LocationCallback callback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
// here is the location
Location lastLocation = locationResult.getLastLocation();
// do what needs to be done
}
};
public void sample() {
FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(context);
client.requestLocationUpdates(locationRequest, callback, Looper.getMainLooper());
}
Finally when your activity or fragment pauses make sure to remove/stop the updates like so
client.removeLocationUpdates(callback)
You can find more information on LocarionRequest here, and play around with its configurations, https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest
Related
I've got a activity that onCreate, it calculates the distance between your location and a event that is nearby, I've used lastKnownLocation to get the current device location and put a marker of it on a google map, but I need it to write the longitude and latitude outside of it's method to be used to calculate distances.
I've used LocationManager to get the rough coordinates but these aren't accurate enough and has a distance of 50 miles for something that's not even half a mile away. I currently have it so will overwrite longitude and latitude got from LocationManager but it does not.
I've attempted to use LocationRequest too and that hasn't helped.
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(60000);
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationCallback locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult){
if(locationRequest==null){
return;
}
for(Location location : locationResult.getLocations()) {
if (location != null){
userLat=location.getLatitude();
userLng=location.getLongitude();
}
}
}
};
LocationServices.getFusedLocationProviderClient(EventLocator.this).requestLocationUpdates(locationRequest,locationCallback, null);
LocationServices.getFusedLocationProviderClient(EventLocator.this).getLastLocation().addOnSuccessListener(new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if(location!=null){
double longitude=location.getLongitude();
double latitude=location.getLatitude();
userLng=longitude;
userLat=latitude;
}
}
});
All the permissions are correct, as I said I've used getLastLocation() to place a marker.
Make sure you added location permission in manifest file
If you are using android os 6 above make sure you have location permission
Make sure you GPS service is enabled in you mobile
public Location getLocation() {
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) {
Location lastKnownLocationGPS = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (lastKnownLocationGPS != null) {
return lastKnownLocationGPS;
} else {
Location loc = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
System.out.println("1::"+loc);----getting null over here
System.out.println("2::"+loc.getLatitude());
return loc;
}
} else {
return null;
}
}
If still not working try to restart your phone and then try again.
You can create interface and implement it in the class where you need to calculate the distance
Usually GPS takes time to warm up. Your initial location reading could be from a cold GPS. In order to get a more accurate reading keep reading results until you get an accuracy reading that works for you.
Keep in mind GPS on a phone is not very accurate and would not be able to get accurate readings inside buildings or if not enough coverage is in your area at the time.
Last know position does not give you your current position, like it says it is the last know position which could have been more than a few minutes ago. Also it could've come from a cell tower reading instead of a GPS reading.
I'm using this code
lateinit var fusedLocationProviderClient: FusedLocationProviderClient
lateinit var latitude : Double
lateinit var longitude : Double
override fun onCreate() {
super.onCreate()
fusedLocationProviderClient = FusedLocationProviderClient(this)
updateLocationTracking()
}
#SuppressLint("MissingPermission")
private fun updateLocationTracking() {
if(PermissionUtility.isPermissionsGranted(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
val request = LocationRequest().apply {
interval = LOCATION_UPDATE_INTERVAL
fastestInterval = FASTEST_LOCATION_INTERVAL
priority = PRIORITY_HIGH_ACCURACY
}
fusedLocationProviderClient.requestLocationUpdates(
request,
locationCallback,
Looper.getMainLooper()
)
}
}
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult?) {
super.onLocationResult(result)
result?.locations?.let { locations ->
for(location in locations) {
setLocationData(location)
}
}
}
}
private fun setLocationData(location : Location){
latitude = location.latitude
longitude = location.longitude
}
}
So I figured it out when I couldn't use my phone I use for debugging and used my personal phone for testing, went on the activity and the distances were correct.
Messed around with both debug phone settings and using GPS_PROVIDER and NETWORK_PROVIDER and when my phone used just GPS to get location, it got nothing. Other phone can, so think it's safe to say my debug phone's GPS is borked.
It's a old phone that, when I got a new one, I factory reset to use for debugging, as it helped with the backwards compatibility for older phones and smaller screens. I never thought if the actual hardware was faulty too after the reset.
So error was with the phone itself not the app. Guess it goes to show have two devices to test on.
I am currently working on my second android app which contains a google maps activity. In this activity I would like to show the user their current location. I kind of managed to do it and it works on both of my devices (Huawei P smart & Galaxy S5). I also sent my friend an APK of the app and on his device (Huawei P9 Lite) the current location returns null after calling task.getResult(). On his device the method will always jump to the else statement (meaning the currentLocation == null). I used the following method.
private void getDeviceLocation() {
Log.d(TAG, "getDeviceLocation: getting the devices current location");
try {
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
mFusedLocationProviderClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location currentLocation) {
Log.d(TAG, "onComplete: found location!");
if (currentLocation != null) {
moveCamera(new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()),
DEFAULT_ZOOM);
} else {
Log.d(TAG, "onComplete: current location is null");
Toast.makeText(MapsActivity.this, "unable to get current location", Toast.LENGTH_SHORT).show();
}
}
});
} catch (SecurityException e) {
Log.e(TAG, "getDeviceLocation: SecurityException: " + e.getMessage());
}
}
I have tried/done the following things:
Adding all required permissions/features to my manifest
Trying some other solutions I found here, as far as I'm aware none of them were showing different results on different devices
Changing the minSDK to 17 (was 23 before).
Adding the nullcheck and try & catch
Because I'm having no problems on my own devices and can't find anything remarkable in my logcat's (since it's all working on my devices) it's kind of hard for me to understand why this is happening.
I have been developing an app that keeps track of distance run/walked by a user.
I used the FusedLocationProviderClient with a foreground service. I set the following parameters for the LocationRequest object,
mLocationRequest.setInterval(3000);
mLocationRequest.setFastestInterval(500);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(6.4008) //7 yards
And, for getting location updates,
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
Location location = locationResult.getLastLocation();
if (mLocation == null) {
if (location.hasAccuracy() && isLocationAccurate(location)) {
mLocation = location;
isGpsAccurate = true;
}
} else {
if (location.hasAccuracy() && isLocationAccurate(location))
calculateDistance(location);
}
}
};
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
Here's the code for isLocationAccurate(location),
boolean isLocationAccurate(Location location) {
float accuracy = location.getAccuracy();
return accuracy > 0 && accuracy < MIN_ACCURACY;
}
And Here's the code for calculateDistance(location),
private calculateDistance(location){
distance += mLocation.distanceTo(location);
mLocation = location;
}
That's the code, works well when moving, but when stationary and indoors(sometimes outdoors), the value keeps changing.
Am I doing something wrong here?
Note: I verified with Strava and Runkeeper. Distance remains constant over there. So, my device(ONE PLUS 3T) works fine.
*There is a getSpeed() function
simple format is
if(location.getSpeed() > 50){
calculateDistance(location);
}
so if the device is in movement means tracking will started otherwise no need to track, just show marker in the last updated position
I'm building a simple five-day weather forecast Android app using the open weather map API. I am trying to insert the user coordinates into my request URL using the Google Play Services but values for latitude and longitude are null when I am passing them into the URL. I am just wondering if there is any way to solve this by passing the coordinates from the onConnected methods of the Google Play Services.
MainActivity
public class MainActivity extends AppCompatActivity implements OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private Location mLastLocation;
private String latitude;
private String longitude;
private String requestUrl = "http://api.openweathermap.org/data/2.5/forecast?lat="+latitude+"&lon="+longitude+"&units=metric&APPID={insert api key here}";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create an instance of GoogleAPIClient.
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
new WeatherAsyncTask().execute(requestUrl);
}
public void updateUi(final ArrayList<Weather> weather) {
// Find a reference to the {#link ListView} in the layout
ListView weatherListView = (ListView) findViewById(R.id.list);
// Create a new {#link ArrayAdapter} of earthquakes
WeatherAdapter adapter = new WeatherAdapter(this, weather);
// Set the adapter on the {#link ListView}
// so the list can be populated in the user interface
weatherListView.setAdapter(adapter);
}
private class WeatherAsyncTask extends AsyncTask<String, Void, ArrayList<Weather>> {
protected ArrayList<Weather> doInBackground(String... requestUrl) {
// Dont perform the request if there is no URL, or first is null
if (requestUrl.length < 1 || requestUrl[0] == null) {
return null;
}
ArrayList<Weather> weather = QueryUtils.fetchWeatherData(requestUrl[0]);
return weather;
}
protected void onPostExecute(ArrayList<Weather> weather) {
// if there is no result do nothing
if (weather == null) {
return;
}
updateUi(weather);
}
}
// if connection not established to google play services
#Override
public void onConnectionFailed(ConnectionResult result) {
// An unresolvable error has occurred and a connection to Google APIs
// could not be established. Display an error message, or handle
// the failure silently
// ...
}
// get latitude and longitude of last known location when connected to google play services
public void onConnected(Bundle connectionHint) {
try {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
} catch (SecurityException e) {
Log.e("MainActivity", "Security exception thrown", e);
}
if (mLastLocation != null) {
latitude = String.valueOf(mLastLocation.getLatitude());
longitude = String.valueOf(mLastLocation.getLongitude());
}
}
}
When the app is run, I get a NullPointerException. I realize that the values for latitude and longitude are null but I am not sure how to retrieve them correctly. Thanks for any help, I'm fairly new at android development.
P.S. I have omitted the API key from the URL
Please refer this guide : Retrieving-Location-with-LocationServices-API
First of all LocationServices.FusedLocationApi.getLastLocation will return you a location if any recent app used location(device has recent location) otherwise it will return null.
So in case of null value you must check it inside onConnected .
For getting non null location you must listen devices location.(FusedLocationApi, LocationManager ...)
Also you must check permission for Android M higher devices, luckily you are using FusedLocationApi inside your Activity.
Basic sample of my code :
#Override
public void onConnected(#Nullable Bundle bundle) {
if( HelperUtils.isGpsOpen(getApplicationContext()) ) {
startListenLocation();
}
}
#Override
public void startListenLocation() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//ask permission whatever you want to do
} else {
if(mGoogleApiClient==null) {
buildGoogleApiClient();
} else {
if(mGoogleApiClient.isConnected()) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
Location mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
//here I am checking null thats where you get null
if(mCurrentLocation!=null) {
mPresenter.onNewLocation(getDeviceId(),mCurrentLocation.getLatitude(),mCurrentLocation.getLongitude());
}
// then I am tracking user location , this will trigger onLocationChanged method when a new location arrived so you can handle new location
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
} else {
if(!mGoogleApiClient.isConnecting())
buildGoogleApiClient();
}
}
}
}
#Override
public void onLocationChanged(Location location) {
// I am just checking null again, more cautious I am :)
if(location!=null) // handle it
}
Then important part is remove location requests when your Activity's onPause or onDestroy :
if(mGoogleApiClient!=null){
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
if(mGoogleApiClient.isConnected()) mGoogleApiClient.disconnect();
}
My objective is to track the user's location and give an alert on reaching marked location. So first I get the location to reach and marked that. Then I get the current location and compare it with marked location. But it is not working. Even I placed the marker at current location. It is not giving the toast message. Here is the code -
public class MainActivity extends FragmentActivity implements LocationListener{
LocationManager locationManager;
String provider;
ToggleButton toggle;
public static double lat,lang,value1,value2,value3,value4;
public static String strlat,strlat1;
public LatLng loc,loc1;
public double latitude,longitude;
public static String strlang,strlang1 ;
Location location;
public LocationManager locationmanager;
// Google Map
private GoogleMap googleMap;
LocationListener Locationlistener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
// Creating an empty criteria object
Criteria criteria = new Criteria();
// Getting the name of the provider that meets the criteria
provider = locationManager.getBestProvider(criteria, false);
if(provider!=null && !provider.equals("")){
// Get the location from the given provider
final Location location = locationManager.getLastKnownLocation(provider);
locationManager.requestLocationUpdates(provider, 1000, 1, this);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
if(location!=null){
lat = location.getLatitude();
lang = location.getLongitude();
value3 =Double.parseDouble(new DecimalFormat("##.##").format(lat));
value4 =Double.parseDouble(new DecimalFormat("##.##").format(lang));
strlat = String.valueOf(value3);
strlang = String.valueOf(value4);
loc1 =new LatLng(lat, lang);
if((value1 == value3) && (value2 == value4))
{
Toast.makeText(getApplicationContext(), "target reached", Toast.LENGTH_SHORT).show();
}
else
Toast.makeText(getBaseContext(), "Location can't be retrieved", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(getBaseContext(), "No Provider Found", Toast.LENGTH_SHORT).show();
}
handler.postDelayed(this, 1000);
}
};
handler.post(runnable);
}
try {
// Loading map
initilizeMap();
// Changing map type
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
// googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
// googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
// googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
// googleMap.setMapType(GoogleMap.MAP_TYPE_NONE);
// Showing / hiding your current location
googleMap.setMyLocationEnabled(true);
// Enable / Disable zooming controls
googleMap.getUiSettings().setZoomControlsEnabled(false);
// Enable / Disable my location button
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
// Enable / Disable Compass icon
googleMap.getUiSettings().setCompassEnabled(true);
// Enable / Disable Rotate gesture
googleMap.getUiSettings().setRotateGesturesEnabled(true);
// Enable / Disable zooming functionality
googleMap.getUiSettings().setZoomGesturesEnabled(true);
// create marker
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(loc, 16.0f));
} catch (Exception e) {
e.printStackTrace();
}
googleMap.setOnMapClickListener(new OnMapClickListener(){
public void onMapClick(LatLng point){
Toast.makeText(getBaseContext(),
point.latitude + ", " + point.longitude,
Toast.LENGTH_LONG).show();
latitude = point.latitude;
longitude = point.longitude;
value1 =Double.parseDouble(new DecimalFormat("##.##").format(latitude));
value2 =Double.parseDouble(new DecimalFormat("##.##").format(longitude));
strlat1 = String.valueOf(value1);
strlang1 = String.valueOf(value2);
loc=new LatLng(point.latitude, point.longitude);
MarkerOptions marker = new MarkerOptions().position(loc).title("Hello Maps ");
// adding marker
googleMap.addMarker(marker);
}
});
}
#Override
protected void onResume() {
super.onResume();
initilizeMap();
}
/**
* function to load map If map is not created it will create it for you
* */
private void initilizeMap() {
if (googleMap == null) {
googleMap = ((MapFragment) getFragmentManager().findFragmentById(
R.id.map)).getMap();
// check if map is created successfully or not
if (googleMap == null) {
Toast.makeText(getApplicationContext(),
"Sorry! unable to create maps", Toast.LENGTH_SHORT)
.show();
}
}
}
/*
* creating random position around a location for testing purpose only
*/
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
}
There are a few things wrong in your code.
You can't compare coordinates with == because you will never have an exact match for lat/lon coordinates when tracking the device's location. You need to figure out a threshold, and if the device gets within that threshold, then show the alert.
Next, you're not using your onLocationChanged() callback, so you are just looking at the current location one time at app start-up.
Take a look at the code in my other answer here which is about determining if the device is within a circle. This is a similar problem.
So, if you were to make your threshold 10 meters, and consider the target reached if the device is within 10 meters of the target location, you would implement your onLocationChanged() method something like this:
#Override
public void onLocationChanged(Location location) {
float[] distance = new float[2];
Location.distanceBetween( location.getLatitude(), location.getLongitude(),
latitude, longitude, distance);
if( distance[0] < 10 ){
//target location reached
Toast.makeText(getApplicationContext(), "target reached", Toast.LENGTH_SHORT).show();
}
}
one more daniel.i changed above code now it was working now have some more problem.
1.i tried to set splash screen like in my second.class.i set the above code for google map.it was running for five second while moving from one to another it get stopped
new Timer().schedule(new TimerTask(){
public void run() {
Intent intent = new Intent (MainActivity.this, SecondActivity.class);
PendingIntent pendIntent = PendingIntent.getActivity(getBaseContext(), 1000, intent, 0);
}
}, 5000);
2.i want set search in google map how to set that i tried that it not working give some idea.