MainPageUtils.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Xamarin.Forms;
  7. using InABox.Core;
  8. using InABox.Configuration;
  9. using InABox.Clients;
  10. using InABox.Mobile;
  11. using Comal.Classes;
  12. using XF.Material.Forms.UI.Dialogs;
  13. using comal.timesheets.CustomControls;
  14. using comal.timesheets.StoreRequis;
  15. using PRSSecurity = InABox.Core.Security;
  16. using Plugin.LocalNotification;
  17. using comal.timesheets.Tasks;
  18. using System.IO;
  19. using static comal.timesheets.CustomControls.JobShell;
  20. using static InABox.Mobile.LocationServices;
  21. namespace comal.timesheets
  22. {
  23. public delegate void MainPageNotificationsChanged();
  24. public delegate void RefreshScreen();
  25. public static class MainPageUtils
  26. {
  27. public static event MainPageNotificationsChanged OnMainPageNotificationsChanged;
  28. public static event RefreshScreen OnRefreshScreen;
  29. public static Assignment CurrentAssignment = null;
  30. public static JobShell Job = new JobShell();
  31. public static int NumberOfNotifications = 0;
  32. public static string matchedDeviceName = "";
  33. public static string deviceName = "";
  34. public static bool bRecentlyUpdatedTiles = false;
  35. public static TimeSheet _timesheet = null;
  36. public static Employee _employee = null;
  37. public static CoreTable _jobs = null;
  38. public static bool firstLoad = true;
  39. public static bool recentlyAskedToUpdate = true;
  40. public static int updateCounter;
  41. public static void Init()
  42. {
  43. InitEvents();
  44. InitData();
  45. InitTimers();
  46. }
  47. private static void InitEvents()
  48. {
  49. try
  50. {
  51. App.GPS.OnLocationFound += LocationFound;
  52. App.GPS.OnLocationError += LocationError;
  53. App.Bluetooth.OnScanFinished += ScanFinished;
  54. App.Data.DataChanged += (s, t, e) => { OnRefreshScreen?.Invoke(); };
  55. App.Data.DataRefreshed += () => { OnRefreshScreen?.Invoke(); };
  56. }
  57. catch { }
  58. }
  59. private static void InitData()
  60. {
  61. try
  62. {
  63. GlobalVariables.EmpID = GlobalVariables.GetEmployeeID();
  64. GlobalVariables.EmpName = GlobalVariables.GetEmployeeName();
  65. App.Data.Employee.ID = GlobalVariables.EmpID;
  66. App.Data.Employee.Name = GlobalVariables.EmpName;
  67. }
  68. catch { }
  69. try
  70. {
  71. _timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  72. _employee = App.Data.Employee;
  73. _jobs = App.Data.Jobs;
  74. deviceName = MobileUtils.GetDeviceID();
  75. firstLoad = false;
  76. }
  77. catch { }
  78. }
  79. private static void InitTimers()
  80. {
  81. Timer t = new Timer(RecentlyAskedToUpdateTimer, null, 600000, 600000); //user is reminded to update when opening screen after timer of 10 minutes
  82. updateCounter = 1; //user is forced to update after 3rd reminder
  83. Timer t1 = new Timer(RecentlyUpdatedTilesTimer, null, 30000, 30000);
  84. //bluetooth data is allowed to upload once every minute, notifications refreshing is piggybacked to this too
  85. }
  86. private static void RecentlyAskedToUpdateTimer(object o)
  87. {
  88. recentlyAskedToUpdate = false;
  89. }
  90. private static void RecentlyUpdatedTilesTimer(object o)
  91. {
  92. bRecentlyUpdatedTiles = false;
  93. App.Data.Refresh(true);
  94. SearchForNewNotifications();
  95. }
  96. public static Assignment CheckCurrentAssignment()
  97. {
  98. Thread.Sleep(5000);
  99. StartAssignmentTimer();
  100. CoreTable table = new Client<Assignment>().Query(
  101. new Filter<Assignment>(x => x.EmployeeLink.ID).IsEqualTo(GlobalVariables.EmpID)
  102. .And(x => x.Date).IsEqualTo(DateTime.Today.Date)
  103. .And(x => x.Actual.Finish).IsEqualTo(null),
  104. null,
  105. new SortOrder<Assignment>(x => x.Actual.Start, SortDirection.Ascending));
  106. if (!table.Rows.Any())
  107. {
  108. Job.OnJobIDChanged += OnJobIDChanged;
  109. return null;
  110. }
  111. else
  112. return table.Rows.LastOrDefault().ToObject<Assignment>();
  113. }
  114. private static void StartAssignmentTimer()
  115. {
  116. Timer t = new Timer(AssignmentTimerCallback, null, 300000, 300000);
  117. }
  118. private static void AssignmentTimerCallback(object state)
  119. {
  120. if (CurrentAssignment != null)
  121. SaveCurrentAssignment("PRS Mobile main screen - Saving assignment on 5 minute timer");
  122. }
  123. public static void SaveCurrentAssignment(string auditnote, bool complete = false)
  124. {
  125. if (!complete)
  126. CurrentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
  127. else
  128. CurrentAssignment.Actual.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
  129. new Client<Assignment>().Save(CurrentAssignment, auditnote);
  130. }
  131. static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d)
  132. {
  133. int f = 0;
  134. double m = (double)(dt.Ticks % d.Ticks) / d.Ticks;
  135. if (m >= 0.5)
  136. f = 1;
  137. return new DateTime(((dt.Ticks / d.Ticks) + f) * d.Ticks);
  138. }
  139. public static void UseCurrentAssignment(Assignment assgn)
  140. {
  141. CurrentAssignment = assgn;
  142. SaveCurrentAssignment("PRS Mobile main screen - saving assignment on re-login to App");
  143. if (CurrentAssignment.JobLink.ID != Guid.Empty)
  144. {
  145. Job.ID = CurrentAssignment.JobLink.ID;
  146. var job = new Client<Job>().Query(new Filter<Job>(x => x.ID).IsEqualTo(Job.ID)).Rows.FirstOrDefault().ToObject<Job>();
  147. Job.JobNumber = job.JobNumber;
  148. Job.Name = job.Name;
  149. Job.OnJobIDChanged += OnJobIDChanged;
  150. }
  151. }
  152. public static void OnJobIDChanged(Guid jobid)
  153. {
  154. if (CurrentAssignment == null)
  155. CreateNewAssignment(jobid);
  156. else
  157. {
  158. SaveCurrentAssignment("PRS Mobile main screen - saving assignment on job change", true);
  159. CreateNewAssignment(jobid);
  160. }
  161. }
  162. private static void CreateNewAssignment(Guid jobid)
  163. {
  164. CurrentAssignment = new Assignment { Date = DateTime.Now.Date };
  165. CurrentAssignment.EmployeeLink.ID = GlobalVariables.EmpID;
  166. CurrentAssignment.JobLink.ID = jobid;
  167. CurrentAssignment.Actual.Start = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay;
  168. CurrentAssignment.Booked.Start = CurrentAssignment.Actual.Start;
  169. CurrentAssignment.Booked.Finish = RoundToNearestInterval(DateTime.Now, new TimeSpan(0, 5, 0)).TimeOfDay.Add(new TimeSpan(0, 5, 0));
  170. var job = new Client<Job>().Query(new Filter<Job>(x => x.ID).IsEqualTo(jobid)).Rows.FirstOrDefault().ToObject<Job>();
  171. CurrentAssignment.Title = "Clocking on to job " + job.Name + " (" + job.JobNumber + ") on PRS Mobile";
  172. new Client<Assignment>().Save(CurrentAssignment, "Changed job on mobile - creating new assignment");
  173. }
  174. #region Notifications
  175. public static Page DetermineCorrectPage(Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e)
  176. {
  177. string data = e.Request.ReturningData;
  178. int index = data.IndexOf("$");
  179. Guid ID = Guid.Parse(data.Remove(index));
  180. string type = data.Substring(index + 1);
  181. if (type == "Comal.Classes.Kanban")
  182. return new AddEditTask(ID);
  183. else if (type == "Comal.Classes.Delivery")
  184. return new DeliveryDetails(ID);
  185. else
  186. return null;
  187. }
  188. public static async void SearchForNewNotifications()
  189. {
  190. //notifications poll reliably in the background for Anroid, unreliable for iOS due to difficulty with cross-platform plugins for notifications
  191. try
  192. {
  193. await Task.Run(() =>
  194. {
  195. if (ClientFactory.UserGuid != Guid.Empty)
  196. {
  197. CoreTable table = new Client<Notification>().Query
  198. (new Filter<Notification>(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid).And(X => X.Closed).IsEqualTo(DateTime.MinValue),
  199. new Columns<Notification>(
  200. x => x.ID, //0
  201. x => x.Sender.Name, //1
  202. x => x.Title, //2
  203. x => x.Created, //3
  204. x => x.Description, //4
  205. x => x.EntityType, //5
  206. x => x.EntityID //6
  207. )
  208. );
  209. if (NumberOfNotifications == table.Rows.Count()) //no new notifications or none present at all
  210. return;
  211. else //new notifications or previous notifications have now been dismissed
  212. {
  213. NumberOfNotifications = table.Rows.Count();
  214. OnMainPageNotificationsChanged?.Invoke();
  215. CheckNotificationsPushed(table);
  216. }
  217. }
  218. });
  219. }
  220. catch { }
  221. }
  222. private static void CheckNotificationsPushed(CoreTable table)
  223. {
  224. try
  225. {
  226. if (!Application.Current.Properties.ContainsKey("LastPushedNotifications"))
  227. {
  228. Application.Current.Properties.Add("LastPushedNotifications", DateTime.Now);
  229. }
  230. DateTime lastPushed = DateTime.Parse(Application.Current.Properties["LastPushedNotifications"].ToString());
  231. List<NotificationShell> toNotify = new List<NotificationShell>();
  232. foreach (CoreRow row in table.Rows)
  233. {
  234. List<object> list = row.Values;
  235. DateTime created = DateTime.Parse(list[3].ToString());
  236. if (created > new DateTime(2022, 8, 22)) // prevent spam from buildup of old notifications before this is released
  237. {
  238. if (created > lastPushed)
  239. {
  240. if (list[1] == null) list[1] = "";
  241. if (list[2] == null) list[2] = "";
  242. if (list[3] == null) list[3] = DateTime.MinValue;
  243. if (list[4] == null) list[4] = "";
  244. if (list[5] == null) list[5] = "";
  245. if (list[6] == null) list[6] = Guid.Empty;
  246. NotificationShell shell = new NotificationShell
  247. {
  248. ID = Guid.Parse(list[0].ToString()),
  249. Sender = list[1].ToString(),
  250. Title = list[2].ToString(),
  251. Created = DateTime.Parse(list[3].ToString()),
  252. EntityType = list[5].ToString(),
  253. EntityID = Guid.Parse(list[6].ToString())
  254. };
  255. toNotify.Add(shell); //add notification to be pushed
  256. }
  257. }
  258. }
  259. if (toNotify.Count > 0)
  260. PushNotificationsAsync(toNotify);
  261. }
  262. catch { }
  263. }
  264. private static async Task PushNotificationsAsync(List<NotificationShell> shells)
  265. {
  266. try
  267. {
  268. int count = 1;
  269. foreach (NotificationShell shell in shells)
  270. {
  271. var notification = new NotificationRequest
  272. {
  273. BadgeNumber = 1,
  274. Description = shell.Title,
  275. Title = "New PRS Notification: ",
  276. ReturningData = shell.EntityID.ToString() + "$" + shell.EntityType,
  277. NotificationId = count,
  278. };
  279. count++;
  280. NotificationImage img = new NotificationImage();
  281. img.ResourceName = "icon16.png";
  282. notification.Image = img;
  283. await LocalNotificationCenter.Current.Show(notification);
  284. }
  285. Application.Current.Properties["LastPushedNotifications"] = DateTime.Now;
  286. }
  287. catch { }
  288. }
  289. #endregion
  290. private static void LocationFound(LocationServices sender)
  291. {
  292. //if (bSharedDevice)
  293. // return;
  294. if (App.Bluetooth.RecentlyScanned)
  295. UploadTiles();
  296. try
  297. {
  298. TimeSheet timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  299. if (timesheet != null)
  300. {
  301. if (timesheet.StartLocation.Latitude.Equals(0.0F) && timesheet.StartLocation.Longitude.Equals(0.0F))
  302. {
  303. timesheet.StartLocation.Latitude = sender.Latitude;
  304. timesheet.StartLocation.Longitude = sender.Longitude;
  305. timesheet.StartLocation.Timestamp = sender.TimeStamp;
  306. timesheet.Address = sender.Address;
  307. new Client<TimeSheet>().Save(timesheet, "Updating Timesheet with GPS Coordinates", (o, e) => { });
  308. }
  309. }
  310. if (!string.IsNullOrWhiteSpace(matchedDeviceName))
  311. {
  312. InABox.Core.Location curlocation = new InABox.Core.Location() { Latitude = App.GPS.Latitude, Longitude = App.GPS.Longitude };
  313. curlocation.Timestamp = DateTime.Now;
  314. GPSTrackerLocation gpsTrackerLocation = new GPSTrackerLocation();
  315. gpsTrackerLocation.DeviceID = matchedDeviceName;
  316. gpsTrackerLocation.Location.Timestamp = curlocation.Timestamp;
  317. gpsTrackerLocation.Location = curlocation;
  318. new Client<GPSTrackerLocation>().Save(gpsTrackerLocation, "Updated company device location from Timebench");
  319. }
  320. OnRefreshScreen?.Invoke();
  321. }
  322. catch { }
  323. }
  324. private static async void UploadTiles()
  325. {
  326. try
  327. {
  328. if (App.GPS.Latitude.Equals(0.0F) && App.GPS.Longitude.Equals(0.0F))
  329. return;
  330. if (App.Bluetooth.DetectedBlueToothMACAddresses.Count == 0)
  331. return;
  332. if (bRecentlyUpdatedTiles)
  333. return;
  334. bRecentlyUpdatedTiles = true;
  335. await Task.Run(() =>
  336. {
  337. InABox.Core.Location curlocation = new InABox.Core.Location() { Latitude = App.GPS.Latitude, Longitude = App.GPS.Longitude };
  338. curlocation.Timestamp = DateTime.Now;
  339. List<GPSTrackerLocation> trackersToUpdate = new List<GPSTrackerLocation>();
  340. foreach (String id in App.Bluetooth.DetectedBlueToothMACAddresses)
  341. {
  342. GPSTracker tracker = GlobalVariables.GPSTrackerCache.Find(x => x.DeviceID.Equals(id));
  343. bool stale = tracker.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 5, 0));
  344. bool moved = tracker.Location.DistanceTo(curlocation, UnitOfLength.Kilometers) > 0.1;
  345. if (stale || moved)
  346. {
  347. GlobalVariables.GPSTrackerCache.Remove(tracker);
  348. tracker.Location = curlocation;
  349. GlobalVariables.GPSTrackerCache.Add(tracker);
  350. //cache is updated
  351. GPSTrackerLocation gpsTrackerLocation = new GPSTrackerLocation();
  352. gpsTrackerLocation.DeviceID = tracker.DeviceID;
  353. gpsTrackerLocation.Location.Timestamp = tracker.Location.Timestamp;
  354. gpsTrackerLocation.Location = curlocation;
  355. trackersToUpdate.Add(gpsTrackerLocation);
  356. }
  357. }
  358. if (trackersToUpdate.Any())
  359. {
  360. if (ClientFactory.UserGuid != Guid.Empty)
  361. new Client<GPSTrackerLocation>().Save(trackersToUpdate, "Updating Bluetooth Device Locations");
  362. }
  363. App.Bluetooth.DetectedBlueToothMACAddresses.Clear();
  364. }
  365. );
  366. }
  367. catch (Exception e)
  368. {
  369. }
  370. //if ((master != null) && (master.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0))))
  371. //{
  372. // GPSTrackerLocation device = new GPSTrackerLocation();
  373. // device.DeviceID = MobileUtils.GetDeviceID();
  374. // device.Location.Latitude = App.GPS.Latitude;
  375. // device.Location.Longitude = App.GPS.Longitude;
  376. // device.Location.Timestamp = DateTime.Now;
  377. // locations.Add(device);
  378. // //device.BatteryLevel = ((double)CrossBattery.Current.RemainingChargePercent);
  379. // //new Client<GPSTrackerLocation>().Save(device, "Updating Device Location"); //, SaveTrackerCallback);
  380. //}
  381. #region OLD
  382. //for (int i = 0; i < App.Bluetooth.Devices.Length; i++)
  383. //{
  384. // String id = App.Bluetooth.Devices[i];
  385. // int level = App.Bluetooth.BatteryLevels[i];
  386. // var btmaster = trackers.FirstOrDefault(x => x.DeviceID.Equals(id));
  387. // if ((btmaster != null) && (!locations.Any(x => x.DeviceID.Equals(btmaster.DeviceID))))
  388. // {
  389. // bool stale = btmaster.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0));
  390. // bool moved = btmaster.Location.DistanceTo(curlocation, UnitOfLength.Kilometers) > 0.1;
  391. // if (stale || moved)
  392. // {
  393. // GPSTrackerLocation location = new GPSTrackerLocation();
  394. // location.DeviceID = id;
  395. // location.Location.Latitude = App.GPS.Latitude;
  396. // location.Location.Longitude = App.GPS.Longitude;
  397. // location.Location.Timestamp = DateTime.Now;
  398. // location.BatteryLevel = level;
  399. // locations.Add(location);
  400. // }
  401. // }
  402. // //new Client<GPSTrackerLocation>().Save(location, "Found Kontakt Device"); //, SaveTrackerCallback);
  403. //}
  404. //if (locations.Any())
  405. // new Client<GPSTrackerLocation>().Save(locations, "Updating Bluetooth Device Locations", (o, e) => { });
  406. #endregion
  407. }
  408. private static void LocationError(LocationServices sebder, Exception error)
  409. {
  410. }
  411. private static void ScanFinished(Bluetooth sender)
  412. {
  413. try
  414. {
  415. OnRefreshScreen?.Invoke();
  416. if (App.GPS.RecentlyLocated)
  417. UploadTiles();
  418. }
  419. catch { }
  420. }
  421. }
  422. }