Skip to content

Commit

Permalink
Merge pull request #36 from wrk-fmd/develop
Browse files Browse the repository at this point in the history
RELEASE: Version 1.1.2
  • Loading branch information
robo-w authored Feb 23, 2019
2 parents 56b0934 + cb38cde commit 5f19c44
Show file tree
Hide file tree
Showing 34 changed files with 1,015 additions and 299 deletions.
8 changes: 8 additions & 0 deletions GeoClient/GeoClient.Android/GeoClient.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,21 @@
<PackageReference Include="Xamarin.Android.Support.v4" Version="28.0.0.1" />
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="28.0.0.1" />
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="28.0.0.1" />
<PackageReference Include="Xamarin.GooglePlayServices.Location">
<Version>29.0.0.2</Version>
</PackageReference>
<PackageReference Include="ZXing.Net.Mobile">
<Version>2.4.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Location\AndroidLocationService.cs" />
<Compile Include="Location\GooglePlayLocationProvider.cs" />
<Compile Include="Location\ILocationProvider.cs" />
<Compile Include="Location\InterceptedTaskSchedulerWakeLock.cs" />
<Compile Include="Location\LocationProviderFactory.cs" />
<Compile Include="Location\NativeAndroidLocationProvider.cs" />
<Compile Include="Location\XamarinLocationFactory.cs" />
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
85 changes: 27 additions & 58 deletions GeoClient/GeoClient.Android/Location/AndroidLocationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,25 @@
namespace GeoClient.Droid.Location
{
[Service]
public class AndroidLocationService : Service, ILocationListener
public class AndroidLocationService : Service
{
private const string LoggerTag = "AndroidLocationService";
private const string WakeLockTag = "cocemocl:locationServiceWakeLock";

private const int RunningServiceNotificationId = 144;
private const string NotificationChannelId = "at.wrk.fmd.geo.location.channel";

private const long MinimumElapsedTimeInMilliseconds = 10000;

// Only use coarse or fine here! Other values are only for bearing, horizontal accuracy, etc.
private const Accuracy RequiredAccuracy = Accuracy.Fine;

private readonly LocationManager _locationManager;
private readonly LocationChangeRegistry _locationChangeRegistry;
private readonly PowerManager _powerManager;
private readonly LocationProviderFactory _locationProviderFactory;

private ILocationProvider _locationProvider;

public AndroidLocationService()
{
_locationManager = Application.Context.GetSystemService(LocationService) as LocationManager;
_powerManager = Application.Context.GetSystemService(PowerService) as PowerManager;
_locationChangeRegistry = LocationChangeRegistry.Instance;
_locationProviderFactory = new LocationProviderFactory();
}

public override IBinder OnBind(Intent intent)
Expand All @@ -52,7 +49,7 @@ public override void OnDestroy()
base.OnDestroy();
Log.Debug(LoggerTag, "Service has been terminated");

_locationManager.RemoveUpdates(this);
RemoveLocationProviderIfPresent();
}

public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
Expand All @@ -70,17 +67,15 @@ public override StartCommandResult OnStartCommand(Intent intent, StartCommandFla
return StartCommandResult.Sticky;
}

public void OnLocationChanged(Android.Locations.Location location)
private void OnLocationChanged(Xamarin.Essentials.Location updatedLocation)
{
Log.Debug(LoggerTag, "Got a location update from android location manager. location: " + location);
Log.Debug(LoggerTag, "Got a location update from location provider. location: " + updatedLocation);
var wakeLock = _powerManager.NewWakeLock(WakeLockFlags.Partial, WakeLockTag);
wakeLock.Acquire();
Log.Debug(LoggerTag, "Acquired wake lock.");
Log.Debug(LoggerTag, "Acquired wake lock in android location service.");

if (location != null)
if (updatedLocation != null)
{
var updatedLocation = CreateXamarinLocation(location);

_locationChangeRegistry.LocationUpdated(updatedLocation);
}
else
Expand All @@ -89,38 +84,7 @@ public void OnLocationChanged(Android.Locations.Location location)
}

wakeLock.Release();
Log.Debug(LoggerTag, "Released wake lock.");
}

public void OnProviderDisabled(string provider)
{
Log.Warn(LoggerTag, "Location provider was disabled! provider: " + provider);
}

public void OnProviderEnabled(string provider)
{
Log.Info(LoggerTag, "Location provider was enabled. provider: " + provider);
}

public void OnStatusChanged(string provider, Availability status, Bundle extras)
{
Log.Info(LoggerTag,
"Status of location provider changed. provider: " + provider + ", availability: " + status);
}

private static Xamarin.Essentials.Location CreateXamarinLocation(Android.Locations.Location location)
{
var updatedLocation = new Xamarin.Essentials.Location
{
Accuracy = location.Accuracy,
Altitude = location.Altitude,
Course = location.Bearing,
Latitude = location.Latitude,
Longitude = location.Longitude,
Speed = location.Speed,
Timestamp = DateTimeOffset.FromUnixTimeMilliseconds(location.Time)
};
return updatedLocation;
Log.Debug(LoggerTag, "Released wake lock in android location service.");
}

private void CreateForegroundServiceNotificationChannel()
Expand Down Expand Up @@ -161,22 +125,27 @@ private PendingIntent BuildMainActivityPendingIntent()

private void StartLocationUpdates()
{
var locationProvider = GetLocationProvider();
_locationManager.RequestLocationUpdates(locationProvider, MinimumElapsedTimeInMilliseconds, 0, this);
Log.Debug(LoggerTag, "Service is registered for location updates.");
RemoveLocationProviderIfPresent();

AssignNewLocationProvider();
_locationProvider.StartLocationProvider();
}

private string GetLocationProvider()
private void AssignNewLocationProvider()
{
var locationCriteria = new Criteria
_locationProvider = _locationProviderFactory.CreateLocationProvider();
_locationProvider.RegisterLocationUpdateDelegate(OnLocationChanged);
}

private void RemoveLocationProviderIfPresent()
{
if (_locationProvider != null)
{
Accuracy = RequiredAccuracy,
PowerRequirement = Power.NoRequirement
};
var oldProvider = _locationProvider;
_locationProvider = null;

var locationProvider = _locationManager.GetBestProvider(locationCriteria, true);
Log.Debug(LoggerTag, $"You are about to get location updates via {locationProvider}");
return locationProvider;
oldProvider.StopLocationProvider();
}
}
}
}
77 changes: 77 additions & 0 deletions GeoClient/GeoClient.Android/Location/GooglePlayLocationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Android.App;
using Android.Gms.Common;
using Android.Gms.Common.Apis;
using Android.Gms.Location;
using Android.OS;
using Android.Util;

namespace GeoClient.Droid.Location
{
public class GooglePlayLocationProvider : Java.Lang.Object, ILocationListener, ILocationProvider, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
{
private const string LoggerTag = "GooglePlayLocationProvider";

private LocationProviderListener _updateDelegate = (location) =>
{
Log.Warn(LoggerTag, "No location update delegate registered!");
};

private readonly GoogleApiClient _googleApiClient;

public GooglePlayLocationProvider()
{
_googleApiClient = new GoogleApiClient.Builder(Application.Context)
.AddApi(LocationServices.API)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(this)
.Build();
}

public void RegisterLocationUpdateDelegate(LocationProviderListener updateDelegate)
{
_updateDelegate = updateDelegate;
}

public void StartLocationProvider()
{
_googleApiClient.Connect();
}

public void StopLocationProvider()
{
_googleApiClient.Disconnect();
}

public void OnConnected(Bundle connectionHint)
{
var locationRequest = new LocationRequest()
.SetPriority(LocationRequest.PriorityHighAccuracy)
.SetInterval(10000)
.SetFastestInterval(5000);
LocationServices.FusedLocationApi.RequestLocationUpdates(_googleApiClient, locationRequest, this);
}

public void OnConnectionSuspended(int cause)
{
Log.Warn(LoggerTag, "Connection of google API client has been suspended!");
}

public void OnConnectionFailed(ConnectionResult result)
{
Log.Warn(LoggerTag, "Connection to google API client failed!");
}

public void OnLocationChanged(Android.Locations.Location location)
{
if (location != null)
{
var updatedLocation = XamarinLocationFactory.CreateXamarinLocation(location);
_updateDelegate.Invoke(updatedLocation);
}
else
{
Log.Debug(LoggerTag, "Received empty location update from google play provider!");
}
}
}
}
13 changes: 13 additions & 0 deletions GeoClient/GeoClient.Android/Location/ILocationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace GeoClient.Droid.Location
{
public delegate void LocationProviderListener(Xamarin.Essentials.Location updatedLocation);

public interface ILocationProvider
{
void RegisterLocationUpdateDelegate(LocationProviderListener updateDelegate);

void StartLocationProvider();

void StopLocationProvider();
}
}
50 changes: 50 additions & 0 deletions GeoClient/GeoClient.Android/Location/LocationProviderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Android.App;
using Android.Gms.Common;
using Android.Util;

