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);
}
}
Related
I am in the midst of developing a mobile application using Xamarin.Forms. The app connects to a BLE device which transmits 16 bytes of data every 100 ms. I am plotting the data with Syncfusion in a bar chart format.
I can connect to the device and receive data without issues. But after a very small amount of time, the app starts to significantly decrease in performance. Soon hereafter, it completely stalls. Obviously I am doing something wrong in handling the incoming data (unless it is a performance issue with the Syncfusion chart).
In a nutshell, this is the process I go through in the app
Pair to the device (outside of the app)
Connect to the device (in the app)
Set up the transmission
Process the incoming data via a Model called SpectrogramModel
Graph the data with Syncfusion in a View called DataPage, which is bound to a ViewModel called DataViewModel
Getting into the nitty-gritty of it all, after pairing and connecting to the device, the following method is called. Could it be the Device.BeginInvokeOnMainThread() call which eventually starts blocking the app? This method is called from a Connection class, which has a reference to the DataViewModel
private void UpdateSpectrogramChart(object sender, EventArgs e)
{
DebugHelper.Message(Type.Method, "UpdateSpectrogramChart");
_characteristic.ValueUpdated += (o, args) =>
{
var raw = args.Characteristic.Value;
for (int i = 0; i < raw.Length; i++)
{
Debug.WriteLine("Level[{0}] = {1}", i, raw[i]);
}
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
DataPageViewModel.Levels.Clear();
for (int i = SpectrogramModel.FrequencyOffset; i < raw.Length; i++)
{
if (SettingsViewModel.IsViewRawData)
{
DataPageViewModel.Title = "Raw data";
DataPageViewModel
.Levels
.Add(
new SpectrogramModel(
raw[i],
1 + (i - SpectrogramModel.FrequencyOffset))
);
}
if (SettingsViewModel.IsViewProcessedData)
{
DataPageViewModel.Title = "Processed data";
DataPageViewModel
.Levels
.Add(
new SpectrogramModel(
raw[i],
1 + (i - SpectrogramModel.FrequencyOffset),
i));
}
}
});
};
}
The SpectrogramModel looks like this
public class SpectrogramModel
{
public SpectrogramModel(byte level, int frequency)
{
Level = level;
Frequency = frequency;
}
public SpectrogramModel(byte level, int frequency, int index) : this(level, frequency)
{
Level = ProcessRawLevel(level, index);
}
private double ProcessRawLevel(byte b, int index)
{
double multiplier = 0.75;
double val = b;
val *= multiplier;
return val;
}
public static readonly int FrequencyOffset = 4;
...
The DataPage looks like this
<chart:SfChart>
<chart:SfChart.Title>
<chart:ChartTitle
Text="{Binding Title}">
</chart:ChartTitle>
</chart:SfChart.Title>
<chart:SfChart.PrimaryAxis>
<chart:CategoryAxis>
</chart:CategoryAxis>
</chart:SfChart.PrimaryAxis>
<chart:SfChart.SecondaryAxis>
<chart:NumericalAxis
Minimum="20"
Maximum="100">
</chart:NumericalAxis>
</chart:SfChart.SecondaryAxis>
<chart:SfChart.Series>
<chart:ColumnSeries ItemsSource="{Binding Levels}" XBindingPath="Frequency" YBindingPath="Level"/>
</chart:SfChart.Series>
</chart:SfChart>
Finally, the DataViewModel which the DataPage is bound
public class DataViewModel : BaseViewModel
{
public DataViewModel()
{
Init();
}
private void Init()
{
Levels = new ObservableCollection<SpectrogramModel>();
for (int i = 0; i < 16; i++) Levels.Add(new SpectrogramModel(20, i));
}
private ObservableCollection<SpectrogramModel> _levels;
public ObservableCollection<SpectrogramModel> Levels
{
get { return _levels; ; }
set
{
_levels = value;
OnPropertyChanged();
}
}
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged();
}
}
}
It should be noted that the UpdateSpectrogramChart() is wrapped in a timer, which looks like this
public void InitTimers()
{
DebugHelper.Message(Type.Method, "InitTimers");
int SECOND = 1000 * 2;
SpectrogramChartTimer = new Timer();
SpectrogramChartTimer.Elapsed += new ElapsedEventHandler(UpdateSpectrogramChart);
SpectrogramChartTimer.Interval = SECOND;
}
I wrapped the call to the UpdateSpectrogramChart() method in a (clear) failed attempt to reduce the performance decrease.
For completeness sake, here is the method body of the method which sets up receiving from the BLE device
public async Task ReceiveFromGattCharacteristic(string service, string characteristic, string descriptor = null)
{
DebugHelper.Message(Type.Method, "ReceiveFromGattCharacteristic");
bleAdapter.DeviceConnected += async (s, e) =>
{
try
{
DebugHelper.Message(Type.Info, "bleAdapter.DeviceConected += async (s, e) ...");
string[] deviceInfo = { e.Device.Name, e.Device.Id.ToString() };
// Connect to service
try
{
DebugHelper.Message(Type.Info, "Connecting to service...");
_service = await e.Device.GetServiceAsync(Guid.Parse(service));
DebugHelper.Message(Type.Info, "OK");
}
catch (Exception)
{
DebugHelper.Error(ErrorType.GATT, "Could not connect to service");
}
// Connect to characteristic
try
{
DebugHelper.Message(Type.Info, "Connecting to characteristic...");
_characteristic = await _service.GetCharacteristicAsync(Guid.Parse(characteristic));
DebugHelper.Message(Type.Info, "OK");
}
catch (Exception)
{
DebugHelper.Error(ErrorType.GATT, "Could not connect to characteristic");
}
await ConfigureSpectrogram(UpdateFrequency.High, 0x1);
try
{
await _characteristic.StartUpdatesAsync();
}
catch
{
DebugHelper.Error(ErrorType.GATT, "Error starting UpdatesAsync");
}
_characteristic.ValueUpdated += (o, args) =>
{
var raw = args.Characteristic.Value;
for (int i = 4; i < raw.Length; i++)
{
Debug.WriteLine("Level[{0}] = {1}", i - 4, raw[i]);
}
};
}
catch (Exception)
{
DebugHelper.Error(ErrorType.GATT, "Error in ReceiveFromGattCharacteristic");
}
};
}
Well, I am not sure if this really qualifies as an answer but I seem to have solved the problem although I can't say for sure why this has solved it.
After fiddling with a BackgroundWorker, which introduced even more errors (probably because I am no expert on the usage of it), I revised the code and moved the update of the Model and the View directly into the ReceiveFromGattCharacteristic(), method instead of updating the Model and the View in a separate method, as follows:
public void ReceiveFromGattCharacteristic(string service, string characteristic, string descriptor = null)
{
DebugHelper.Message(Type.Method, "ReceiveFromGattCharacteristic");
bleAdapter.DeviceConnected += async (s, e) =>
{
try
{
DebugHelper.Message(Type.Info, "bleAdapter.DeviceConected += async (s, e) ...");
string[] deviceInfo = { e.Device.Name, e.Device.Id.ToString() };
// Connect to service
try
{
DebugHelper.Message(Type.Info, "Connecting to service...");
_service = await e.Device.GetServiceAsync(Guid.Parse(service));
DebugHelper.Message(Type.Info, "OK");
}
catch (Exception)
{
DebugHelper.Error(ErrorType.GATT, "Could not connect to service");
}
// Connect to characteristic
try
{
DebugHelper.Message(Type.Info, "Connecting to characteristic...");
_characteristic = await _service.GetCharacteristicAsync(Guid.Parse(characteristic));
DebugHelper.Message(Type.Info, "OK");
}
catch (Exception)
{
DebugHelper.Error(ErrorType.GATT, "Could not connect to characteristic");
}
await ConfigureSpectrogram(UpdateFrequency.High, 0x1);
try
{
await _characteristic.StartUpdatesAsync();
}
catch
{
DebugHelper.Error(ErrorType.GATT, "Error starting UpdatesAsync");
}
// ADDITION
_characteristic.ValueUpdated += (o, args) =>
{
var raw = args.Characteristic.Value;
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
DataPageViewModel.Levels.Clear();
for (int i = Models.Spectrogram.FrequencyOffset; i < raw.Length; i++)
{
if (SettingsViewModel.IsViewRawData)
{
DataPageViewModel.Title = "Raw data";
DataPageViewModel
.Levels
.Add(
new Models.Spectrogram(
raw[i],
1 + (i - Models.Spectrogram.FrequencyOffset))
);
}
if (SettingsViewModel.IsViewProcessedData)
{
DataPageViewModel.Title = "Processed data";
DataPageViewModel
.Levels
.Add(
new Models.Spectrogram(
raw[i],
1 + (i - Models.Spectrogram.FrequencyOffset),
i));
}
}
});
};
}
// END OF ADDITION
catch (Exception)
{
DebugHelper.Error(ErrorType.GATT, "Error in ReceiveFromGattCharacteristic");
}
};
}
We would like to let you know that some of the SfChart configuration need to consider while using huge amount of data and increasing the performance.
Make use of SuspendSeriesNotification and ResumeSeriesNoification.
We can stop the chart from being updated for each modification in the items source collection. By using SuspendSeriesNotification and ResumeSeriesNotification methods.
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
DataPageViewModel.Levels.Clear();
Chart.SuspendSeriesNotification();
for (int i = SpectrogramModel.FrequencyOffset; i < raw.Length; i++)
{
if (SettingsViewModel.IsViewRawData)
{
DataPageViewModel.Title = "Raw data";
DataPageViewModel
.Levels
.Add(
new SpectrogramModel(
raw[i],
1 + (i - SpectrogramModel.FrequencyOffset))
);
}
if (SettingsViewModel.IsViewProcessedData)
{
DataPageViewModel.Title = "Processed data";
DataPageViewModel
.Levels
.Add(
new SpectrogramModel(
raw[i],
1 + (i - SpectrogramModel.FrequencyOffset),
i));
}
}
Chart.ResumeSeriesNotification();
});
Avoid use of Category Axis.
We have figured that you are using Category axis with column series. We always suggested to use Numeric or datetime axis with fast line series to get better performance. If you really need the category axis and column series, please let us know how many data your chart will load or any reason to use of category axis.
Some of the tips to get better performance from SfChart, please read the below blog.
https://blog.syncfusion.com/post/7-tips-to-optimize-xamarin-charts-performance.aspx#comment-10677
Regards,
Bharathi.
i'm trying to get the coordinates of my kml layer in my android app, and i just cant seem to find how to do it.
i have this kml layer:
KmlLayer layer = new KmlLayer(mMap, R.raw.allowedarea, getApplicationContext());
and i'm trying to get the Latitude and Longtitude list of his boundries points.
ArrayList<LatLnt> latlitArray = layer.soemthing();
could find anything, please guys help.
Try this solution
try {
KmlLayer layer = new KmlLayer(googleMap, R.raw.zone, this);
layer.addLayerToMap();
Iterable<KmlContainer> containers = layer.getContainers();
accessContainers(containers);
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
public void accessContainers(Iterable<KmlContainer> containers) {
for (KmlContainer container : containers) {
if (container != null) {
if (container.hasContainers()) {
accessContainers(container.getContainers());
} else {
if (container.hasPlacemarks()) {
accessPlacemarks(container.getPlacemarks());
}
}
}
}
}
public void accessPlacemarks(Iterable<KmlPlacemark> placemarks) {
for (KmlPlacemark placemark : placemarks) {
if (placemark != null) {
KmlGeometry geometry = placemark.getGeometry();
if (geometry instanceof KmlPolygon) {
KmlPolygon polygon = (KmlPolygon) geometry;
mLatLngList.addAll(polygon.getOuterBoundaryCoordinates());
}
}
}
}
This will recursively access every placemark geometry inside the container. I'm not aware if the object obtained can actually be an instance of any other class or collection besides List and LatLng.
public void accessContainers(Iterable<KmlContainer> containers) {
for(KmlContainer c : containers) {
if(c.hasPlacemarks()) {
for(KmlPlacemark p : c.getPlacemarks()) {
KmlGeometry g = p.getGeometry();
Object object = g.getGeometryObject();
if(object instanceof LatLng) {
LatLng latlng = (LatLng)object;
//Do more stuff with the point
}
if(object instanceof List<?>) {
List<LatLng> list = (List<LatLng>)object;
//Do more stuff with the list of points
}
Log.d(TAG, g.getGeometryType() + ":" + object.toString());
}
}
if(c.hasContainers()) {
accessContainers(c.getContainers());
}
}
}
I am trying to log location during an activity however I am not getting location updates. I have played around with the min refresh interval as well as the distance interval however it still doesn't work. I followed Xamarin's example for location updates but I've been banging my head against the wall as to why location updates arent working!
public class ActivityManager : Java.Lang.Object, ILocationListener, ISensorEventListener
{
private readonly LocationManager _locationManager;
private readonly SensorManager _sensorManager;
private readonly List<Location> _locationCache;
private readonly List<SensorEvent> _sensorCache;
private bool Continous { get; set; }
public ActivityManager(LocationManager locationManager, SensorManager sensorManager)
{
_locationManager = locationManager;
_sensorManager = sensorManager;
_locationCache = new List<Location>();
_sensorCache = new List<SensorEvent>();
Continous = false;
}
public void StartTrackingLocation()
{
const string provider = LocationManager.GpsProvider;
if (_locationManager.IsProviderEnabled(provider))
{
_locationManager.RequestLocationUpdates(provider, 0, 0, this);
}
}
public void StartTrackingAccelerometer()
{
var mHandlerThread = new HandlerThread("sensorThread");
mHandlerThread.Start();
var handler = new Handler(mHandlerThread.Looper);
_sensorManager.RegisterListener(this, _sensorManager.GetDefaultSensor(SensorType.Accelerometer),
SensorDelay.Normal, handler);
}
public void StopTrackingLocation()
{
_locationManager.RemoveUpdates(this);
}
public void StopTrackingAccelerometer()
{
_sensorManager.UnregisterListener(this);
}
public void StartContinousTracking()
{
_locationCache.Clear();
_sensorCache.Clear();
Continous = true;
}
public void StopContinousTracking()
{
_locationCache.Clear();
_sensorCache.Clear();
Continous = false;
}
public void ExportLocationData(string path)
{
var kml = new Kml
{
Feature = new Placemark
{
Geometry = new LineString
{
Coordinates = new CoordinateCollection(_locationCache.Select(l => new Vector {Latitude = l.Latitude, Longitude = l.Longitude}))
}
}
};
var kmlFile = KmlFile.Create(kml, true);
using (var stream = File.OpenWrite(path))
{
kmlFile.Save(stream);
}
}
public void ExportSensorData(string path)
{
var csv = new CsvWriter(new StreamWriter(path));
csv.WriteField("AccX");
csv.WriteField("AccY");
csv.WriteField("AccZ");
csv.NextRecord();
foreach (var s in _sensorCache.ToList())
{
csv.WriteField(s.Values[0]);
csv.WriteField(s.Values[1]);
csv.WriteField(s.Values[2]);
csv.NextRecord();
}
csv.Dispose();
}
public void OnLocationChanged(Location location)
{
_locationCache.Add(location);
if (!Continous) _locationCache.RemoveAll(l => location.Time - l.Time > 120000);
}
public void OnSensorChanged(SensorEvent e)
{
_sensorCache.Add(e);
if (!Continous) _sensorCache.RemoveAll(s => e.Timestamp - s.Timestamp > 120000000000);
}
public void OnProviderDisabled(string provider) { }
public void OnProviderEnabled(string provider) { }
public void OnStatusChanged(string provider, Availability status, Bundle extras) { }
public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy) { }
}
Thanks in advance.
This is the implementation I have used for the Android element of a Xamarin.Forms app:
The communicating class
public class PlatformLocation : ILocation
{
private readonly global::Android.Locations.LocationManager _locationManager;
private readonly string[] _providers;
private readonly TaskCompletionSource<PfgLocationInfo> _tcs;
public PlatformLocation()
{
_locationManager = (global::Android.Locations.LocationManager)Application.Context.GetSystemService(Context.LocationService);
_providers = _locationManager.GetProviders(false).Where(s => s != global::Android.Locations.LocationManager.PassiveProvider).ToArray();
_tcs = new TaskCompletionSource<PfgLocationInfo>();
}
#region Private Methods
Task<PfgLocationInfo> StartUpdatingLocation(int timeout = 0)
{
var lastKnownGpsLocation = _locationManager.GetLastKnownLocation("gps");
if (lastKnownGpsLocation != null)
{
var pfgLocation = new PfgLocationInfo()
{
Longitude = lastKnownGpsLocation.Longitude,
Latitude = lastKnownGpsLocation.Latitude,
Timestamp = DateTime.Now,
Success = true,
Status = PfgLocationStatus.Valid
};
_tcs.TrySetResult(pfgLocation);
return _tcs.Task;
}
var lastKnownNetworkLocation = _locationManager.GetLastKnownLocation("network");
if (lastKnownNetworkLocation != null)
{
var pfgLocation = new PfgLocationInfo()
{
Longitude = lastKnownNetworkLocation.Longitude,
Latitude = lastKnownNetworkLocation.Latitude,
Timestamp = DateTime.Now,
Success = true,
Status = PfgLocationStatus.Valid
};
_tcs.TrySetResult(pfgLocation);
return _tcs.Task;
}
LocationListener listener = null;
listener = new LocationListener(
_providers.Where(_locationManager.IsProviderEnabled),
() =>
{
if (listener.Task.IsCanceled)
{
_locationManager.RemoveUpdates(listener);
}
},
timeout
);
try
{
var looper = Looper.MyLooper() ?? Looper.MainLooper;
var enabled = 0;
for (var i = 0; i < _providers.Length; ++i)
{
if (_locationManager.IsProviderEnabled(_providers[i]))
{
enabled++;
}
_locationManager.RequestLocationUpdates(_providers[i], 0, 0, listener, looper);
}
if (enabled == 0)
{
for (var i = 0; i < _providers.Length; ++i)
{
_locationManager.RemoveUpdates(listener);
}
_tcs.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
return _tcs.Task;
}
}
catch (TaskCanceledException tcex)
{
_tcs.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
return _tcs.Task;
}
catch (SecurityException)
{
_tcs.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
return _tcs.Task;
}
return listener.Task;
}
#endregion
#region ILocation implementation
public Task<PfgLocationInfo> GetLocationAsync()
{
return StartUpdatingLocation();
}
public Task<PfgLocationInfo> GetLocationAsync(int timeout)
{
return StartUpdatingLocation(timeout);
}
#endregion
}
The ILocationListener implementation:
public class LocationListener : Java.Lang.Object, ILocationListener
{
private readonly HashSet<string> _activeProviders;
private readonly TaskCompletionSource<PfgLocationInfo> _completionSource = new TaskCompletionSource<PfgLocationInfo>();
private readonly Action _finishedCallback;
private readonly Timer _timer;
private readonly int _timeout;
private readonly object _locationLock = new object();
public Task<PfgLocationInfo> Task
{
get
{
return _completionSource.Task;
}
}
public LocationListener(IEnumerable<string> providers, Action finishedCallback, int timeout = -1)
{
_activeProviders = new HashSet<string>(providers);
_finishedCallback = finishedCallback;
if (timeout > 0)
{
_timeout = timeout;
_timer = new Timer(TimesUp, null, _timeout * 1000, 0);
}
}
#region Timeout Methods
void TimesUp(object state)
{
lock (_locationLock)
{
if (_completionSource.TrySetCanceled() && _finishedCallback != null)
{
_finishedCallback();
}
}
_timer.Dispose();
}
#endregion
#region ILocationListener implementation
public void OnLocationChanged(global::Android.Locations.Location location)
{
if (location != null)
{
Finish(location);
return;
}
}
public void OnProviderDisabled(string provider)
{
lock (_activeProviders)
{
if (_activeProviders.Remove(provider) && _activeProviders.Count == 0)
{
_completionSource.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
}
}
}
public void OnProviderEnabled(string provider)
{
lock (_activeProviders)
_activeProviders.Add(provider);
}
public void OnStatusChanged(string provider, Availability status, global::Android.OS.Bundle extras)
{
switch (status)
{
case Availability.Available:
OnProviderEnabled(provider);
break;
case Availability.OutOfService:
OnProviderDisabled(provider);
break;
}
}
#endregion
#region Private Methods
void Finish(global::Android.Locations.Location location)
{
var pfgLocationInfo = new PfgLocationInfo()
{
Longitude = location.Longitude,
Latitude = location.Latitude,
Timestamp = DateTime.Now,
Success = true,
Status = PfgLocationStatus.Valid
};
_completionSource.TrySetResult(pfgLocationInfo);
}
#endregion
}
The PfgLocationInfo is just a simple class for holding the results
This works exactly as required and should be relatively easy to transform to your requirements.
It turns out this was working but very intermittently. The issue appeared to be that another thread that was running was asynchronously to log the sensor data was queueing up operations which were effectively drowning out the location updates.
Removing the sensor data logging started to allow location updates to come through. This seemed strange to me as this seemed to occur despite the sensor data logging being contained in their own thread.
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");
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