Created
June 4, 2017 05:38
-
-
Save ericbrunner/d13270abd26f2130fc397609b89fc3ef to your computer and use it in GitHub Desktop.
GpsTrackerService
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using Android.App; | |
using Android.Content; | |
using Android.OS; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Plugin.Connectivity; | |
using trucker_rolsped.Models; | |
using Android.Media; | |
using Android.Provider; | |
using Android.Support.V4.App; | |
using Android.Support.V4.Content; | |
using Android.Telephony; | |
using Android.Util; | |
using HockeyApp.Android.Metrics; | |
using Microsoft.WindowsAzure.MobileServices; | |
using Newtonsoft.Json; | |
using Newtonsoft.Json.Linq; | |
using Plugin.Geolocator; | |
using trucker_rolsped.GeoLocation; | |
using trucker_rolsped.StoreManager; | |
namespace trucker_rolsped.Droid.GeoLocation | |
{ | |
[Service] | |
public class GpsTrackerService : Service | |
{ | |
private static readonly string LogTag = "X:TruckerApp-" + typeof(GpsTrackerService).Name; | |
private static readonly int SERVICE_RUNNING_NOTIFICATION_ID = 10000; | |
private static readonly int GPS_NOT_ENABLED_NOTIFICATION_ID = 10001; | |
private MobileServiceClient _mobileServiceClient; | |
private PowerManager.WakeLock _cpuWakeLock; | |
public TelephonyManager GetTelefonyManager() | |
{ | |
return (Android.Telephony.TelephonyManager) GetSystemService(TelephonyService); | |
} | |
public bool IsRunningAsService { get; set; } | |
public override async void OnCreate() | |
{ | |
base.OnCreate(); | |
try | |
{ | |
// keep CPU on even when screen is off. see: https://developer.android.com/training/scheduling/wakelock.html#cpu | |
PowerManager pm = (PowerManager) GetSystemService(PowerService); | |
_cpuWakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "GpsTrackerServiceWakeLock"); | |
_cpuWakeLock.Acquire(); | |
if (MainActivity.CurrentActivity == null) | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.OnCreate :: -------- SERVICE IS RUNNING ---------"); | |
IsRunningAsService = true; | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnCreate :: -------- SERVICE IS RUNNING ---------"); | |
MetricsManager.Register(Application, MainActivity.HockeyAppId); | |
const string imeiKey = "imei"; | |
var telefonyManager = GetTelefonyManager(); | |
if (telefonyManager == null) | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) telefonyManager == null"); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) IMEI == null."); | |
return; | |
} | |
//IMEI number | |
String imei = telefonyManager.DeviceId; | |
if (imei == null) | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) IMEI == null"); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) IMEI == null."); | |
return; | |
} | |
Log.Debug(LogTag, $"GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) IMEI == {imei}"); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, $"GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) IMEI == {imei}"); | |
JObject credentials = new JObject | |
{ | |
[imeiKey] = imei | |
}; | |
_mobileServiceClient = new MobileServiceClient(trucker_rolsped.Helper.Constants.AppServiceUrl); | |
if (_mobileServiceClient == null) | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) _mobileServiceClient == null."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) _mobileServiceClient == null."); | |
return; | |
} | |
await _mobileServiceClient.LoginAsync("custom", credentials); | |
if (_mobileServiceClient.CurrentUser == null) | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) ZUMO USER still == null."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) ZUMO USER still == null."); | |
return; | |
} | |
Log.Debug(LogTag, $"GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) ZUMO USER {_mobileServiceClient.CurrentUser.UserId}"); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, $"GpsTrackerService.OnCreate :: (SERVICE IS RUNNING) ZUMO USER {_mobileServiceClient.CurrentUser.UserId}"); | |
} | |
else | |
{ | |
// When App is running | |
Log.Debug(LogTag, "GpsTrackerService.OnCreate :: +++++++++ APP IS RUNNING ++++++++++"); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnCreate :: +++++++++ APP IS RUNNING ++++++++++"); | |
_mobileServiceClient = OfflineSyncStoreManager.Instance.MobileAppClient; | |
Log.Debug(LogTag, $"GpsTrackerService.OnCreate :: (APP IS RUNNING) ZUMO USER {_mobileServiceClient?.CurrentUser?.UserId}"); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, $"GpsTrackerService.OnCreate :: (APP IS RUNNING) ZUMO USER {_mobileServiceClient?.CurrentUser?.UserId}"); | |
} | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.OnCreate :: Exception:{e.Message} Type:{e.GetType().Name}"); | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
finally | |
{ | |
_cpuWakeLock.Release(); | |
} | |
} | |
void RegisterForegroundService() | |
{ | |
var notification = new NotificationCompat.Builder(this) | |
.SetContentTitle("TruckerApp GPS Service") | |
.SetContentText("TruckerApp GPS Service running ...") | |
.SetTicker("TruckerApp GPS Service") | |
.SetSmallIcon(Resource.Drawable.ic_launcher) | |
.SetContentIntent(BuildIntentToShowMainActivity()) | |
.SetOngoing(true) | |
.AddAction(BuildStopServiceAction()) | |
.Build(); | |
// Enlist this instance of the service as a foreground service | |
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification); | |
} | |
/// <summary> | |
/// Builds a PendingIntent that will display the main activity of the app. This is used when the | |
/// user taps on the notification; it will take them to the main activity of the app. | |
/// </summary> | |
/// <returns>The content intent.</returns> | |
PendingIntent BuildIntentToShowMainActivity() | |
{ | |
var notificationIntent = new Intent(this, typeof(MainActivity)); | |
notificationIntent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTask); | |
var pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, PendingIntentFlags.UpdateCurrent); | |
return pendingIntent; | |
} | |
/// <summary> | |
/// Builds the Notification.Action that will allow the user to stop the service via the | |
/// notification in the status bar | |
/// </summary> | |
/// <returns>The stop service action.</returns> | |
NotificationCompat.Action BuildStopServiceAction() | |
{ | |
var stopServiceIntent = new Intent(this, GetType()); | |
stopServiceIntent.SetAction(GpsConstants.ACTION_STOP_SERVICE); | |
var stopServicePendingIntent = PendingIntent.GetService(this, 0, stopServiceIntent, 0); | |
var builder = new NotificationCompat.Action.Builder(Android.Resource.Drawable.IcMediaPause, | |
"Stop", | |
stopServicePendingIntent); | |
return builder.Build(); | |
} | |
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) | |
{ | |
base.OnStartCommand(intent, flags, startId); | |
if (intent.Action != null && intent.Action.Equals(GpsConstants.ACTION_STOP_SERVICE)) | |
{ | |
try | |
{ | |
Log.Info(LogTag, "GpsTrackerService.OnStartCommand :: The service is stopping."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnStartCommand :: The service is stopping."); | |
StopForeground(true); | |
StopSelf(); | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.OnStartCommand :: (ACTION_STOP_SERVICE branch) Exception:{e.Message} Type:{e.GetType().Name}"); | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
} | |
else | |
{ | |
try | |
{ | |
_cpuWakeLock.Acquire(); | |
RegisterForegroundService(); | |
Log.Debug(LogTag, "GpsTrackerService.OnStartCommand :: Service started."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnStartCommand :: Service started."); | |
Task.Run(async () => | |
{ | |
try | |
{ | |
await DoWork(); | |
await Task.Delay(TimeSpan.FromMinutes(2)); | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback :: (OUTER try-catch) Exception:{e.Message} Type:{e.GetType().Name}, Stacktrack:{e.StackTrace}"); | |
if (e.InnerException != null) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback :: (OUTER try-catch) Exception:{e.InnerException.Message} Type:{e.InnerException.GetType().Name}, Stacktrack:{e.InnerException.StackTrace}"); | |
} | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
finally | |
{ | |
// Restart Service | |
Intent broadcastIntent = new Intent(action: GpsConstants.GpsRestart); | |
SendBroadcast(broadcastIntent); | |
try | |
{ | |
_cpuWakeLock.Release(); | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.OnStartCommand :: (_cpuWakeLock.Release) Exception:{e.Message} Type:{e.GetType().Name}"); | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
} | |
}); | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.OnStartCommand :: (DoWork branch) Exception:{e.Message} Type:{e.GetType().Name}"); | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
} | |
return StartCommandResult.Sticky; | |
} | |
public bool IsWorkingDayTime() | |
{ | |
var now = DateTime.Now; | |
return (now.DayOfWeek != DayOfWeek.Saturday || now.DayOfWeek != DayOfWeek.Sunday) && (now.Hour > 4 && now.Hour < 21); | |
} | |
public async Task DoWork() | |
{ | |
// long running code ... | |
Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: Invoked."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: Invoked."); | |
if (CrossConnectivity.Current.IsConnected) | |
{ | |
//if (!IsWorkingDayTime()) | |
//{ | |
// Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: Skip Gps data tracking out of business time (Sat,Sun, 21 p.m - 4 a.m.)."); | |
// GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: Skip Gps data tracking out of business time (Sat,Sun, 21 p.m - 4 a.m.)."); | |
// return; | |
//} | |
TruckFahrerGeoData truckFahrerGeoData = new TruckFahrerGeoData(); | |
TruckFahrerGeoPosition truckFahrerGeoPosition = new TruckFahrerGeoPosition(); | |
try | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: BEFORE GPS Fetch."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: BEFORE GPS Fetch."); | |
var crossGeolocator = CrossGeolocator.Current; | |
if (crossGeolocator == null) | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: crossGeolocator == null."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: crossGeolocator == null."); | |
return; | |
} | |
// getting GPS status | |
var gpsSensorStatus = await GeoLocationDataManager.Instance.HasGpsPermissionsEnabled(serviceMode: IsRunningAsService); | |
if (!gpsSensorStatus) | |
{ | |
var notificationManager = (NotificationManager) GetSystemService(NotificationService); | |
if (notificationManager != null) | |
{ | |
try | |
{ | |
Intent gpsSettingsIntent = new Intent(Settings.ActionLocationSourceSettings); | |
//activity will not be launched if it is already running at the top of the history stack. | |
gpsSettingsIntent.AddFlags(ActivityFlags.SingleTop); | |
if (IsRunningAsService) | |
{ | |
gpsSettingsIntent.AddFlags(ActivityFlags.NewTask); | |
} | |
NotificationCompat.Builder builder = new NotificationCompat.Builder(this); | |
var notification = builder | |
.SetContentIntent(PendingIntent.GetActivity(this, 0, gpsSettingsIntent, 0)) | |
.SetSmallIcon(Resource.Drawable.ic_launcher) | |
.SetContentTitle("Nachricht von TruckerApp GPS Service") | |
.SetTicker("Nachricht von TruckerApp GPS Service") | |
.SetContentText("GPS basierte Auftragszuteilung konnte nicht gestarted werden. " + | |
"Bitte aktivieren Sie GPS in den Einstellungen. " + | |
"Diese ist nur innerhalb möglicher Transportzeiten von Mo-Fr von 04:00:00-21:00:00 Uhr erforderlich. " + | |
"Ausserhalb dieser Zeiten werden keine GPS Daten verwendet.") | |
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification)) | |
.SetAutoCancel(true) | |
.Build(); | |
notificationManager.Notify(GPS_NOT_ENABLED_NOTIFICATION_ID, notification); | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback :: (at Notification) Exception:{e.Message} Type:{e.GetType().Name}"); | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
} | |
else | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: notificationManager == null."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: notificationManager == null."); | |
} | |
return; | |
} | |
crossGeolocator.DesiredAccuracy = 50; | |
var location = await crossGeolocator.GetPositionAsync(TimeSpan.FromSeconds(5)); | |
if (location == null) | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: location == null from LocationManager.GpsProvider"); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: location == null from LocationManager.GpsProvider."); | |
return; | |
} | |
int castedLat = (int) location.Latitude; | |
int castedLon = (int) location.Longitude; | |
if (castedLat == 0 || castedLon == 0) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback ::Skip that Lat/Lon({castedLat}/{castedLon}) because of 0 values."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, $"GpsTrackerService.HandleTimerCallback ::Skip that Lat/Lon({castedLat}/{castedLon}) because of 0 values."); | |
return; | |
} | |
truckFahrerGeoPosition.Latitude = location.Latitude; | |
truckFahrerGeoPosition.Longitude = location.Longitude; | |
truckFahrerGeoPosition.Accuracy = location.Accuracy; | |
truckFahrerGeoPosition.Speed = location.Speed; | |
await GpsTrackerServiceUtils.PersistGeoLocationData(LogTag, _mobileServiceClient, this, truckFahrerGeoPosition, truckFahrerGeoData); | |
Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: AFTER GPS Fetch."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: AFTER GPS Fetch."); | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback :: Exception:{e.Message} Type:{e.GetType().Name}, Stacktrack:{e.StackTrace}"); | |
if (e.InnerException != null) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback :: Exception:{e.InnerException.Message} Type:{e.InnerException.GetType().Name}, Stacktrack:{e.InnerException.StackTrace}"); | |
} | |
Dictionary<string, string> telemetryDictionary = new Dictionary<string, string>(); | |
var serializedGeoData = JsonConvert.SerializeObject(truckFahrerGeoData); | |
telemetryDictionary.Add("TruckFahrerGeoData", serializedGeoData); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Error", LogTag, _mobileServiceClient, this, e.Message, telemetryDictionary); | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
} | |
} | |
public override IBinder OnBind(Intent intent) | |
{ | |
// This is a started service, not a bound service, so we just return null. | |
return null; | |
} | |
public override void OnDestroy() | |
{ | |
try | |
{ | |
Log.Debug(LogTag, "GpsTrackerService.OnDestroy :: Service stopped."); | |
GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnStartCommand :: Service stopped."); | |
StopForeground(removeNotification: true); | |
// Remove the notification from the status bar. | |
var notificationManager = (NotificationManager) GetSystemService(NotificationService); | |
notificationManager.Cancel(SERVICE_RUNNING_NOTIFICATION_ID); | |
// whe app is running and service stopped on task manager | |
if (MainActivity.CurrentActivity != null) | |
{ | |
Intent broadcastIntent = new Intent(action: GpsConstants.GpsRestart); | |
SendBroadcast(broadcastIntent); | |
} | |
//TODO: We must discuss that in detail. Currently no restart occurs on service. If desired uncomment the below lines. | |
// TODO: Maybe through a flag in TruckFahrer Table. e.g. if tf.Status == "FULLTIME_ROLSPED" -> restart service on app close . | |
//Intent broadcastIntent = new Intent(action: GpsConstants.GpsRestart); | |
//SendBroadcast(broadcastIntent); | |
//Log.Debug(LogTag, "GpsTrackerService.OnDestroy :: Gps Service restart broadcast message sent."); | |
//GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnDestroy :: Gps Service restart broadcast message sent."); | |
} | |
catch (Exception e) | |
{ | |
Log.Debug(LogTag, $"GpsTrackerService.OnDestroy :: Exception:{e.Message} Type:{e.GetType().Name}"); | |
GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this); | |
} | |
base.OnDestroy(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment