using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Xamarin.Forms; using InABox.Core; using InABox.Configuration; using InABox.Clients; using InABox.Mobile; using Comal.Classes; using XF.Material.Forms.UI.Dialogs; using comal.timesheets.CustomControls; using comal.timesheets.StoreRequis; using PRSSecurity = InABox.Core.Security; using Plugin.LocalNotification; using comal.timesheets.Tasks; using System.IO; using static comal.timesheets.CustomControls.JobShell; using static Android.Graphics.Paint; using static InABox.Mobile.LocationServices; namespace comal.timesheets { public delegate void MainPageNotificationsChanged(); public delegate void RefreshScreen(); public static class MainPageUtils { public static event MainPageNotificationsChanged OnMainPageNotificationsChanged; public static event RefreshScreen OnRefreshScreen; public static Assignment CurrentAssignment = null; public static JobShell Job = new JobShell(); public static int NumberOfNotifications = 0; public static string matchedDeviceName = ""; public static string deviceName = ""; public static bool bRecentlyUpdatedTiles = false; public static TimeSheet _timesheet = null; public static Employee _employee = null; public static CoreTable _jobs = null; public static bool firstLoad = true; public static bool recentlyAskedToUpdate = true; public static int updateCounter; public static void Init() { InitEvents(); InitData(); InitTimers(); } private static void InitEvents() { App.GPS.OnLocationFound += LocationFound; App.GPS.OnLocationError += LocationError; App.Bluetooth.OnScanFinished += ScanFinished; App.Data.DataChanged += (s, t, e) => { OnRefreshScreen?.Invoke(); }; App.Data.DataRefreshed += () => { OnRefreshScreen?.Invoke(); }; } private static void InitData() { GlobalVariables.EmpID = GlobalVariables.GetEmployeeID(); GlobalVariables.EmpName = GlobalVariables.GetEmployeeName(); App.Data.Employee.ID = GlobalVariables.EmpID; App.Data.Employee.Name = GlobalVariables.EmpName; _timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject(); _employee = App.Data.Employee; _jobs = App.Data.Jobs; deviceName = MobileUtils.GetDeviceID(); firstLoad = false; } private static void InitTimers() { Timer t = new Timer(RecentlyAskedToUpdateTimer, null, 600000, 600000); //user is reminded to update when opening screen after timer of 10 minutes updateCounter = 1; //user is forced to update after 3rd reminder Timer t1 = new Timer(RecentlyUpdatedTilesTimer, null, 30000, 30000); //bluetooth data is allowed to upload once every minute, notifications refreshing is piggybacked to this too } private static void RecentlyAskedToUpdateTimer(object o) { recentlyAskedToUpdate = false; } private static void RecentlyUpdatedTilesTimer(object o) { bRecentlyUpdatedTiles = false; App.Data.Refresh(true); SearchForNewNotifications(); } public static Assignment CheckCurrentAssignment() { Thread.Sleep(5000); StartAssignmentTimer(); CoreTable table = new Client().Query( new Filter(x => x.EmployeeLink.ID).IsEqualTo(GlobalVariables.EmpID) .And(x => x.Date).IsEqualTo(DateTime.Today.Date) .And(x => x.Actual.Finish).IsEqualTo(null), null, new SortOrder(x => x.Actual.Start, SortDirection.Ascending)); if (!table.Rows.Any()) { Job.OnJobIDChanged += OnJobIDChanged; return null; } else return table.Rows.LastOrDefault().ToObject(); } private static void StartAssignmentTimer() { Timer t = new Timer(AssignmentTimerCallback, null, 300000, 300000); } private static void AssignmentTimerCallback(object state) { if (CurrentAssignment != null) SaveCurrentAssignment("PRS Mobile main screen - Saving assignment on 5 minute timer"); } public static void SaveCurrentAssignment(string auditnote, bool complete = false) { if (!complete) CurrentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay; else CurrentAssignment.Actual.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay; new Client().Save(CurrentAssignment, auditnote); } static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d) { int f = 0; double m = (double)(dt.Ticks % d.Ticks) / d.Ticks; if (m >= 0.5) f = 1; return new DateTime(((dt.Ticks / d.Ticks) + f) * d.Ticks); } public static void UseCurrentAssignment(Assignment assgn) { CurrentAssignment = assgn; SaveCurrentAssignment("PRS Mobile main screen - saving assignment on re-login to App"); if (CurrentAssignment.JobLink.ID != Guid.Empty) { Job.ID = CurrentAssignment.JobLink.ID; var job = new Client().Query(new Filter(x => x.ID).IsEqualTo(Job.ID)).Rows.FirstOrDefault().ToObject(); Job.JobNumber = job.JobNumber; Job.Name = job.Name; Job.OnJobIDChanged += OnJobIDChanged; } } public static void OnJobIDChanged(Guid jobid) { if (CurrentAssignment == null) CreateNewAssignment(jobid); else { SaveCurrentAssignment("PRS Mobile main screen - saving assignment on job change", true); CreateNewAssignment(jobid); } } private static void CreateNewAssignment(Guid jobid) { CurrentAssignment = new Assignment { Date = DateTime.Now.Date }; CurrentAssignment.EmployeeLink.ID = GlobalVariables.EmpID; CurrentAssignment.JobLink.ID = jobid; CurrentAssignment.Actual.Start = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay; CurrentAssignment.Booked.Start = CurrentAssignment.Actual.Start; CurrentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay.Add(new TimeSpan(0, 5, 0)); var job = new Client().Query(new Filter(x => x.ID).IsEqualTo(jobid)).Rows.FirstOrDefault().ToObject(); CurrentAssignment.Title = "Clocking on to job " + job.Name + " (" + job.JobNumber + ") on PRS Mobile"; new Client().Save(CurrentAssignment, "Changed job on mobile - creating new assignment"); } #region Notifications public static Page DetermineCorrectPage(Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e) { string data = e.Request.ReturningData; int index = data.IndexOf("$"); Guid ID = Guid.Parse(data.Remove(index)); string type = data.Substring(index + 1); if (type == "Comal.Classes.Kanban") return new AddEditTask(ID); else if (type == "Comal.Classes.Delivery") return new DeliveryDetails(ID); else return null; } public static async void SearchForNewNotifications() { //notifications poll reliably in the background for Anroid, unreliable for iOS due to difficulty with cross-platform plugins for notifications try { await Task.Run(() => { if (ClientFactory.UserGuid != Guid.Empty) { CoreTable table = new Client().Query (new Filter(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid).And(X => X.Closed).IsEqualTo(DateTime.MinValue), new Columns( x => x.ID, //0 x => x.Sender.Name, //1 x => x.Title, //2 x => x.Created, //3 x => x.Description, //4 x => x.EntityType, //5 x => x.EntityID //6 ) ); if (NumberOfNotifications == table.Rows.Count()) //no new notifications or none present at all return; else //new notifications or previous notifications have now been dismissed { NumberOfNotifications = table.Rows.Count(); OnMainPageNotificationsChanged?.Invoke(); CheckNotificationsPushed(table); } } }); } catch { } } private static void CheckNotificationsPushed(CoreTable table) { try { if (!Application.Current.Properties.ContainsKey("LastPushedNotifications")) { Application.Current.Properties.Add("LastPushedNotifications", DateTime.Now); } DateTime lastPushed = DateTime.Parse(Application.Current.Properties["LastPushedNotifications"].ToString()); List toNotify = new List(); foreach (CoreRow row in table.Rows) { List list = row.Values; DateTime created = DateTime.Parse(list[3].ToString()); if (created > new DateTime(2022, 8, 22)) // prevent spam from buildup of old notifications before this is released { if (created > lastPushed) { if (list[1] == null) list[1] = ""; if (list[2] == null) list[2] = ""; if (list[3] == null) list[3] = DateTime.MinValue; if (list[4] == null) list[4] = ""; if (list[5] == null) list[5] = ""; if (list[6] == null) list[6] = Guid.Empty; NotificationShell shell = new NotificationShell { ID = Guid.Parse(list[0].ToString()), Sender = list[1].ToString(), Title = list[2].ToString(), Created = DateTime.Parse(list[3].ToString()), EntityType = list[5].ToString(), EntityID = Guid.Parse(list[6].ToString()) }; toNotify.Add(shell); //add notification to be pushed } } } if (toNotify.Count > 0) PushNotificationsAsync(toNotify); } catch { } } private static async Task PushNotificationsAsync(List shells) { try { int count = 1; foreach (NotificationShell shell in shells) { var notification = new NotificationRequest { BadgeNumber = 1, Description = shell.Title, Title = "New PRS Notification: ", ReturningData = shell.EntityID.ToString() + "$" + shell.EntityType, NotificationId = count, }; count++; NotificationImage img = new NotificationImage(); img.ResourceName = "icon16.png"; notification.Image = img; await LocalNotificationCenter.Current.Show(notification); } Application.Current.Properties["LastPushedNotifications"] = DateTime.Now; } catch { } } #endregion private static void LocationFound(LocationServices sender) { //if (bSharedDevice) // return; if (App.Bluetooth.RecentlyScanned) UploadTiles(); try { TimeSheet timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject(); if (timesheet != null) { if (timesheet.StartLocation.Latitude.Equals(0.0F) && timesheet.StartLocation.Longitude.Equals(0.0F)) { timesheet.StartLocation.Latitude = sender.Latitude; timesheet.StartLocation.Longitude = sender.Longitude; timesheet.StartLocation.Timestamp = sender.TimeStamp; timesheet.Address = sender.Address; new Client().Save(timesheet, "Updating Timesheet with GPS Coordinates", (o, e) => { }); } } if (!string.IsNullOrWhiteSpace(matchedDeviceName)) { InABox.Core.Location curlocation = new InABox.Core.Location() { Latitude = App.GPS.Latitude, Longitude = App.GPS.Longitude }; curlocation.Timestamp = DateTime.Now; GPSTrackerLocation gpsTrackerLocation = new GPSTrackerLocation(); gpsTrackerLocation.DeviceID = matchedDeviceName; gpsTrackerLocation.Location.Timestamp = curlocation.Timestamp; gpsTrackerLocation.Location = curlocation; new Client().Save(gpsTrackerLocation, "Updated company device location from Timebench"); } OnRefreshScreen?.Invoke(); } catch { } } private static async void UploadTiles() { try { if (App.GPS.Latitude.Equals(0.0F) && App.GPS.Longitude.Equals(0.0F)) return; if (App.Bluetooth.DetectedBlueToothMACAddresses.Count == 0) return; if (bRecentlyUpdatedTiles) return; bRecentlyUpdatedTiles = true; await Task.Run(() => { InABox.Core.Location curlocation = new InABox.Core.Location() { Latitude = App.GPS.Latitude, Longitude = App.GPS.Longitude }; curlocation.Timestamp = DateTime.Now; List trackersToUpdate = new List(); foreach (String id in App.Bluetooth.DetectedBlueToothMACAddresses) { GPSTracker tracker = GlobalVariables.GPSTrackerCache.Find(x => x.DeviceID.Equals(id)); bool stale = tracker.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 5, 0)); bool moved = tracker.Location.DistanceTo(curlocation, UnitOfLength.Kilometers) > 0.1; if (stale || moved) { GlobalVariables.GPSTrackerCache.Remove(tracker); tracker.Location = curlocation; GlobalVariables.GPSTrackerCache.Add(tracker); //cache is updated GPSTrackerLocation gpsTrackerLocation = new GPSTrackerLocation(); gpsTrackerLocation.DeviceID = tracker.DeviceID; gpsTrackerLocation.Location.Timestamp = tracker.Location.Timestamp; gpsTrackerLocation.Location = curlocation; trackersToUpdate.Add(gpsTrackerLocation); } } if (trackersToUpdate.Any()) { if (ClientFactory.UserGuid != Guid.Empty) new Client().Save(trackersToUpdate, "Updating Bluetooth Device Locations"); } App.Bluetooth.DetectedBlueToothMACAddresses.Clear(); } ); } catch (Exception e) { } //if ((master != null) && (master.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0)))) //{ // GPSTrackerLocation device = new GPSTrackerLocation(); // device.DeviceID = MobileUtils.GetDeviceID(); // device.Location.Latitude = App.GPS.Latitude; // device.Location.Longitude = App.GPS.Longitude; // device.Location.Timestamp = DateTime.Now; // locations.Add(device); // //device.BatteryLevel = ((double)CrossBattery.Current.RemainingChargePercent); // //new Client().Save(device, "Updating Device Location"); //, SaveTrackerCallback); //} #region OLD //for (int i = 0; i < App.Bluetooth.Devices.Length; i++) //{ // String id = App.Bluetooth.Devices[i]; // int level = App.Bluetooth.BatteryLevels[i]; // var btmaster = trackers.FirstOrDefault(x => x.DeviceID.Equals(id)); // if ((btmaster != null) && (!locations.Any(x => x.DeviceID.Equals(btmaster.DeviceID)))) // { // bool stale = btmaster.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0)); // bool moved = btmaster.Location.DistanceTo(curlocation, UnitOfLength.Kilometers) > 0.1; // if (stale || moved) // { // GPSTrackerLocation location = new GPSTrackerLocation(); // location.DeviceID = id; // location.Location.Latitude = App.GPS.Latitude; // location.Location.Longitude = App.GPS.Longitude; // location.Location.Timestamp = DateTime.Now; // location.BatteryLevel = level; // locations.Add(location); // } // } // //new Client().Save(location, "Found Kontakt Device"); //, SaveTrackerCallback); //} //if (locations.Any()) // new Client().Save(locations, "Updating Bluetooth Device Locations", (o, e) => { }); #endregion } private static void LocationError(LocationServices sebder, Exception error) { } private static void ScanFinished(Bluetooth sender) { try { OnRefreshScreen?.Invoke(); if (App.GPS.RecentlyLocated) UploadTiles(); } catch { } } } }