MainPage.xaml.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867
  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.Clients;
  9. using InABox.Mobile;
  10. using Comal.Classes;
  11. using InABox.Configuration;
  12. using XF.Material.Forms.UI.Dialogs;
  13. namespace PRS.Mobile
  14. {
  15. public partial class MainPage : BasePage
  16. {
  17. #region Fields
  18. bool bUpdatingTimesheet = false;
  19. bool midnightTimerOn = false;
  20. DateTime oneSecondBeforeMidnight = DateTime.Today.AddSeconds(864399);
  21. bool clockedOffInLast5Seconds = false;
  22. bool bRecentlyUpdatedTiles = false;
  23. string matchedDeviceName = "";
  24. int notCount = 1;
  25. private TimeSheet _currenttimesheet = null;
  26. #endregion
  27. #region Constructor
  28. public MainPage() : base()
  29. {
  30. MobileLogging.Log("MainPage.Create");
  31. InitializeComponent();
  32. MobileLogging.Log("MainPage.InitNotificationCentre");
  33. InitNotificationCentre();
  34. MobileLogging.Log("MainPage.Subscribe");
  35. MessagingCenter.Subscribe<App>(this, App.MessageOnResume,
  36. (o) =>
  37. {
  38. if (!App.GPS.RecentlyLocated)
  39. App.GPS.GetLocation();
  40. RefreshScreen();
  41. }
  42. );
  43. MobileLogging.Log("MainPage.NotifyChanges");
  44. NotifyChanges();
  45. MobileLogging.Log("MainPage.Create Done");
  46. }
  47. private void NotifyChanges()
  48. {
  49. Task.Run(() =>
  50. {
  51. string changes = NotifyMobileChanges.Notifiy();
  52. if (!string.IsNullOrWhiteSpace(changes))
  53. Device.BeginInvokeOnMainThread(() =>
  54. {
  55. DisplayAlert("Latest changes", changes, "OK");
  56. });
  57. });
  58. }
  59. #endregion
  60. #region OnAppearing and Display
  61. private SelectionPage _selectionpage = null;
  62. protected override void OnAppearing()
  63. {
  64. MobileLogging.Log("MainPage.Appearing");
  65. if (_selectionpage == null)
  66. {
  67. BackButtonEnabled = App.IsSharedDevice;
  68. EnableModules();
  69. StartMonitoringNotifications();
  70. StartPromptingForUpdates();
  71. App.Data.GPSLocationUpdated += OnGPSLocationUpdated;
  72. App.Data.BluetoothScanFinished += OnBluetothScanFinished;
  73. //LoadHRToDos(); to be uncommented when ready for roll out
  74. CheckTimeSheet();
  75. }
  76. _selectionpage = null;
  77. MobileLogging.Log("MainPage.refreshScreen");
  78. RefreshScreen();
  79. MobileLogging.Log("MainPage.Base.OnAppearing");
  80. base.OnAppearing();
  81. MobileLogging.Log("MainPage.OnAppearing Done");
  82. }
  83. private IMaterialModalPage _snackbar = null;
  84. protected override void OnDisappearing()
  85. {
  86. base.OnDisappearing();
  87. //App.Data.GPSLocationUpdated -= OnGPSLocationUpdated;
  88. //App.Data.BluetoothScanFinished -= OnBluetothScanFinished;
  89. _canceltasks.Cancel();
  90. }
  91. private void OnBluetothScanFinished(BluetoothEventArgs args)
  92. {
  93. Dispatcher.BeginInvokeOnMainThread(RefreshScreen);
  94. }
  95. private void OnGPSLocationUpdated(GPSEventArgs args)
  96. {
  97. Dispatcher.BeginInvokeOnMainThread(RefreshScreen);
  98. }
  99. private void EnableModules()
  100. {
  101. void SetupClockOnButton(bool disable)
  102. {
  103. LogoRow.Height = disable ? 150 : 0;
  104. ClockOnRow.Height = disable ? 0 : new GridLength(5.5, GridUnitType.Star);
  105. JobRow.Height = disable ? 0 : 55;
  106. TaskRow.Height = disable ? 0 : 55;
  107. }
  108. SetupClockOnButton(InABox.Core.Security.IsAllowed<IsJobOnlyEmployee>());
  109. Assignments.IsVisible = InABox.Core.Security.CanView<Assignment>();
  110. Deliveries.IsVisible = InABox.Core.Security.CanView<Delivery>();
  111. Forms.IsVisible = InABox.Core.Security.CanView<DigitalForm>();
  112. Equipment.IsVisible = InABox.Core.Security.CanView<Equipment>();
  113. InOut.IsVisible = InABox.Core.Security.IsAllowed<CanViewInOutBoard>();
  114. Manufacturing.IsVisible = InABox.Core.Security.CanView<ManufacturingPacket>();
  115. Products.IsVisible = InABox.Core.Security.CanView<Product>();
  116. PurchaseOrders.IsVisible = InABox.Core.Security.CanView<PurchaseOrder>();
  117. StoreRequis.IsVisible = InABox.Core.Security.CanView<Requisition>();
  118. MyTasks.IsVisible = InABox.Core.Security.CanView<Kanban>();
  119. Warehousing.IsVisible = InABox.Core.Security.CanView<StockWarehouse>();
  120. }
  121. protected override void UpdateTransportStatus()
  122. {
  123. base.UpdateTransportStatus();
  124. RefreshScreen();
  125. }
  126. private CancellationTokenSource _canceltasks = new CancellationTokenSource();
  127. private int _notificationcount = 0;
  128. private void StartMonitoringNotifications()
  129. {
  130. //notifications are allowed to upload once every 30 seconds
  131. // This will eventually be replaced with websocket pushes
  132. // but we're not ready for that yet :-(
  133. var token = _canceltasks.Token;
  134. Task.Run(
  135. () =>
  136. {
  137. while (!_canceltasks.Token.IsCancellationRequested)
  138. {
  139. App.Data.Notifications.Refresh(true);
  140. if (App.Data.Notifications.Items.Count != _notificationcount)
  141. {
  142. Dispatcher.BeginInvokeOnMainThread(() =>
  143. {
  144. _notificationcount = App.Data.Notifications.Items.Count;
  145. Notifications.Indicator = _notificationcount.ToString();
  146. });
  147. }
  148. Task.Delay(TimeSpan.FromSeconds(30), token)
  149. .Wait(token);
  150. }
  151. },
  152. token
  153. );
  154. }
  155. private void StartPromptingForUpdates()
  156. {
  157. var token = _canceltasks.Token;
  158. Task.Run(
  159. () =>
  160. {
  161. while (!token.IsCancellationRequested)
  162. {
  163. bool isLatest = MobileUtils.AppVersion.IsUsingLatestVersion().Result;
  164. if (!isLatest)
  165. {
  166. string latestVersionNumber = MobileUtils.AppVersion.GetLatestVersionNumber().Result;
  167. string chosenOption = DisplayActionSheet(
  168. $"Version {latestVersionNumber} Available. Update now?",
  169. "You will be reminded again in 10 minutes.", null, "Yes", "No").Result;
  170. if (String.Equals(chosenOption,"Yes"))
  171. Dispatcher.BeginInvokeOnMainThread(() => { MobileUtils.AppVersion.OpenAppInStore(); });
  172. }
  173. Task.Delay(TimeSpan.FromMinutes(10), token)
  174. .Wait(token);
  175. }
  176. }
  177. , token
  178. );
  179. }
  180. private void RefreshScreen()
  181. {
  182. bool GateReady = CheckLocation();
  183. var location = GetAddress();
  184. Title = App.Data.Me != null ? App.Data.Me.Name : "(Not Logged In)";
  185. ClockOnButton.IsEnabled = App.Data.IsConnected() && GateReady;
  186. ClockOnOffLabel.Text = GateReady ? _currenttimesheet == null ? "CLOCK ON" : "CLOCK OFF" : "PLEASE WAIT";
  187. CurrentLocation.Text = location.ToUpper().Contains("ERROR")
  188. ? "Unknown Address"
  189. : location;
  190. ClockOnButton.BackgroundColor = App.Data.IsConnected() && GateReady
  191. ? _currenttimesheet == null
  192. ? Color.FromHex("#e6e6fa")
  193. : Color.FromHex("#15C7C1")
  194. : Color.Silver;
  195. ClockOnButton.BorderColor = App.Data.IsConnected() && GateReady
  196. ? _currenttimesheet == null
  197. ? Color.FromHex("#96969a")
  198. : Color.FromHex("#059791")
  199. : Color.Gray;
  200. addNoteBtn.IsEnabled = App.Data.IsConnected() && _currenttimesheet != null;
  201. jobBtn.IsEnabled = App.Data.IsConnected() && _currenttimesheet != null;
  202. taskBtn.IsEnabled = App.Data.IsConnected() && _currenttimesheet != null;
  203. jobBtn.Text = ((_currenttimesheet?.JobID ?? Guid.Empty) != Guid.Empty)
  204. ? $"{_currenttimesheet.JobLink.JobNumber}: {_currenttimesheet.JobLink.Name}"
  205. : "No Job Selected";
  206. Assignments.IsEnabled = App.Data.IsConnected();
  207. Deliveries.IsEnabled = App.Data.IsConnected();
  208. Forms.IsEnabled = App.Data.IsConnected();
  209. //Equipment.IsEnabled = App.Data.IsConnected();
  210. InOut.IsEnabled = App.Data.IsConnected();
  211. Manufacturing.IsEnabled = App.Data.IsConnected();
  212. MyHR.IsEnabled = App.Data.IsConnected();
  213. Notifications.IsEnabled = App.Data.IsConnected();
  214. Products.IsEnabled = App.Data.IsConnected();
  215. PurchaseOrders.IsEnabled = App.Data.IsConnected();
  216. Site.IsEnabled = App.Data.IsConnected();
  217. StoreRequis.IsEnabled = App.Data.IsConnected();
  218. MyTasks.IsEnabled = App.Data.IsConnected();
  219. Warehousing.IsEnabled = App.Data.IsConnected();
  220. }
  221. #endregion
  222. #region Clock on/off
  223. private void CheckTimeSheet()
  224. {
  225. if (App.Data.IsConnected())
  226. {
  227. var client = new Client<TimeSheet>();
  228. var filter = new Filter<TimeSheet>(x => x.EmployeeLink.ID).IsEqualTo(App.Data.Me.ID)
  229. .And(x => x.Date).IsEqualTo(DateTime.Today)
  230. .And(x => x.Finish).IsEqualTo(TimeSpan.Zero);
  231. var table = client.Query(filter);
  232. _currenttimesheet = table.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  233. }
  234. var token = _canceltasks.Token;
  235. Task.Run(
  236. () =>
  237. {
  238. while (!token.IsCancellationRequested)
  239. {
  240. if (_currenttimesheet != null)
  241. {
  242. if (_currenttimesheet.Date != DateTime.Today)
  243. {
  244. InABox.Core.Location here = new InABox.Core.Location()
  245. {
  246. Latitude = App.GPS.Latitude,
  247. Longitude = App.GPS.Longitude,
  248. Timestamp = DateTime.Now
  249. };
  250. _currenttimesheet.FinishLocation = here;
  251. _currenttimesheet.Finish = TimeSpan.FromDays(1).Subtract(TimeSpan.FromSeconds(1));
  252. new Client<TimeSheet>().Save(_currenttimesheet,
  253. "Closing Timesheet at Modnight on Mobile Device");
  254. CreateTimeSheet(_currenttimesheet.JobLink.ID, _currenttimesheet.JobLink.JobNumber, _currenttimesheet.JobLink.Name, here, here.Address, "Creating Timeheet at Midnight on Mobile Device");
  255. }
  256. }
  257. Task.Delay(TimeSpan.FromMinutes(1), token).Wait(token);
  258. }
  259. },
  260. token
  261. );
  262. }
  263. private DateTime _debounce = DateTime.MinValue;
  264. async void ClockOnOff_Clicked(object sender, System.EventArgs e)
  265. {
  266. if (_debounce > DateTime.Now.Subtract(TimeSpan.FromMilliseconds(500)))
  267. return;
  268. _debounce = DateTime.MaxValue;
  269. string chosenOption = _currenttimesheet != null
  270. ? await DisplayActionSheet("Clock off?", "Cancel", null, "Continue", "Cancel")
  271. : "Continue";
  272. if (!String.Equals(chosenOption, "Continue"))
  273. {
  274. _debounce = DateTime.Now;
  275. return;
  276. }
  277. try
  278. {
  279. using (await MaterialDialog.Instance.LoadingDialogAsync(message: $"Clocking {(_currenttimesheet == null ? "On" : "Off")}"))
  280. {
  281. InABox.Core.Location here = new InABox.Core.Location()
  282. {
  283. Latitude = App.GPS.Latitude,
  284. Longitude = App.GPS.Longitude,
  285. Timestamp = DateTime.Now
  286. };
  287. if (_currenttimesheet != null)
  288. {
  289. FinishTimeSheet(here);
  290. }
  291. else
  292. {
  293. if (!InABox.Core.Security.IsAllowed<CanBypassBluetoothGates>())
  294. {
  295. var tracker = App.Data.BluetoothGates.FirstOrDefault(x => App.Bluetooth.Devices.Contains(x.DeviceID));
  296. if (tracker != null)
  297. CreateTimeSheet(tracker.JobID, tracker.JobNumber, tracker.JobName, here, App.GPS.Address, "Clocking On");
  298. }
  299. else if ((!App.GPS.Latitude.Equals(0.0F)) && (!App.GPS.Longitude.Equals(0.0F))
  300. || InABox.Core.Security.IsAllowed<CanBypassGPSClockIn>())
  301. {
  302. var job = await ChooseNearbyJob(here);
  303. CreateTimeSheet(job?.ID ?? Guid.Empty, job?.JobNumber ?? "", job?.Name ?? "" , here, App.GPS.Address, "Clocking On");
  304. }
  305. }
  306. Dispatcher.BeginInvokeOnMainThread(RefreshScreen);
  307. }
  308. }
  309. catch (Exception ex)
  310. {
  311. InABox.Mobile.MobileLogging.Log(ex);
  312. }
  313. _debounce = DateTime.Now;
  314. }
  315. private void FinishTimeSheet(InABox.Core.Location here)
  316. {
  317. if ((_currenttimesheet == null) || (_currenttimesheet.ID == Guid.Empty))
  318. {
  319. _currenttimesheet = null;
  320. return;
  321. }
  322. try
  323. {
  324. if (ZeroLengthTimesheet())
  325. new Client<TimeSheet>().Delete(_currenttimesheet, "Deleted due to zero duration timesheet");
  326. else
  327. {
  328. _currenttimesheet.Finish =
  329. new TimeSpan(DateTime.Now.TimeOfDay.Hours, DateTime.Now.TimeOfDay.Minutes, 0);
  330. _currenttimesheet.FinishLocation = here;
  331. bUpdatingTimesheet = true;
  332. new Client<TimeSheet>().Save(_currenttimesheet, "Clocking Off");
  333. }
  334. _currenttimesheet = null;
  335. }
  336. catch (Exception ex)
  337. {
  338. InABox.Mobile.MobileLogging.Log(ex);
  339. }
  340. }
  341. #endregion
  342. #region Utilities
  343. private void InitNotificationCentre()
  344. {
  345. // LocalNotificationCenter.Current.NotificationActionTapped += (Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e) =>
  346. // {
  347. // if (MainPageUtils.DetermineCorrectPage(e) != null)
  348. // {
  349. // Device.BeginInvokeOnMainThread(() =>
  350. // {
  351. // Navigation.PushAsync(MainPageUtils.DetermineCorrectPage(e));
  352. // });
  353. // }
  354. // };
  355. //
  356. // MainPageUtils.OnMainPageNotificationsChanged += RefreshOnNotificationsChange;
  357. }
  358. private void CheckNotificationsPushed(CoreTable table)
  359. {
  360. // try
  361. // {
  362. // if (!Application.Current.Properties.ContainsKey("LastPushedNotifications"))
  363. // {
  364. // Application.Current.Properties.Add("LastPushedNotifications", DateTime.Now);
  365. // }
  366. // DateTime lastPushed = DateTime.Parse(Application.Current.Properties["LastPushedNotifications"].ToString());
  367. // List<NotificationShell> toNotify = new List<NotificationShell>();
  368. // foreach (CoreRow row in table.Rows)
  369. // {
  370. // List<object> list = row.Values;
  371. // DateTime created = DateTime.Parse(list[3].ToString());
  372. // if (created > new DateTime(2022, 8, 22)) // prevent spam from buildup of old notifications before this is released
  373. // {
  374. // if (created > lastPushed)
  375. // {
  376. // if (list[1] == null) list[1] = "";
  377. // if (list[2] == null) list[2] = "";
  378. // if (list[3] == null) list[3] = DateTime.MinValue;
  379. // if (list[4] == null) list[4] = "";
  380. // if (list[5] == null) list[5] = "";
  381. // if (list[6] == null) list[6] = Guid.Empty;
  382. //
  383. // NotificationShell shell = new NotificationShell
  384. // {
  385. // ID = Guid.Parse(list[0].ToString()),
  386. // Sender = list[1].ToString(),
  387. // Title = list[2].ToString(),
  388. // Created = DateTime.Parse(list[3].ToString()),
  389. // EntityType = list[5].ToString(),
  390. // EntityID = Guid.Parse(list[6].ToString())
  391. // };
  392. // toNotify.Add(shell); //add notification to be pushed
  393. // }
  394. // }
  395. // }
  396. // if (toNotify.Count > 0)
  397. // PushNotificationsAsync(toNotify);
  398. // }
  399. // catch { }
  400. }
  401. private async Task PushNotificationsAsync(List<NotificationShell> shells)
  402. {
  403. // try
  404. // {
  405. // int count = 1;
  406. //
  407. // foreach (NotificationShell shell in shells)
  408. // {
  409. // var notification = new NotificationRequest
  410. // {
  411. // BadgeNumber = 1,
  412. // Description = shell.Title,
  413. // Title = "New PRS Notification: ",
  414. // ReturningData = shell.EntityID.ToString() + "$" + shell.EntityType,
  415. // NotificationId = count,
  416. // };
  417. // count++;
  418. // NotificationImage img = new NotificationImage();
  419. // img.ResourceName = "icon16.png";
  420. // notification.Image = img;
  421. //
  422. // await LocalNotificationCenter.Current.Show(notification);
  423. // }
  424. // Application.Current.Properties["LastPushedNotifications"] = DateTime.Now;
  425. // }
  426. // catch { }
  427. }
  428. private async Task<JobShell> ChooseNearbyJob(InABox.Core.Location here)
  429. {
  430. var nearbyjobs = App.Data.Jobs.Where(x => here.DistanceTo(x.Location, UnitOfLength.Kilometers) < 1.0F)
  431. .ToArray();
  432. if (nearbyjobs.Length > 1)
  433. {
  434. Dictionary<String, JobShell> dict = new Dictionary<string, JobShell>();
  435. foreach (var job in nearbyjobs)
  436. dict[job.DisplayName] = job;
  437. string chosenOption = await DisplayActionSheet("Choose job site", "Cancel", null, dict.Keys.ToArray());
  438. if (string.IsNullOrEmpty(chosenOption) || chosenOption.Equals("Cancel"))
  439. return null;
  440. return dict[chosenOption];
  441. }
  442. return nearbyjobs.FirstOrDefault();
  443. }
  444. void AddNote_Tapped(System.Object sender, System.EventArgs e)
  445. {
  446. if (_currenttimesheet == null)
  447. return;
  448. var notepage = new TimeSheetNotePage(_currenttimesheet);
  449. Navigation.PushAsync(notepage);
  450. }
  451. private void TaskBtn_Tapped(object sender, EventArgs e)
  452. {
  453. _selectionpage = new TaskSelectionPage( (task) =>
  454. {
  455. if (_currenttimesheet != null)
  456. {
  457. // Not sure hwat to do here...
  458. }
  459. }
  460. );
  461. Navigation.PushAsync(_selectionpage);
  462. }
  463. // private async void RequestUserInput(Guid taskID)
  464. // {
  465. // const string addtask = "Change current assignment task";
  466. // const string newassignment = "Start a new assignment with this task";
  467. // string chosenOption = await DisplayActionSheet("Choose an option", "Cancel", null, addtask, newassignment);
  468. // switch (chosenOption)
  469. // {
  470. // case addtask:
  471. // MainPageUtils.ChangeAssignmentTask(taskID);
  472. // break;
  473. // case newassignment:
  474. // MainPageUtils.SaveCurrentAssignment("PRS Mobile main screen - saving assignment on task change", true);
  475. // MainPageUtils.CreateNewAssignment(Guid.Empty, taskID);
  476. // break;
  477. // default:
  478. // break;
  479. // }
  480. // }
  481. private void JobBtn_Tapped(object sender, EventArgs e)
  482. {
  483. _selectionpage = new JobSelectionPage(
  484. (job) =>
  485. {
  486. if (_currenttimesheet != null)
  487. {
  488. if (ZeroLengthTimesheet())
  489. {
  490. _currenttimesheet.JobLink.ID = job?.ID ?? Guid.Empty;
  491. _currenttimesheet.JobLink.JobNumber = job?.JobNumber ?? "";
  492. _currenttimesheet.JobLink.Name = job?.Name ?? "";
  493. new Client<TimeSheet>().Save(_currenttimesheet,"Updated Job Number via mobile device");
  494. }
  495. else
  496. {
  497. var here = new Location()
  498. {
  499. Latitude = App.GPS.Latitude,
  500. Longitude = App.GPS.Longitude,
  501. Address = App.GPS.Address,
  502. Timestamp = App.GPS.TimeStamp
  503. };
  504. FinishTimeSheet(here);
  505. CreateTimeSheet(job?.ID ?? Guid.Empty, job?.JobNumber ?? "", job?.Name ?? "", here, here.Address,
  506. "Switched Jobs via Mobile Device");
  507. }
  508. }
  509. }
  510. );
  511. Navigation.PushAsync(_selectionpage);
  512. }
  513. // private bool CheckTimeSheetAgainstGates(TimeSheet timesheet)
  514. // {
  515. // DateTime now = DateTime.Now;
  516. //
  517. // //var timesheet = CurrentTimeSheet();
  518. //
  519. // //Can't confirm if there is no timesheet
  520. // if (timesheet == null)
  521. // return false;
  522. //
  523. // // Can't confirm if there are no devices
  524. // if (App.Bluetooth.Devices.Length == 0)
  525. // return false;
  526. //
  527. // if (App.Data.Gates == null)
  528. // return false;
  529. //
  530. // long tsTicks = timesheet.Date.Add(timesheet.Start).Ticks;
  531. // long btTicks = App.Bluetooth.TimeStamp.Ticks;
  532. //
  533. // if (Math.Abs(tsTicks - btTicks) > new TimeSpan(0, 2, 0).Ticks)
  534. // return false;
  535. //
  536. // CoreRow firstgate = null;
  537. // List<String> gates = new List<string>();
  538. // // Scan every located d
  539. // foreach (var device in App.Bluetooth.Devices)
  540. // {
  541. // CoreRow gate = App.Data.Gates?.Rows.FirstOrDefault(r => r.Get<JobTracker, String>(c => c.TrackerLink.DeviceID) == device);
  542. // if (gate != null)
  543. // {
  544. //
  545. // if ((gate.Get<JobTracker, bool>(x => x.IsJobSite) == true) && (firstgate == null))
  546. // firstgate = gate;
  547. //
  548. // gates.Add(gate.Get<JobTracker, String>(x => x.Gate));
  549. // }
  550. // }
  551. // if (gates.Any())
  552. // {
  553. // timesheet.Gate = String.Join(", ", gates.OrderBy(x => x));
  554. // if (firstgate != null)
  555. // {
  556. // timesheet.JobLink.ID = firstgate.Get<JobTracker, Guid>(x => x.JobLink.ID);
  557. // timesheet.JobLink.JobNumber = firstgate.Get<JobTracker, String>(x => x.JobLink.JobNumber);
  558. // timesheet.JobLink.Name = firstgate.Get<JobTracker, String>(x => x.JobLink.Name);
  559. // }
  560. // return true;
  561. // //new Client<TimeSheet>().Save(timesheet, "Confirmed Gate Entry by Bluetooth Tracker", (o, e) => { });
  562. // }
  563. //
  564. // return false;
  565. //
  566. // }
  567. private bool ZeroLengthTimesheet()
  568. {
  569. if (_currenttimesheet == null)
  570. return true;
  571. if (!String.IsNullOrWhiteSpace(_currenttimesheet.Notes))
  572. return false;
  573. if (_currenttimesheet.Date.Equals(DateTime.Today))
  574. {
  575. var diff = (DateTime.Now.TimeOfDay - _currenttimesheet.Start).TotalSeconds;
  576. if (Math.Abs(diff) < 120.0F)
  577. return true;
  578. }
  579. return false;
  580. }
  581. private async void CreateTimeSheet(Guid jobid, string jobnumber, String jobname, InABox.Core.Location location, String address, String auditmessage)
  582. {
  583. try
  584. {
  585. _currenttimesheet = new TimeSheet();
  586. _currenttimesheet.EmployeeLink.ID = App.Data.Me.ID;
  587. _currenttimesheet.Date = DateTime.Today;
  588. TimeSpan tod = DateTime.Now - DateTime.Today;
  589. tod = new TimeSpan(tod.Hours, tod.Minutes, 0);
  590. _currenttimesheet.Start = tod;
  591. _currenttimesheet.StartLocation = location;
  592. _currenttimesheet.JobLink.ID = jobid;
  593. _currenttimesheet.JobLink.JobNumber = jobnumber;
  594. _currenttimesheet.JobLink.Name = jobname;
  595. _currenttimesheet.Address = address;
  596. _currenttimesheet.SoftwareVersion = MobileUtils.AppVersion.InstalledVersionNumber + MobileUtils.GetDeviceID();
  597. //if (ClientFactory.IsAllowed<AllowTimeSheetRollover>()) CheckTimeSheetAgainstGates(timesheet);
  598. bUpdatingTimesheet = true;
  599. new Client<TimeSheet>().Save(_currenttimesheet, auditmessage);
  600. }
  601. catch (Exception ex)
  602. {
  603. InABox.Mobile.MobileLogging.Log(ex);
  604. }
  605. }
  606. private bool CheckLocation()
  607. {
  608. // a 15 minute timeout is awfully long for this process.
  609. // The App times tick over every 30 seconds, so surely we can
  610. // drop this to max 1 or 2 minutes..
  611. // Also, we would expect the GPS / Bluetooth subsystems to take care
  612. // of purging stale data
  613. if (InABox.Core.Security.IsAllowed<CanBypassGPSClockIn>())
  614. return true;
  615. if (InABox.Core.Security.IsAllowed<CanBypassBluetoothGates>())
  616. return App.GPS.TimeStamp > DateTime.Now.Subtract(new TimeSpan(0, 15, 0));
  617. if (App.Bluetooth.TimeStamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0)))
  618. return false;
  619. return App.Data.BluetoothGates.Any(x =>
  620. App.Bluetooth.DetectedBlueToothMACAddresses.Contains(x.DeviceID));
  621. }
  622. private String GetAddress()
  623. {
  624. if (InABox.Core.Security.IsAllowed<CanBypassBluetoothGates>())
  625. {
  626. if (App.GPS.TimeStamp < DateTime.Now.Subtract(new TimeSpan(0, 5, 0)))
  627. {
  628. App.GPS.GetLocation(true);
  629. return InABox.Core.Security.IsAllowed<CanBypassGPSClockIn>()? "" : "Searching for GPS";
  630. }
  631. return App.GPS.Address;
  632. }
  633. else
  634. {
  635. var gate = App.Data.BluetoothGates.FirstOrDefault(x =>
  636. App.Bluetooth.DetectedBlueToothMACAddresses.Contains(x.DeviceID));
  637. return gate?.Gate ?? "Looking for Gate";
  638. }
  639. }
  640. private async void LoadHRToDos()
  641. {
  642. try
  643. {
  644. await Task.Run(() =>
  645. {
  646. Thread.Sleep(10000);
  647. if (App.Data.UpdateHRItemsNeedingAttention())
  648. {
  649. string message = "You have HR Items needing attention. Open My HR now?";
  650. Device.BeginInvokeOnMainThread(async () =>
  651. {
  652. string chosenOption = await DisplayActionSheet(message, "Cancel", null, "Yes", "No");
  653. switch (chosenOption)
  654. {
  655. case "Cancel":
  656. break;
  657. case "No":
  658. break;
  659. default:
  660. break;
  661. case "Yes":
  662. MyHRHome myHRHome = new MyHRHome();
  663. Navigation.PushAsync(myHRHome);
  664. break;
  665. }
  666. });
  667. }
  668. });
  669. }
  670. catch { }
  671. }
  672. #endregion
  673. private void Settings_OnTapped(object sender, EventArgs e)
  674. {
  675. Navigation.PushAsync(new SettingsPage());
  676. }
  677. private void Assignments_OnTapped(object sender, EventArgs e)
  678. {
  679. Navigation.PushAsync(new AssignmentList());
  680. }
  681. private void Deliveries_OnTapped(object sender, EventArgs e)
  682. {
  683. Navigation.PushAsync(new DeliveryModule());
  684. }
  685. private void Equipment_OnTapped(object sender, EventArgs e)
  686. {
  687. Navigation.PushAsync(new EquipmentModule());
  688. }
  689. private void Forms_OnTapped(object sender, EventArgs e)
  690. {
  691. Navigation.PushAsync(new KanbanForms());
  692. //Navigation.PushAsync(new DigitalFormsPicker("Kanban");
  693. }
  694. private void InOut_OnTapped(object sender, EventArgs e)
  695. {
  696. Navigation.PushAsync(new StaffStatusPage());
  697. }
  698. private void Manufacturing_OnTapped(object sender, EventArgs e)
  699. {
  700. Navigation.PushAsync(new ManufacturingScreen());
  701. }
  702. private void MyHR_OnTapped(object sender, EventArgs e)
  703. {
  704. Navigation.PushAsync(new MyHRHome());
  705. }
  706. private void Notifications_OnTapped(object sender, EventArgs e)
  707. {
  708. Navigation.PushAsync(new NotificationList());
  709. }
  710. private void Products_OnTapped(object sender, EventArgs e)
  711. {
  712. Navigation.PushAsync(new ProductListTwo());
  713. }
  714. private void PurchaseOrders_OnTapped(object sender, EventArgs e)
  715. {
  716. Navigation.PushAsync(new PurchaseOrderModule());
  717. }
  718. private void Site_OnTapped(object sender, EventArgs e)
  719. {
  720. Guid jobid =
  721. _currenttimesheet?.JobLink.ID
  722. ?? new LocalConfiguration<SiteModuleSettings>().Load().JobID;
  723. Navigation.PushAsync(
  724. new SiteModule()
  725. {
  726. JobID = jobid
  727. }
  728. );
  729. }
  730. private void StoreRequis_OnTapped(object sender, EventArgs e)
  731. {
  732. Navigation.PushAsync(new StoreRequiList());
  733. }
  734. private void MyTasks_OnTapped(object sender, EventArgs e)
  735. {
  736. var tasks = new KanbanList()
  737. {
  738. Model = App.Data.Kanbans,
  739. Title = "My Tasks"
  740. };
  741. Navigation.PushAsync(tasks);
  742. }
  743. private void Warehousing_OnTapped(object sender, EventArgs e)
  744. {
  745. Navigation.PushAsync(new Warehousing2());
  746. }
  747. }
  748. }