I want to create an application for Android that enables me to get the geolocation of a user. This has to be made as a client-server app and for the server side I'm using OpenFire.
For getting the user's location I would have to use XEP-0080, right? And SmackAPI also?
I'm completely new to XMPP and Smack, so if anyone could get me a few pointers or maybe examples or any kind of documentation about this I'd be very grateful.
Thanks in advance for any help.
An Android project I’m currently working on required periodically publishing a user’s location to their XMPP roster friends using aSmack & XEP-0080.
It turned out trickier than I would have liked so I documented my solution here: http://www.dbotha.com/2014/11/02/xep-0080-user-location-on-android-using-pep-with-smack/
For completeness I'll cover the important parts here. In the interest of brevity the only XML child elements from the XEP-0080 specification that I’ll cover are those relating to latitude and longitude.
A PEPItem to hold the user location and transform it into the appropriate XML:
public class UserLocation extends PEPItem {
public static final String NODE =
"http://jabber.org/protocol/geoloc";
public final double latitude, longitude;
public UserLocation(double latitude, double longitude) {
this(StringUtils.randomString(16), latitude, longitude);
}
public UserLocation(double latitude, double longitude,
String id) {
super(id);
this.latitude = latitude;
this.longitude = longitude;
}
#Override
java.lang.String getNode() {
return NODE;
}
// return an XML element approximately inline
// with the XEP-0080 spec
#Override
java.lang.String getItemDetailsXML() {
return String.format(
"<geoloc xmlns='%s'><lat>%f</lat>" +
"<lon>%f</lon></geoloc>",
NODE, latitude, longitude);
}
}
A mostly boilerplate PEPEvent to hold the UserLocation PEPItem:
public class UserLocationEvent extends PEPEvent {
private final UserLocation location;
public UserLocationEvent(UserLocation location) {
this.location = location;
}
public UserLocation getLocation() {
return location;
}
#Override
public String getNamespace() {
return "http://jabber.org/protocol/pubsub#event";
}
#Override
public String toXML() {
return String.format("<event xmlns=" +
"'http://jabber.org/protocol/pubsub#event' >" +
"<items node='%s' >%s</items></event>",
UserLocation.NODE, location.toXML());
}
}
A custom PacketExtensionProvider to parse out the UserLocationEvent's from incoming packets where present.
public class UserLocationProvider
implements PacketExtensionProvider {
// This method will get called whenever aSmack discovers a
// packet extension containing a publish element with the
// attribute node='http://jabber.org/protocol/geoloc'
#Override
public PacketExtension parseExtension(XmlPullParser parser)
throws Exception {
boolean stop = false;
String id = null;
double latitude = 0;
double longitude = 0;
String openTag = null;
while (!stop) {
int eventType = parser.next();
switch (eventType) {
case XmlPullParser.START_TAG:
openTag = parser.getName();
if ("item".equals(openTag)) {
id = parser.getAttributeValue("", "id");
}
break;
case XmlPullParser.TEXT:
if ("lat".equals(openTag)) {
try {
latitude = Double.parseDouble(
parser.getText());
} catch (NumberFormatException ex) {
/* ignore */
}
} else if ("lon".equals(openTag)) {
try {
longitude = Double.parseDouble(
parser.getText());
} catch (NumberFormatException ex) {
/* ignore */
}
}
break;
case XmlPullParser.END_TAG:
// Stop parsing when we hit </item>
stop = "item".equals(parser.getName());
openTag = null;
break;
}
}
return new UserLocationEvent(
new UserLocation(id, latitude, longitude));
}
}
Now tying it all together:
XMPPTCPConnection connection = new XMPPTCPConnection();
ServiceDiscoveryManager sdm = ServiceDiscoveryManager
.getInstanceFor(connection);
sdm.addFeature("http://jabber.org/protocol/geoloc");
sdm.addFeature("http://jabber.org/protocol/geoloc+notify");
EntityCapsManager capsManager = EntityCapsManager
.getInstanceFor(connection);
capsManager.enableEntityCaps();
PEPProvider pepProvider = new PEPProvider();
pepProvider.registerPEPParserExtension(
"http://jabber.org/protocol/geoloc",
new UserLocationProvider());
ProviderManager.addExtensionProvider("event",
"http://jabber.org/protocol/pubsub#event", pepProvider);
PEPManager pepManager = new PEPManager(connection);
pepManager.addPEPListener(PEP_LISTENER);
connection.connect();
connection.login(username, password);
And finally a listener for incoming LocationEvent's:
PEPListener PEP_LISTENER = new PEPListener() {
#Override
public void eventReceived(String from, PEPEvent event) {
if (event instanceof UserLocationEvent) {
// do something interesting
}
}
};
I believe this is close to what you are trying to accomplish.
XEP-0080 User Location in Smack Library
Related
I want to get swimming data from Samsung Health App but not get any proper solution.
Below is my code.
public class SwimmingReport {
private final HealthDataStore mStore;
private SwimObserver swimObserver;
private static final long ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000L;
public SwimmingReport(HealthDataStore store) {
mStore = store;
}
public void start(SwimObserver listener, String strDate, JSONObject jsonObject) {
swimObserver = listener;
HealthDataObserver.addObserver(mStore, HealthConstants.Exercise.HEALTH_DATA_TYPE, new HealthDataObserver(null) {
#Override
public void onChange(String s) {
readTodaySwimData(strDate,jsonObject);
}
});
readTodaySwimData(strDate,jsonObject);
}
private void readTodaySwimData(String strDate,JSONObject jsonObject) {
HealthDataResolver resolver = new HealthDataResolver(mStore, null);
long startTime = GlobalMethods.getEpochTime(strDate);
long endTime = startTime + ONE_DAY_IN_MILLIS;
HealthDataResolver.ReadRequest request = new HealthDataResolver.ReadRequest.Builder()
.setDataType(HealthConstants.Exercise.HEALTH_DATA_TYPE)
.setProperties(new String[]{HealthConstants.Exercise.EXERCISE_TYPE})
.setLocalTimeRange(HealthConstants.Exercise.START_TIME, HealthConstants.Exercise.TIME_OFFSET,
startTime, endTime)
.build();
try {
resolver.read(request).setResultListener(result ->{
double distance = 0.0;
try {
for (HealthData data : result) {
distance += data.getFloat(HealthConstants.Exercise.DISTANCE);
}
} finally {
result.close();
}
if (swimObserver != null) {
swimObserver.onChanged(distance,strDate,jsonObject);
}
});
} catch (Exception e) {
Log.e("=> ", "Getting step count fails.", e);
}
}
public interface SwimObserver {
void onChanged(Double distance, String date, JSONObject jsonObject);
}
}
Here is my whole source code this
I figured out on your code that you want to read distance value from the read result.
distance += data.getFloat(HealthConstants.Exercise.DISTANCE);
But you described absolutely not related property. Maybe that is the problem.
.setProperties(new String[]{HealthConstants.Exercise.EXERCISE_TYPE})
Try this.
.setProperties(new String[]{HealthConstants.Exercise.DISTANCE})
A SQLite database has fields to store Latitude and Longitude values in a table. A mailing address is input from users in an Add activity.
In the AsyncTask that fetches the LatLng object for the input address, the database write operation is the last statement of its doInBackground method. However, contrary to expectation, the write is happening BEFORE the JSON parsing of the response, writing NULL values for the lat and long initially.
The ListView that front-ends the table shows null Lat and Long values, but the right values after a refresh. The focus rushes back from the Add activity before the JSON parsing and all, which is not the desired behaviour. The ListView must return only after the right LatLng are populated in the table, until which an indeterminate progress bar should keep showing. This IS all working fine, except that
the progress bar appears and vanishes too fast
The ListView is back with other components of its list items properly populated, except the LatLong values.
The question is, how can the database write be forced to wait till the JSON parsing is complete?
Hope this verbose explanation of the situation will suffice. Please mention if more elaboration or code is required.
Many thanks in advance!
Relevant pseudo-code for your kind investigation:
The AsyncTask extension:
private class Geocode extends AsyncTask<String, Void, LatLng>
{
#Override
protected LatLng doInBackground(String...addressToGeocode)
{
int DELAY = 9000;
URL geoCodeURL;
String jsonResponse = null;
String geoCodeRequestURL = Contract.HTTPS_GOOGLE_GEOCODE_PRE_ADDRESS_URL
+ Uri.encode(addressToGeocode[0]);
// Get JSON response
try
{
geoCodeURL = new URL(geoCodeRequestURL);
URLConnection geoCodeURLConnection = geoCodeURL.openConnection();
BufferedReader responseReader = new BufferedReader(new InputStreamReader (geoCodeURLConnection.getInputStream()));
StringBuilder response = new StringBuilder();
int cp;
while((cp = responseReader.read()) != -1)
{
response.append((char)(cp));
}
jsonResponse = response.toString();
Thread.sleep(DELAY); // Force a delay, apparently not enough...
}
catch (relevantExceptions e)
{
Log.i(LOG_TAG, e.toString());
e.printStackTrace();
}
// Parse the LatLng from the JSON response
try
{
JSONObject responseObject = new JSONObject(jsonResponse);
double lat = ((JSONArray) responseObject
.get("results"))
.getJSONObject(0)
.getJSONObject("geometry")
.getJSONObject("location")
.getDouble("lat");
double lng = ((JSONArray) responseObject
.get("results"))
.getJSONObject(0)
.getJSONObject("geometry")
.getJSONObject("location")
.getDouble("lng");
addressLatLng = new LatLng(lat, lng);
object.setLat(lat);
objet.setLng(lng);
}
catch (JSONException e)
{
e.printStackTrace();
}
if (addressLatLng != null)
{
....
}
else
{
Toast.makeText(getApplicationContext(), "Unable to Geocode", Toast.LENGTH_SHORT).show();
}
// Write to the database
dbResult = dbHelper.commitRow(object);
return addressLatLng;
}
#Override
protected void onPreExecute()
{
setProgressBarIndeterminate(true);
setProgressBarIndeterminateVisibility(true);
}
#Override
protected void onPostExecute(LatLng addressLatLng)
{
setProgressBarIndeterminateVisibility(false);
}
} // of getLatLong()
Relevant code in the Add activity that instantiates the AsyncTask:
String addressToGeocode = address +
" " +
city +
country;
AsyncTask<String, Void, LatLng> geocodeTask = new Geocode();
geocodeTask.execute(addressToGeocode);
The Add activity is invoked by a ListView activity like so:
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
int itemId = item.getItemId();
switch (itemId)
{
case (R.id.action_menu_add):
{
Intent addIntent = new Intent(this, AddActivity.class);
startActivityForResult(addIntent, Contract.ADD_REQUEST_CODE);
return true;
}
default:
return false;
}
} // of onOptionsItemSelected()
The ListView's onActivityResult
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (resultCode == RESULT_OK)
{
Bundle b = data.getBundleExtra(GlobeShopperContract.NEW_BUNDLE);
MyObject d = (MyObject) b.get(Contract.NEW_BUNDLE_NAME);
collection.add(d);
dViewAdapter.setNotifyOnChange(true);
dViewAdapter.notifyDataSetChanged();
}
else
...
}
I am following an Android weather app tutorial where the weather app requests a wether information about a place using an API;
However, the app was not "location aware" so I decided to try to do that, but when my app is created it calls the API with the hardcoded coordinates (private latitude and longitude). I dont understand why thid is happening, I am calling the getLoation method before the getForecast method. getLocation method should use the location manager's internet provider and set the latitude and longitude before the getForecast in called (which uses the coordinates to make the asynchronous API call). The strange thing is that when I press the refresh button, the location's coordinates are obtained from the location manager and the getLocationName is working fine too (finds the name of the location using the coordinates and set's the locationLabel to the name).
I suspect e problem is because if the fact that OkHTTP API that I am using to make the asynchronous call uses worker thread.
NODE: I am using Butter knife and YoYo API's also I have four classes which store the information about the weather called Forecast, Hour and Current and Day. I did not include them because I thought it is not important, since the problem is in my main activity class
Here is the code in my main activity class:
public static final String TAG = MainActivity.class.getSimpleName();
private Forecast mForecast;
//default coordinates - Aberdeen, UK Lati:57.156866 ; Long:
private double latitude = 57.156866;
private double longitude = -2.094278;
private LocationManager locationManager;
#InjectView(R.id.timeLabel) TextView mTimeLabel;
#InjectView(R.id.temperatureLabel) TextView mTemperatureLabel;
#InjectView(R.id.humidityValue) TextView mHumidityValue;
#InjectView(R.id.precipValue) TextView mPrecipValue;
#InjectView(R.id.summaryLabel) TextView mSummaryLabel;
#InjectView(R.id.locationLabel) TextView mLocationLabel;
#InjectView(R.id.windSpeedValue) TextView mWindSpeedValue;
#InjectView(R.id.iconImageView) ImageView mIconImageView;
#InjectView(R.id.refreshImageView) ImageView mRefreshImaveView;
#InjectView(R.id.progressBar) ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImaveView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getLocation();
getForecast(latitude, longitude);
}
});
getLocation();
getForecast(latitude, longitude);
}
#Override
protected void onResume() {
super.onResume();
getForecast(latitude, longitude);
}
private void getForecast(double latitude, double longitude) {
//animations
YoYo.with(Techniques.FadeIn).duration(1800).playOn(mLocationLabel);
YoYo.with(Techniques.FadeIn).duration(1600).playOn(mTemperatureLabel);
YoYo.with(Techniques.FadeIn).duration(1800).playOn(mIconImageView);
YoYo.with(Techniques.FadeIn).duration(1000).playOn(mSummaryLabel);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mHumidityValue);
YoYo.with(Techniques.FadeIn).duration(1400).playOn(mWindSpeedValue);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mPrecipValue);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mTimeLabel);
String API_KEY = "API_KEY";
String forecast = "https://api.forecast.io/forecast/"+ API_KEY +"/"+ latitude+","+ longitude+"?units=auto";
if(isNetworkAvailable()) {
toggleRefresh();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(forecast)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
alertUserAboutError();
}
//when the call to the Okhttp library finishes, than calls this method:
#Override
public void onResponse(Response response) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
try {
String jsonData = response.body().string();
//Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mForecast = parseForecastDetails(jsonData);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateDisplay();
}
});
} else {
alertUserAboutError();
}
} catch (IOException | JSONException e) {
Log.e(TAG, "Exception caught:", e);
}
}
});
}else{
//Toast.makeText(this,getString(R.string.network_unavailable_message),Toast.LENGTH_LONG).show();
WIFIDialogFragment dialog = new WIFIDialogFragment();
dialog.show(getFragmentManager(), getString(R.string.error_dialog_text));
}
}
private void toggleRefresh() {
if(mProgressBar.getVisibility() == View.INVISIBLE){
mProgressBar.setVisibility(View.VISIBLE);
mRefreshImaveView.setVisibility(View.INVISIBLE);
}else{
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImaveView.setVisibility(View.VISIBLE);
}
}
//updates the dysplay with the data in the CUrrentWeather locaal object
private void updateDisplay() {
Current current = mForecast.getCurrent();
//setting the current weather details to the ui
mTemperatureLabel.setText(current.getTemperature()+"");
mTimeLabel.setText("At "+ current.getFormattedTime()+" it will be");
mHumidityValue.setText(current.getHumidity() +"%");
mPrecipValue.setText(current.getPrecipChange()+"%");
mSummaryLabel.setText(current.getSummery());
mWindSpeedValue.setText(current.getWindSpeed()+"");
mLocationLabel.setText(current.getTimeZone());
//sets the mLocationLavel to the appropriate name and not the timezome from the API
getLocationName();
Drawable drawable = ContextCompat.getDrawable(this, current.getIconId());
mIconImageView.setImageDrawable(drawable);
}
private Forecast parseForecastDetails(String jsonData) throws JSONException {
Forecast forecast = new Forecast();
forecast.setCurrent(getCurrentDetails(jsonData));
forecast.setHourlyForecast(getHourlyForecast(jsonData));
forecast.setDailyForecast(getDailyForecast(jsonData));
return forecast;
}
private Day[] getDailyForecast(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject daily = forecast.getJSONObject("daily");
JSONArray data = daily.getJSONArray("data");
Day[] days = new Day[data.length()];
for(int i = 0;i < data.length();i++){
JSONObject jsonDay = data.getJSONObject(i);
Day day = new Day();
day.setSummary(jsonDay.getString("summary"));
day.setIcon(jsonDay.getString("icon"));
day.setTemperatureMax(jsonDay.getDouble("temperatureMax"));
day.setTime(jsonDay.getLong("time"));
day.setTimezone(timezone);
days[i] = day;
Log.v(MainActivity.class.getSimpleName(),days[i].getIcon());
}
return days;
}
private Hour[] getHourlyForecast(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject hourly = forecast.getJSONObject("hourly");
JSONArray data = hourly.getJSONArray("data");
Hour[]hours = new Hour[data.length()];
for(int i = 0;i < data.length();i++){
JSONObject jsonHour = data.getJSONObject(i);
Hour hour = new Hour();
hour.setSummary(jsonHour.getString("summary"));
hour.setTemperature(jsonHour.getDouble("temperature"));
hour.setIcon(jsonHour.getString("icon"));
hour.setTime(jsonHour.getLong("time"));
hour.setTimezone(timezone);
hours[i] = hour;
}
return hours;
}
/*
* throws JSONException, doing it like that, we place the
* responsability of handaling this exeption to the caller of the method
*/
private Current getCurrentDetails(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
Log.i(TAG,"From JSON: " + timezone);
JSONObject currently = forecast.getJSONObject("currently");
Current mCurrent = new Current();
mCurrent.setHumidity(currently.getDouble("humidity"));
mCurrent.setTime(currently.getLong("time"));
mCurrent.setIcon(currently.getString("icon"));
mCurrent.setPrecipChange(currently.getDouble("precipProbability"));
mCurrent.setSummery(currently.getString("summary"));
mCurrent.setTemperature(currently.getDouble("temperature"));
mCurrent.setTimeZone(timezone);
mCurrent.setWindSpeed(currently.getDouble("windSpeed"));
Log.d(TAG, mCurrent.getFormattedTime());
return mCurrent;
}
private boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
//contition to check if there is a network and if the device is connected
if(networkInfo != null && networkInfo.isConnected()){
isAvailable = true;
}
return isAvailable;
}
private void alertUserAboutError() {
AlertDIalogFragment dialog = new AlertDIalogFragment();
dialog.show(getFragmentManager(),getString(R.string.error_dialog_text));
}
private void getLocation(){
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
if(isNetworkAvailable()){
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1, 1000, new MyLocationListener());
}else{
WIFIDialogFragment dialog = new WIFIDialogFragment();
dialog.show(getFragmentManager(), getString(R.string.error_dialog_text));
}
}
private class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location loc) {
latitude = loc.getLatitude();
longitude = loc.getLongitude();
Toast.makeText(MainActivity.this,
"Location changed: Lat: " + loc.getLatitude() + " Lng: "
+ loc.getLongitude(), Toast.LENGTH_SHORT).show();
locationManager.removeUpdates(this);
}
#Override
public void onProviderDisabled(String provider) {}
#Override
public void onProviderEnabled(String provider) {}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
private void getLocationName(){
Geocoder geo = new Geocoder(this, Locale.getDefault());
try {
List<Address> addressList = geo.getFromLocation(this.latitude,this.longitude,1);
if (addressList.isEmpty()){
//gets the default name from the timeZone
//that we set in as a local variable
}else{
if(addressList.size() > 0){
Log.v(MainActivity.class.getSimpleName(),addressList.get(0).getLocality() + ", "+ addressList.get(0).getCountryName()+"");
mLocationLabel.setText(addressList.get(0).getLocality() + ", "+ addressList.get(0).getCountryName());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
This is a screenshot of the app:
By looking at your code, there are couple of things which are wrong.
you have called getLocation Method in OnCreate and onResume as well which is logically incorrect. you can keep it at onResume only if you wish you get location frequently.
when we call getLocation method it's not guarantee to give the latitude and longitude straight away, it takes it's own sweet time depending on the providers (Read API of location manager) hence your first call for getForecast will probably fail.
Solution : you can call getForecast method when onLocationChange method is called, by that time you can show progressDialog.
Make sure you have given Location related permissions in the manifest file
So after finally getting my head around Xamarin.Forms DependencyService I have nearly got it returning the device's current location.
my interface
public interface ICurrentLocation
{
MyLocation SetCurrentLocation();
}
MyLocation
public class MyLocation
{
public double Latitude {get; set;}
public double Longitude{get; set;}
}
the line that calls it
MyLocation location = DependencyService.Get<ICurrentLocation>().SetCurrentLocation();
and in the CurrentLocation class in the Android project that implements the Geolocation class of Xamarin.Mobile
[assembly: Dependency(typeof(CurrentLocation))]
namespace MyCockburn.Droid
{
public class CurrentLocation : Activity, ICurrentLocation
Geolocator locator;
Position position = new Position();
MyLocation location;
public MyLocation SetCurrentLocation()
{
GetPosition();
location = new MyLocation()
{
Latitude = position.Latitude,
Longitude = position.Longitude
};
return location;
}
async void GetPosition()
{
try
{
locator = new Geolocator(this) { DesiredAccuracy = 50 };
if (locator.IsListening != true)
locator.StartListening(minTime: 1000, minDistance: 0);
position = await locator.GetPositionAsync(timeout: 20000);
}
catch (Exception e)
{
Log.Debug("GeolocatorError", e.ToString());
}
}
}
my problem seems to be that is returning location before position holds the longitude and latitude
I am hoping my mistake is glaringly obvious
EDIT: the code works if I run it as a normal Android Activity
I would do a slight modification since best practice is to either do all async or none. When you try to return the result from an async method from a non async method you can run into problems with deadlocks. Also, since you aren't using the await keyword when calling the GetPosition method, you are getting back a Task, but aren't checking when the operation is complete. I suggest slightly modifying your code as such.
public interface ICurrentLocation {
Task<MyLocation> GetCurrentLocation();
}
public async Task<MyLocation> GetCurrentLocation()
{
var position = await GetPosition();
return new MyLocation()
{
Latitude = position.Latitude,
Longitude = position.Longitude
};
}
async Task<Location> GetPosition()
{
try
{
locator = new Geolocator(this) { DesiredAccuracy = 50 };
if (locator.IsListening != true)
locator.StartListening(minTime: 1000, minDistance: 0);
return await locator.GetPositionAsync(timeout: 20000);
}
catch (Exception e)
{
Log.Debug("GeolocatorError", e.ToString());
}
}
You aren't waiting for the position function to finish. Many different options and keeping it async is the best one but if you want it synchronous then try this blocking call:
void GetPosition()
{
try
{
locator = new Geolocator(this) { DesiredAccuracy = 50 };
if (locator.IsListening != true)
locator.StartListening(minTime: 1000, minDistance: 0);
position = locator.GetPositionAsync(timeout: 20000).Result;
}
catch (Exception e)
{
Log.Debug("GeolocatorError", e.ToString());
}
}
I also recommend taking a look at Xamarin.Forms.Labs as it already has abstracted GPS service and working sample that is functional for all 3 platforms:
https://github.com/XForms/Xamarin-Forms-Labs
Try adding the assembly above the namespace and awaiting the GetPosition method.
Take a look at this image:
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/dependency-service/Images/solution.png
I developed an app that works fine in getting GPS location. I believe the codes below will be of great help.
You can then edit the SubmitGPSLocation function to your preference.
public async Task Run(CancellationToken token)
{
await Task.Run(async () =>
{
if (GPSService.Instance.IsListening)
{
GPSService.Instance.StopListening();
}
GPSService.Instance.StartListening(2500, 50, true);
GPSService.Instance.PositionChanged += Instance_PositionChanged;
System.Diagnostics.Debug.WriteLine(getRunningStateLocationService());
while (getRunningStateLocationService())
{
token.ThrowIfCancellationRequested();
await Task.Delay(500).ConfigureAwait(true);
}
GPSService.Instance.StopListening();
//await CrossGeolocator.Current.StopListeningAsync().ConfigureAwait(true);
GPSService.Instance.PositionChanged -= Instance_PositionChanged;
return;
}, token).ConfigureAwait(false);
}
private void Instance_PositionChanged(object sender, PositionEventArgs e)
{
try
{
isEvenCount = !isEvenCount;
if (e.Position != null)
{
var message = new LocationMessage
{
Latitude = e.Position.Latitude,
Longitude = e.Position.Longitude,
Accuracy = e.Position.Accuracy,
Speed = e.Position.Speed,
Heading = e.Position.Heading,
TimeStamp = e.Position.Timestamp.DateTime
};
SubmitGPSLocation(e).ConfigureAwait(false);
}
else
{
CrossToastPopUp.Current.ShowToastMessage("Failed to get GPS location");
}
}
catch (Exception ex)
{
CrossToastPopUp.Current.ShowToastMessage(ex.Message);
}
}
private static async Task SubmitGPSLocation(PositionEventArgs e)
{
if (!NetworkCheck.IsInternet())
{
return;
}
if (!int.TryParse(App.PhoneID, out var number))
{
return;
}
try
{
var thetrackers = Convert.ToString(Application.Current.Properties["AuthorizedTrackers"]);
GeneralUserPhoneLocation MyGeneralUserPhoneLocation = new GeneralUserPhoneLocation();
MyGeneralUserPhoneLocation.PhoneID = int.Parse(App.PhoneID);
MyGeneralUserPhoneLocation.Latitude = e.Position.Latitude.ToString("n6");
MyGeneralUserPhoneLocation.Longitude = e.Position.Longitude.ToString("n6");
MyGeneralUserPhoneLocation.Accuracy = e.Position.Accuracy;
MyGeneralUserPhoneLocation.Heading = e.Position.Heading;
MyGeneralUserPhoneLocation.Speed = e.Position.Speed;
MyGeneralUserPhoneLocation.TimeStamp = e.Position.Timestamp.DateTime;
MyGeneralUserPhoneLocation.RequestType = "N";
MyGeneralUserPhoneLocation.Comment = thetrackers;
string servicestring = JsonConvert.SerializeObject(MyGeneralUserPhoneLocation);
HttpContent theusercontent = new StringContent(servicestring, Encoding.UTF8, "application/json");
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://mygpswebapi.com");
var response = await client.PostAsync("Home/SaveGeneralUserPhoneLocationAPP/", theusercontent).ConfigureAwait(true);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
}
catch (Exception ex)
{
CrossToastPopUp.Current.ShowToastMessage(ex.Message);
}
}
Code 1 is a part of my Fragment class
From Code 1 i am getting my place name. I want to pass that place name to a non Activity class That is to CODE 2.
Code 1
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
gps = new GPSTracker(getActivity());
Geocoder geocoder= new Geocoder(getActivity(), Locale.ENGLISH);
myAddress=(TextView)getView().findViewById(R.id.gpsLocation);
surveyView = (SurveyView) getView().findViewById(R.id.surveyView);
newsHomeView = (NewsHomeView) getView().findViewById(R.id.newsHomeView);
audioView = (AudioItemView) getView().findViewById(R.id.audioView);
AudioListener listener = (AudioListener)getActivity();
audioView.setListener(listener);
newsHomeView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity mnAct = (MainActivity)HomeFragment.this.getActivity();
mnAct.moveToPage(Constants.NEWS_PAGE);
}
});
iPrevIndex = -1;
// check if GPS enabled
if(gps.canGetLocation()){
double latitude = gps.getLatitude();
double longitude = gps.getLongitude();
// \n is for new line
//Toast.makeText(getApplicationContext(), "Your Location is - \nLat: " + latitude + "\nLong: " + longitude, Toast.LENGTH_LONG).show();
try {
//Place your latitude and longitude
// List<Address> addresses = geocoder.getFromLocation(37.423247,-122.085469, 1);
List<Address> addresses = geocoder.getFromLocation(latitude,longitude, 1);
if(addresses != null) {
Address fetchedAddress = addresses.get(0);
StringBuilder strAddress = new StringBuilder();
for(int i=0; i<=fetchedAddress.getMaxAddressLineIndex(); i++) {
strAddress.append(fetchedAddress.getAddressLine(i)).append("\n");
}
Log.i("country name ",fetchedAddress.getAddressLine(fetchedAddress.getMaxAddressLineIndex()));
String s=fetchedAddress.getAddressLine(fetchedAddress.getMaxAddressLineIndex()-1);// Bangalore, Karnataka, 560038
String str[]=s.split(" ");// array of Bangalore, Karnataka, 560038
System.out.println(Arrays.toString(str)); // print all array element
// myAddress.setText("You'r location is: " +strAddress.toString());
}
else
myAddress.setText("No location found..!");
// Toast.makeText(getActivity(),"Please switch on yor gps",Toast.LENGTH_LONG).show();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(getActivity(),"Could not get address..!", Toast.LENGTH_LONG).show();
}
}else{
// can't get location
// GPS or Network is not enabled
// Ask user to enable GPS/network in settings
gps.showSettingsAlert();
}
}
CODE 2
public class Audios extends BaseCollection<Audio> {
private static String newValue;
public static setNewValue(String value) {
this.newValue = value;
//Code to use this value.
}
#Override
public void loadWithJson(JSONArray jsonObj) {
if(null == jsonObj) {
return;
}
try {
List<Audio> entries = new ArrayList<Audio>();
for (int o = 0; o < jsonObj.length(); ++o) {
Audio opt = Audio.fromJson(jsonObj.getJSONObject(o));
// String title = opt.getTitle();
//System.out.println(opt.getTitle().substring(0, 4)); // title.substring(0, 3);
// entries.add(opt);
entries.add(opt);
}
this.entries = entries;
} catch (Exception e) {
e.printStackTrace();
}
}
public void getAudioResult(JSONObject jsonRes) {
int id, grpId, dwnCount, upCount;
if(null != jsonRes) {
try {
id = jsonRes.getInt(Constants.MEDIA_ID);
grpId = jsonRes.getInt(Constants.GROUP_ID);
dwnCount= jsonRes.getInt(Constants.SET_THUMBS_DWN);
upCount = jsonRes.getInt(Constants.SET_THUMBS_UP);
}
catch(JSONException je) { id = grpId = dwnCount = upCount = -1;}
if(-1 == id || -1 == grpId) {
return;
}
for(int iLoop = 0; iLoop < entries.size(); iLoop++) {
Audio opt = entries.get(iLoop);
if(opt.token == id && opt.groupId == grpId) {
opt.thumbDwns = dwnCount;
opt.thumbUps = upCount;
break;
}
}
}
}
}
In code 1 Hear i am getting my place name
Values:
place=str[1].substring(0, 4);
AudiosFragment hm=new AudiosFragment();
Bundle bundle = new Bundle();
bundle.putString("place", str[1].substring(0, 4));
Please tell me how i will pass this place value.
easy way to pass data from one class to another is by using constructor
Consider Example:
Class A{
Object o;
private methodA()
{
B b = new B(o); //here you are passing o to Class B
b.methodB();
}
}
Class B{
Object o;
public B(Object O)
{
this.o=o;
}
public methodB()
{
use object o here
}
}
may be this will help..
You can use a Singleton class but is maybe to complex for only a String [].
public class Singleton {
private static Singleton uniqInstance;
private String str[];
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (uInstance == null) {
uInstance = new Singleton();
}
return uInstance;
}
}
Just create a method inside the Audios Class.
Now, depending on the fact that you want different objects of the Audio Class to have different value for this String, you can define it static or not. Then just call that method.
Example :
Class Audios extends BaseCollection<Audio> {
private static String newValue;
public static void setNewValue(String value) {
this.newValue = value;
//Code to use this value.
..
}
}
From the Fragment, just call Audios.setNewValue("This is the value for the String");