namespace GeoClient.Droid.Location
{
public class LocationProviderFactory
{
private const string LoggerTag = nameof(LocationProviderFactory);

public ILocationProvider CreateLocationProvider()
{
ILocationProvider locationProvider;

if (IsGooglePlayServicesInstalled())
{
locationProvider = new GooglePlayLocationProvider();
}
else
{
locationProvider = new NativeAndroidLocationProvider();
}

return locationProvider;
}

private bool IsGooglePlayServicesInstalled()
{
var queryResult = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(Application.Context);
if (queryResult == ConnectionResult.Success)
{
Log.Info(LoggerTag, "Google Play Services is installed on this device. Will be used as location provider.");
return true;
}

if (GoogleApiAvailability.Instance.IsUserResolvableError(queryResult))
{
var errorString = GoogleApiAvailability.Instance.GetErrorString(queryResult);
Log.Error(LoggerTag, "There is a problem with Google Play Services on this device: {0} - {1}",
queryResult, errorString);
}
else
{
Log.Error(LoggerTag, "Unkown error while checking if Google Play Services are installed.");
}

return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using Android.App;
using Android.Content;
using Android.Locations;
using Android.OS;
using Android.Util;
using Object = Java.Lang.Object;

namespace GeoClient.Droid.Location
{
public class NativeAndroidLocationProvider : Object, ILocationProvider, ILocationListener
{
private const string LoggerTag = "NativeAndroidLocationProvider";

private const long MinimumElapsedTimeInMilliseconds = 10000;

// Only use coarse or fine here! Other values are only for bearing, horizontal accuracy, etc.
private const Accuracy RequiredAccuracy = Accuracy.Fine;

private readonly LocationManager _locationManager;

private LocationProviderListener _locationUpdateDelegate = (location) =>
{
Log.Warn(LoggerTag, "No location update delegate registered!");
};

public NativeAndroidLocationProvider()
{
_locationManager = Application.Context.GetSystemService(Context.LocationService) as LocationManager;
}

public void RegisterLocationUpdateDelegate(LocationProviderListener updateDelegate)
{
_locationUpdateDelegate = updateDelegate;
}

public void StartLocationProvider()
{
var locationProvider = GetLocationProvider();
_locationManager.RequestLocationUpdates(locationProvider, MinimumElapsedTimeInMilliseconds, 0, this);
Log.Debug(LoggerTag, "Native android location provider is registered for location updates.");
}

public void StopLocationProvider()
{
_locationManager.RemoveUpdates(this);
}

public void OnLocationChanged(Android.Locations.Location location)
{
var updatedLocation = XamarinLocationFactory.CreateXamarinLocation(location);
_locationUpdateDelegate.Invoke(updatedLocation);
}

public void OnProviderDisabled(string provider)
{
Log.Warn(LoggerTag, "Location provider was disabled! provider: " + provider);
}

public void OnProviderEnabled(string provider)
{
Log.Info(LoggerTag, "Location provider was enabled. provider: " + provider);
}

public void OnStatusChanged(string provider, Availability status, Bundle extras)
{
Log.Info(
LoggerTag,
"Status of location provider changed. provider: " + provider + ", availability: " + status);
}

private string GetLocationProvider()
{
var locationCriteria = new Criteria
{
Accuracy = RequiredAccuracy,
PowerRequirement = Power.NoRequirement
};

var locationProvider = _locationManager.GetBestProvider(locationCriteria, true);
Log.Debug(LoggerTag, $"You are about to get location updates via {locationProvider}");
return locationProvider;
}
}
}
Loading

0 comments on commit 5f19c44

Please sign in to comment.