MainPage.xaml.cs 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Xamarin.Forms;
  8. using Xamarin.Forms.Maps;
  9. using InABox.Core;
  10. using InABox.Configuration;
  11. using InABox.Clients;
  12. using InABox.Mobile;
  13. using Comal.Classes;
  14. using XF.Material.Forms.UI;
  15. using Xamarin.Essentials;
  16. using XF.Material.Forms.UI.Dialogs;
  17. using comal.timesheets.QAForms;
  18. using comal.timesheets.LiveMaps;
  19. using comal.timesheets.CustomControls;
  20. using comal.timesheets.SiteITPModule;
  21. using comal.timesheets.StoreRequis;
  22. using PRSSecurity = InABox.Core.Security;
  23. using Plugin.LocalNotification;
  24. using comal.timesheets.Tasks;
  25. namespace comal.timesheets
  26. {
  27. public partial class MainPage : ContentPage
  28. {
  29. #region Fields
  30. List<ToolEntry> toolEntries = new List<ToolEntry>();
  31. private TimeSheet _timesheet = null;
  32. private Employee _employee = null;
  33. private CoreTable _jobs = null;
  34. public bool SettingsChanged { get; private set; }
  35. bool bUpdatingTimesheet = false;
  36. bool firstLoad = true;
  37. bool recentlyAskedToUpdate = true;
  38. int updateCounter;
  39. public static ConnectionSettings connectionSettings = null;
  40. bool midnightTimerOn = false;
  41. DateTime oneSecondBeforeMidnight = DateTime.Today.AddSeconds(864399);
  42. bool clockedOffInLast5Seconds = false;
  43. bool bRecentlyUpdatedTiles = false;
  44. bool bSharedDeviceFirstLoad = true;
  45. bool bSharedDevice = false;
  46. int NumberOfNotfications = 0;
  47. private Job _job = new Job();
  48. string deviceName = "";
  49. string matchedDeviceName = "";
  50. int notCount = 1;
  51. #endregion
  52. #region Constructor
  53. public MainPage()
  54. {
  55. InitializeComponent();
  56. try
  57. {
  58. App.GPS.OnLocationFound += LocationFound;
  59. App.GPS.OnLocationError += LocationError;
  60. App.Bluetooth.OnScanFinished += ScanFinished;
  61. App.Data.DataChanged += DataChanged;
  62. App.Data.DataRefreshed += DataRefreshed;
  63. GlobalVariables.EmpID = GlobalVariables.GetEmployeeID();
  64. GlobalVariables.EmpName = GlobalVariables.GetEmployeeName();
  65. MessagingCenter.Subscribe<App>(this, App.MessageOnResume,
  66. (o) =>
  67. {
  68. if (!App.GPS.RecentlyLocated)
  69. App.GPS.GetLocation();
  70. RefreshScreen();
  71. }
  72. );
  73. _timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  74. _employee = App.Data.Employee;
  75. _jobs = App.Data.Jobs;
  76. deviceName = MobileUtils.GetDeviceID();
  77. LoadCacheLists();
  78. InitToolEntryList();
  79. Timer t = new Timer(RecentlyAskedToUpdateTimer, null, 600000, 600000); //user is reminded to update when opening screen after timer of 10 minutes
  80. updateCounter = 1; //user is forced to update after 3rd reminder
  81. Timer t1 = new Timer(RecentlyUpdatedTilesTimer, null, 30000, 30000);
  82. //bluetooth data is allowed to upload once every minute, notifications refreshing is piggybacked to this too
  83. InitNotificationCentre();
  84. firstLoad = false;
  85. //if (GlobalVariables.EmpID == Guid.Parse("40f6ccd9-5272-4b1a-99bf-de7542205aac"))
  86. // RunCustomScript();
  87. NotifyChanges();
  88. }
  89. catch (Exception e)
  90. {
  91. }
  92. NavigationPage.SetHasBackButton(this, false);
  93. }
  94. private void RunCustomScript()
  95. {
  96. var form = new Client<KanbanForm>().Query(new Filter<KanbanForm>(x => x.FormCompletedBy.ID).IsEqualTo(ClientFactory.UserGuid).
  97. And(x => x.Form.Code).IsEqualTo("TEST 1")).Rows.First().ToObject<KanbanForm>();
  98. form.FormCompleted = DateTime.Now;
  99. new Client<KanbanForm>().Save(form, "Test by nick");
  100. }
  101. private void NotifyChanges()
  102. {
  103. string latestChanges = "";
  104. List<string> changes = new List<string>
  105. {
  106. "- Bug fixes stock control functions",
  107. "- Improvements to Digital Form expressions",
  108. };
  109. foreach (string s in changes)
  110. {
  111. latestChanges = s + System.Environment.NewLine + latestChanges;
  112. }
  113. Task.Run(() =>
  114. {
  115. if (App.Current.Properties.Count > 0)
  116. {
  117. foreach (string s in App.Current.Properties.Keys)
  118. {
  119. if (s.Contains("NotifiedOfChanges"))
  120. {
  121. if (!s.Equals("NotifiedOfChanges" + MobileUtils.AppVersion.InstalledVersionNumber))
  122. {
  123. App.Current.Properties.Remove(s);
  124. }
  125. }
  126. }
  127. }
  128. if (!App.Current.Properties.ContainsKey("NotifiedOfChanges" + MobileUtils.AppVersion.InstalledVersionNumber))
  129. {
  130. App.Current.Properties.Add("NotifiedOfChanges" + MobileUtils.AppVersion.InstalledVersionNumber, "True");
  131. Thread.Sleep(7500);
  132. Device.BeginInvokeOnMainThread(() =>
  133. {
  134. DisplayAlert("Latest Changes", latestChanges
  135. , "OK");
  136. });
  137. }
  138. });
  139. }
  140. #endregion
  141. #region OnAppearing and Display
  142. protected override void OnAppearing()
  143. {
  144. if (!App.IsUserLoggedIn)
  145. {
  146. Navigation.PopAsync();
  147. return;
  148. }
  149. //if (Application.Current.Properties.ContainsKey("IsSharedDevice"))
  150. //{
  151. // if (Application.Current.Properties["IsSharedDevice"].Equals("True"))
  152. // {
  153. // bSharedDevice = true;
  154. // if (!bSharedDeviceFirstLoad)
  155. // {
  156. // App.LogoutUser();
  157. // Navigation.PopToRootAsync();
  158. // }
  159. // else
  160. // {
  161. // bSharedDeviceFirstLoad = false;
  162. // clockOnButton.IsEnabled = false;
  163. // clockOnButton.Text = "Shared Device";
  164. // clockOnButton.BackgroundColor = Color.CornflowerBlue;
  165. // CurrentLocation.IsVisible = false;
  166. // jobBtn.IsVisible = false;
  167. // addNoteBtn.IsVisible = false;
  168. // Grid.SetRowSpan(flexLayoutScrollView, 3);
  169. // Grid.SetRow(flexLayoutScrollView, 1);
  170. // Timer t = new Timer(AutoLogoutUser, null, 600000, Timeout.Infinite);
  171. // }
  172. // }
  173. // else
  174. // bSharedDevice = false;
  175. //}
  176. //getting strange results from .IsAllowed
  177. //if (!PRSSecurity.IsAllowed<CanBypassTimeBench>())
  178. //{
  179. // clockOnButton.IsVisible = false;
  180. // jobBtn.IsVisible = false;
  181. // addNoteBtn.IsVisible = false;
  182. // CurrentLocation.IsVisible = false;
  183. // row0.Height = 0;
  184. // row1.Height = 0;
  185. // row2.Height = 0;
  186. // ForceLayout();
  187. //}
  188. if (!firstLoad)
  189. RefreshScreen();
  190. Task.Run(async () =>
  191. {
  192. bool isLatest = true;
  193. try
  194. {
  195. isLatest = await MobileUtils.AppVersion.IsUsingLatestVersion();
  196. }
  197. catch (Exception eLatest)
  198. {
  199. if (!recentlyAskedToUpdate)
  200. {
  201. Device.BeginInvokeOnMainThread(() =>
  202. {
  203. });
  204. recentlyAskedToUpdate = true;
  205. }
  206. string s = eLatest.Message;
  207. }
  208. if (!isLatest)
  209. {
  210. if (!recentlyAskedToUpdate)
  211. {
  212. string latestVersionNumber = await MobileUtils.AppVersion.GetLatestVersionNumber();
  213. if (updateCounter < 3)
  214. {
  215. Device.BeginInvokeOnMainThread(async () =>
  216. {
  217. string chosenOption = await DisplayActionSheet(String.Format("Version {0} Available. Update now?", latestVersionNumber), "You will be reminded again in 10 minutes.", null, "Yes", "No");
  218. switch (chosenOption)
  219. {
  220. case "No":
  221. break;
  222. case "Cancel":
  223. break;
  224. case "Yes":
  225. Dispatcher.BeginInvokeOnMainThread(() => { MobileUtils.AppVersion.OpenAppInStore(); });
  226. break;
  227. default:
  228. break;
  229. }
  230. });
  231. }
  232. else if (updateCounter >= 3)
  233. {
  234. Device.BeginInvokeOnMainThread(() =>
  235. {
  236. DisplayAlert(String.Format("Version {0} Available", latestVersionNumber), "Please update your software to the latest version.", "OK")
  237. .ContinueWith((Task task) =>
  238. {
  239. Dispatcher.BeginInvokeOnMainThread(() => { MobileUtils.AppVersion.OpenAppInStore(); });
  240. });
  241. });
  242. }
  243. recentlyAskedToUpdate = true;
  244. updateCounter++;
  245. }
  246. }
  247. });
  248. base.OnAppearing();
  249. }
  250. private void RecentlyAskedToUpdateTimer(object o)
  251. {
  252. recentlyAskedToUpdate = false;
  253. }
  254. private void AutoLogoutUser(object o)
  255. {
  256. App.LogoutUser();
  257. Navigation.PopToRootAsync();
  258. }
  259. private void RefreshScreen()
  260. {
  261. //if (bSharedDevice)
  262. // return;
  263. try
  264. {
  265. Device.BeginInvokeOnMainThread(() =>
  266. {
  267. homeScreenGrid.RaiseChild(CurrentLocation);
  268. bBusy = true;
  269. if (GlobalVariables.EmpID == Guid.Empty)
  270. {
  271. GlobalVariables.EmpID = GlobalVariables.GetEmployeeID();
  272. GlobalVariables.EmpName = GlobalVariables.GetEmployeeName();
  273. }
  274. clockOnButton.IsEnabled = false;
  275. bool PRSReady = (App.Data.Employee != null) && (App.Data.TimeSheets != null);
  276. bool GateReady = CheckLocation();
  277. CurrentLocation.Text = DisplayAddress();
  278. if (CurrentLocation.Text.Contains("ERROR"))
  279. CurrentLocation.Text = "Unknown Address";
  280. Title = null;
  281. Title = App.Data.Employee != null ? App.Data.Employee.Name : "";
  282. CoreRow timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault();
  283. clockOnButton.Text = PRSReady && GateReady ? timesheet == null ? "CLOCK ON" : "CLOCK OFF" : "PLEASE WAIT";
  284. clockOnButton.IsEnabled = PRSReady && GateReady;
  285. clockOnButton.BackgroundColor = PRSReady && GateReady ? timesheet == null ? Color.FromHex("#e6e6fa") : Color.FromHex("#15C7C1") : Color.Gainsboro;
  286. clockOnButton.BorderColor = PRSReady && GateReady ? timesheet == null ? Color.Black : Color.FromHex("#15C7C1") : Color.Gainsboro;
  287. if (clockOnButton.Text == "CLOCK OFF")
  288. {
  289. addNoteBtn.IsEnabled = true;
  290. if (GlobalVariables.JobsLoaded)
  291. jobBtn.IsEnabled = true;
  292. }
  293. else
  294. {
  295. addNoteBtn.IsEnabled = false;
  296. jobBtn.IsEnabled = false;
  297. }
  298. if (firstLoad)
  299. RefreshJobFromTimeSheet(timesheet);
  300. homeScreenGrid.RaiseChild(CurrentLocation);
  301. firstLoad = false;
  302. bBusy = false;
  303. });
  304. }
  305. catch (Exception e)
  306. {
  307. }
  308. //CurrentLocation.IsEnabled = PRSReady && GateReady;
  309. }
  310. private void RefreshJobFromTimeSheet(CoreRow timesheet)
  311. {
  312. Guid jobid = timesheet == null ? Guid.Empty : timesheet.Get<TimeSheet, Guid>(x => x.JobLink.ID);
  313. if (!jobid.Equals(Guid.Empty))
  314. {
  315. jobBtn.Text = String.Format("{0}: {1}", timesheet.Get<TimeSheet, String>(x => x.JobLink.JobNumber), timesheet.Get<TimeSheet, String>(x => x.JobLink.Name));
  316. _job.ID = timesheet == null ? Guid.Empty : timesheet.Get<TimeSheet, Guid>(x => x.JobLink.ID);
  317. _job.JobNumber = timesheet == null ? String.Empty : timesheet.Get<TimeSheet, String>(x => x.JobLink.JobNumber);
  318. _job.Name = timesheet == null ? String.Empty : timesheet.Get<TimeSheet, String>(x => x.JobLink.Name);
  319. }
  320. else
  321. {
  322. jobBtn.Text = "No Job Selected";
  323. _job = new Job();
  324. }
  325. }
  326. #endregion
  327. #region Clock on/off
  328. private void DataChanged(object sender, Type type, Exception e)
  329. {
  330. //if (bSharedDevice)
  331. // return;
  332. Device.BeginInvokeOnMainThread(() =>
  333. {
  334. if (e != null)
  335. {
  336. //DisplayAlert("Connection error with server - double check your connection", e.Message, "OK");
  337. }
  338. else
  339. RefreshScreen();
  340. });
  341. }
  342. private void DataRefreshed()
  343. {
  344. //if (bSharedDevice)
  345. // return;
  346. Device.BeginInvokeOnMainThread(() => { RefreshScreen(); });
  347. }
  348. bool bBusy = false;
  349. async void ClockOnOff_Clicked(object sender, System.EventArgs e)
  350. {
  351. if (bBusy)
  352. return;
  353. bBusy = true;
  354. string chosenOption = "Continue";
  355. if (clockOnButton.Text == "CLOCK OFF")
  356. {
  357. chosenOption = await DisplayActionSheet("Clock off?", "Cancel", null, "Continue", "Cancel");
  358. }
  359. switch (chosenOption)
  360. {
  361. case "Continue":
  362. break;
  363. case "Cancel":
  364. bBusy = false;
  365. return;
  366. break;
  367. default:
  368. bBusy = false;
  369. return;
  370. break;
  371. }
  372. if (clockOnButton.Text == "CLOCK ON" && clockedOffInLast5Seconds)
  373. {
  374. bBusy = false;
  375. return;
  376. }
  377. try
  378. {
  379. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
  380. {
  381. InABox.Core.Location here = new InABox.Core.Location()
  382. {
  383. Latitude = App.GPS.Latitude,
  384. Longitude = App.GPS.Longitude,
  385. Timestamp = DateTime.Now
  386. };
  387. TimeSheet timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  388. if (timesheet != null)
  389. {
  390. if (timesheet.ID != Guid.Empty)
  391. {
  392. if (ZeroLengthTimesheet())
  393. {
  394. bUpdatingTimesheet = true;
  395. new Client<TimeSheet>().Delete(timesheet, "Deleted due to zero duration timesheet");
  396. App.Data.TimeSheets.Rows.Clear();
  397. }
  398. else
  399. {
  400. FinishTimeSheet(timesheet, here);
  401. }
  402. }
  403. }
  404. else
  405. {
  406. Guid jobid = Guid.Empty;
  407. String jobnumber = "";
  408. String jobname = "";
  409. if (!App.Data.CanBypassGates)
  410. {
  411. CoreRow row = App.Data.Gates.Rows.FirstOrDefault(r => App.Bluetooth.Devices.Contains(r.Get<JobTracker, String>(c => c.TrackerLink.DeviceID)));
  412. if (row != null)
  413. {
  414. jobid = row.Get<JobTracker, Guid>(x => x.JobLink.ID);
  415. jobnumber = row.Get<JobTracker, String>(x => x.JobLink.JobNumber);
  416. jobname = row.Get<JobTracker, String>(x => x.JobLink.Name);
  417. }
  418. CreateTimeSheet(jobid, jobnumber, jobname, App.Data.Employee.UsualActivity.ID, App.Data.Employee.UsualActivity.Code, App.Data.Employee.UsualActivity.Description, here, App.GPS.Address, "Clocking On");
  419. }
  420. else
  421. {
  422. if ((!App.GPS.Latitude.Equals(0.0F)) && (!App.GPS.Longitude.Equals(0.0F)))
  423. {
  424. ChooseNearbyJob(here);
  425. }
  426. }
  427. }
  428. RefreshScreen();
  429. }
  430. }
  431. catch (Exception e2)
  432. {
  433. }
  434. bBusy = false;
  435. }
  436. #endregion
  437. #region Bluetooth
  438. private async void UploadTiles()
  439. {
  440. try
  441. {
  442. if (App.GPS.Latitude.Equals(0.0F) && App.GPS.Longitude.Equals(0.0F))
  443. return;
  444. if (App.Bluetooth.DetectedBlueToothMACAddresses.Count == 0)
  445. return;
  446. if (bRecentlyUpdatedTiles)
  447. return;
  448. bRecentlyUpdatedTiles = true;
  449. await Task.Run(() =>
  450. {
  451. InABox.Core.Location curlocation = new InABox.Core.Location() { Latitude = App.GPS.Latitude, Longitude = App.GPS.Longitude };
  452. curlocation.Timestamp = DateTime.Now;
  453. List<GPSTrackerLocation> trackersToUpdate = new List<GPSTrackerLocation>();
  454. foreach (String id in App.Bluetooth.DetectedBlueToothMACAddresses)
  455. {
  456. GPSTracker tracker = GlobalVariables.GPSTrackerCache.Find(x => x.DeviceID.Equals(id));
  457. bool stale = tracker.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 5, 0));
  458. bool moved = tracker.Location.DistanceTo(curlocation, UnitOfLength.Kilometers) > 0.1;
  459. if (stale || moved)
  460. {
  461. GlobalVariables.GPSTrackerCache.Remove(tracker);
  462. tracker.Location = curlocation;
  463. GlobalVariables.GPSTrackerCache.Add(tracker);
  464. //cache is updated
  465. GPSTrackerLocation gpsTrackerLocation = new GPSTrackerLocation();
  466. gpsTrackerLocation.DeviceID = tracker.DeviceID;
  467. gpsTrackerLocation.Location.Timestamp = tracker.Location.Timestamp;
  468. gpsTrackerLocation.Location = curlocation;
  469. trackersToUpdate.Add(gpsTrackerLocation);
  470. }
  471. }
  472. if (trackersToUpdate.Any())
  473. {
  474. if (ClientFactory.UserGuid != Guid.Empty)
  475. new Client<GPSTrackerLocation>().Save(trackersToUpdate, "Updating Bluetooth Device Locations");
  476. }
  477. App.Bluetooth.DetectedBlueToothMACAddresses.Clear();
  478. }
  479. );
  480. }
  481. catch (Exception e)
  482. {
  483. }
  484. //if ((master != null) && (master.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0))))
  485. //{
  486. // GPSTrackerLocation device = new GPSTrackerLocation();
  487. // device.DeviceID = MobileUtils.GetDeviceID();
  488. // device.Location.Latitude = App.GPS.Latitude;
  489. // device.Location.Longitude = App.GPS.Longitude;
  490. // device.Location.Timestamp = DateTime.Now;
  491. // locations.Add(device);
  492. // //device.BatteryLevel = ((double)CrossBattery.Current.RemainingChargePercent);
  493. // //new Client<GPSTrackerLocation>().Save(device, "Updating Device Location"); //, SaveTrackerCallback);
  494. //}
  495. #region OLD
  496. //for (int i = 0; i < App.Bluetooth.Devices.Length; i++)
  497. //{
  498. // String id = App.Bluetooth.Devices[i];
  499. // int level = App.Bluetooth.BatteryLevels[i];
  500. // var btmaster = trackers.FirstOrDefault(x => x.DeviceID.Equals(id));
  501. // if ((btmaster != null) && (!locations.Any(x => x.DeviceID.Equals(btmaster.DeviceID))))
  502. // {
  503. // bool stale = btmaster.Location.Timestamp < DateTime.Now.Subtract(new TimeSpan(0, 15, 0));
  504. // bool moved = btmaster.Location.DistanceTo(curlocation, UnitOfLength.Kilometers) > 0.1;
  505. // if (stale || moved)
  506. // {
  507. // GPSTrackerLocation location = new GPSTrackerLocation();
  508. // location.DeviceID = id;
  509. // location.Location.Latitude = App.GPS.Latitude;
  510. // location.Location.Longitude = App.GPS.Longitude;
  511. // location.Location.Timestamp = DateTime.Now;
  512. // location.BatteryLevel = level;
  513. // locations.Add(location);
  514. // }
  515. // }
  516. // //new Client<GPSTrackerLocation>().Save(location, "Found Kontakt Device"); //, SaveTrackerCallback);
  517. //}
  518. //if (locations.Any())
  519. // new Client<GPSTrackerLocation>().Save(locations, "Updating Bluetooth Device Locations", (o, e) => { });
  520. #endregion
  521. }
  522. private void RecentlyUpdatedTilesTimer(object o)
  523. {
  524. bRecentlyUpdatedTiles = false;
  525. App.Data.Refresh(true);
  526. SearchForNewNotifications();
  527. }
  528. private void LocationFound(LocationServices sender)
  529. {
  530. //if (bSharedDevice)
  531. // return;
  532. if (App.Bluetooth.RecentlyScanned)
  533. UploadTiles();
  534. try
  535. {
  536. TimeSheet timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  537. if (timesheet != null)
  538. {
  539. if (timesheet.StartLocation.Latitude.Equals(0.0F) && timesheet.StartLocation.Longitude.Equals(0.0F))
  540. {
  541. timesheet.StartLocation.Latitude = sender.Latitude;
  542. timesheet.StartLocation.Longitude = sender.Longitude;
  543. timesheet.StartLocation.Timestamp = sender.TimeStamp;
  544. timesheet.Address = sender.Address;
  545. new Client<TimeSheet>().Save(timesheet, "Updating Timesheet with GPS Coordinates", (o, e) => { });
  546. }
  547. }
  548. if (!string.IsNullOrWhiteSpace(matchedDeviceName))
  549. {
  550. InABox.Core.Location curlocation = new InABox.Core.Location() { Latitude = App.GPS.Latitude, Longitude = App.GPS.Longitude };
  551. curlocation.Timestamp = DateTime.Now;
  552. GPSTrackerLocation gpsTrackerLocation = new GPSTrackerLocation();
  553. gpsTrackerLocation.DeviceID = matchedDeviceName;
  554. gpsTrackerLocation.Location.Timestamp = curlocation.Timestamp;
  555. gpsTrackerLocation.Location = curlocation;
  556. new Client<GPSTrackerLocation>().Save(gpsTrackerLocation, "Updated company device location from Timebench");
  557. }
  558. Device.BeginInvokeOnMainThread(() =>
  559. {
  560. RefreshScreen();
  561. });
  562. }
  563. catch { }
  564. }
  565. private void LocationError(LocationServices sebder, Exception error)
  566. {
  567. }
  568. private void ScanFinished(Bluetooth sender)
  569. {
  570. try
  571. {
  572. //if (bSharedDevice)
  573. // return;
  574. Device.BeginInvokeOnMainThread(() =>
  575. {
  576. RefreshScreen();
  577. //if (Button2.BackgroundColor == Color.WhiteSmoke)
  578. // Button2.BackgroundColor = Color.Red;
  579. //else
  580. // Button2.BackgroundColor = Color.WhiteSmoke;
  581. });
  582. if (App.GPS.RecentlyLocated)
  583. UploadTiles();
  584. }
  585. catch { }
  586. }
  587. #endregion
  588. #region Utilities
  589. private void InitNotificationCentre()
  590. {
  591. try
  592. {
  593. LocalNotificationCenter.Current.NotificationActionTapped += (Plugin.LocalNotification.EventArgs.NotificationActionEventArgs e) =>
  594. {
  595. string data = e.Request.ReturningData;
  596. int index = data.IndexOf("$");
  597. Guid ID = Guid.Parse(data.Remove(index));
  598. string type = data.Substring(index + 1);
  599. if (type == "Comal.Classes.Kanban")
  600. {
  601. Device.BeginInvokeOnMainThread(() =>
  602. {
  603. AddEditTask taskPage = new AddEditTask(ID);
  604. Navigation.PushAsync(taskPage);
  605. });
  606. }
  607. };
  608. }
  609. catch { }
  610. }
  611. private async void SearchForNewNotifications()
  612. {
  613. //notifications poll reliably in the background for Anroid, unreliable for iOS due to difficulty with cross-platform plugins for notifications
  614. try
  615. {
  616. await Task.Run(() =>
  617. {
  618. if (ClientFactory.UserGuid != Guid.Empty)
  619. {
  620. CoreTable table = new Client<Notification>().Query
  621. (new Filter<Notification>(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid).And(X => X.Closed).IsEqualTo(DateTime.MinValue),
  622. new Columns<Notification>(
  623. x => x.ID, //0
  624. x => x.Sender.Name, //1
  625. x => x.Title, //2
  626. x => x.Created, //3
  627. x => x.Description, //4
  628. x => x.EntityType, //5
  629. x => x.EntityID //6
  630. )
  631. );
  632. if (NumberOfNotfications == table.Rows.Count()) //no new notifications or none present at all
  633. return;
  634. else //new notifications or previous notifications have now been dismissed
  635. {
  636. NumberOfNotfications = table.Rows.Count();
  637. RefreshOnNotificationsChange();
  638. CheckNotificationsPushed(table);
  639. }
  640. }
  641. });
  642. }
  643. catch { }
  644. }
  645. private void RefreshOnNotificationsChange()
  646. {
  647. try
  648. {
  649. int index = toolEntries.FindIndex(x => x.Text.Equals("Notifications"));
  650. toolEntries.RemoveAt(index);
  651. string notificationsString = "";
  652. if (NumberOfNotfications != 0)
  653. {
  654. notificationsString = NumberOfNotfications.ToString();
  655. }
  656. ToolEntry Notifications = new ToolEntry(notificationsString)
  657. {
  658. Text = "Notifications",
  659. Image = "notifications"
  660. };
  661. Notifications.OnTapped += (async (object sender, EventArgs e) =>
  662. {
  663. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
  664. {
  665. NotificationList notificationList = new NotificationList();
  666. notificationList.NotificationsClosed += (n) =>
  667. {
  668. NumberOfNotfications = n;
  669. RefreshOnNotificationsChange();
  670. };
  671. Navigation.PushAsync(notificationList);
  672. }
  673. });
  674. toolEntries.Insert(index, Notifications);
  675. Device.BeginInvokeOnMainThread(() =>
  676. {
  677. flexLayout.Children.RemoveAt(index);
  678. flexLayout.Children.Insert(index, toolEntries[index]);
  679. });
  680. }
  681. catch { }
  682. }
  683. private void CheckNotificationsPushed(CoreTable table)
  684. {
  685. try
  686. {
  687. if (!Application.Current.Properties.ContainsKey("LastPushedNotifications"))
  688. {
  689. Application.Current.Properties.Add("LastPushedNotifications", DateTime.Now);
  690. }
  691. DateTime lastPushed = DateTime.Parse(Application.Current.Properties["LastPushedNotifications"].ToString());
  692. List<NotificationShell> toNotify = new List<NotificationShell>();
  693. foreach (CoreRow row in table.Rows)
  694. {
  695. List<object> list = row.Values;
  696. DateTime created = DateTime.Parse(list[3].ToString());
  697. if (created > new DateTime(2022, 8, 22)) // prevent spam from buildup of old notifications before this is released
  698. {
  699. if (created > lastPushed)
  700. {
  701. if (list[1] == null) list[1] = "";
  702. if (list[2] == null) list[2] = "";
  703. if (list[3] == null) list[3] = DateTime.MinValue;
  704. if (list[4] == null) list[4] = "";
  705. if (list[5] == null) list[5] = "";
  706. if (list[6] == null) list[6] = Guid.Empty;
  707. NotificationShell shell = new NotificationShell
  708. {
  709. ID = Guid.Parse(list[0].ToString()),
  710. Sender = list[1].ToString(),
  711. Title = list[2].ToString(),
  712. Created = DateTime.Parse(list[3].ToString()),
  713. EntityType = list[5].ToString(),
  714. EntityID = Guid.Parse(list[6].ToString())
  715. };
  716. toNotify.Add(shell); //add notification to be pushed
  717. }
  718. }
  719. }
  720. if (toNotify.Count > 0)
  721. PushNotificationsAsync(toNotify);
  722. }
  723. catch { }
  724. }
  725. private async Task PushNotificationsAsync(List<NotificationShell> shells)
  726. {
  727. try
  728. {
  729. int count = 1;
  730. foreach (NotificationShell shell in shells)
  731. {
  732. var notification = new NotificationRequest
  733. {
  734. BadgeNumber = 1,
  735. Description = shell.Title,
  736. Title = "New PRS Notification: ",
  737. ReturningData = shell.EntityID.ToString() + "$" + shell.EntityType,
  738. NotificationId = count,
  739. };
  740. count++;
  741. NotificationImage img = new NotificationImage();
  742. img.ResourceName = "icon16.png";
  743. notification.Image = img;
  744. await LocalNotificationCenter.Current.Show(notification);
  745. //if (Device.RuntimePlatform.Equals(Device.iOS))
  746. //{
  747. // var content = new UNMutableNotificationContent();
  748. // content.Title = "New PRS Notification: ";
  749. // content.Subtitle = shell.Title;
  750. // content.Body = "";
  751. // content.Badge = 1;
  752. // var trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(1, false);
  753. // var requestID = "request";
  754. // var request = UNNotificationRequest.FromIdentifier(requestID, content, trigger);
  755. // UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
  756. // {
  757. // if (err != null)
  758. // {
  759. // Do something with error...
  760. // }
  761. // });
  762. //}
  763. }
  764. Application.Current.Properties["LastPushedNotifications"] = DateTime.Now;
  765. }
  766. catch { }
  767. }
  768. private void StartMidnightTimeSheetTimer()
  769. {
  770. midnightTimerOn = true;
  771. int msUntilMidnight = (int)(oneSecondBeforeMidnight - DateTime.Now).TotalMilliseconds;
  772. Timer midnightTimer = new Timer(MidnightTimerCallback, null, msUntilMidnight, Timeout.Infinite);
  773. }
  774. private void MidnightTimerCallback(object o)
  775. {
  776. try
  777. {
  778. //if (bSharedDevice)
  779. // return;
  780. if (midnightTimerOn)
  781. {
  782. if (clockOnButton.Text == "CLOCK OFF")
  783. {
  784. InABox.Core.Location here = new InABox.Core.Location()
  785. {
  786. Latitude = App.GPS.Latitude,
  787. Longitude = App.GPS.Longitude,
  788. Timestamp = DateTime.Now
  789. };
  790. TimeSheet timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  791. if (timesheet != null)
  792. {
  793. if (timesheet.ID != Guid.Empty)
  794. {
  795. if (ZeroLengthTimesheet())
  796. {
  797. bUpdatingTimesheet = true;
  798. new Client<TimeSheet>().Delete(timesheet, "Deleted due to zero duration timesheet");
  799. App.Data.TimeSheets.Rows.Clear();
  800. }
  801. else
  802. {
  803. timesheet.Finish = new TimeSpan(23, 59, 59);
  804. timesheet.FinishLocation = here;
  805. bUpdatingTimesheet = true;
  806. new Client<TimeSheet>().Save(timesheet, "Auto Close timesheet at Midnight");
  807. App.Data.TimeSheets.Rows.Clear();
  808. Guid jobid = Guid.Empty;
  809. String jobnumber = "";
  810. String jobname = "";
  811. if (!App.Data.CanBypassGates)
  812. {
  813. CoreRow row = App.Data.Gates.Rows.FirstOrDefault(r => App.Bluetooth.Devices.Contains(r.Get<JobTracker, String>(c => c.TrackerLink.DeviceID)));
  814. if (row != null)
  815. {
  816. jobid = row.Get<JobTracker, Guid>(x => x.JobLink.ID);
  817. jobnumber = row.Get<JobTracker, String>(x => x.JobLink.JobNumber);
  818. jobname = row.Get<JobTracker, String>(x => x.JobLink.Name);
  819. }
  820. CreateTimeSheet(jobid, jobnumber, jobname, App.Data.Employee.UsualActivity.ID, App.Data.Employee.UsualActivity.Code, App.Data.Employee.UsualActivity.Description, here, App.GPS.Address, "Clocking On");
  821. }
  822. else
  823. {
  824. if ((!App.GPS.Latitude.Equals(0.0F)) && (!App.GPS.Longitude.Equals(0.0F)))
  825. {
  826. ChooseNearbyJob(here);
  827. }
  828. }
  829. }
  830. }
  831. }
  832. }
  833. }
  834. }
  835. catch { }
  836. }
  837. private void FinishTimeSheet(TimeSheet timesheet, InABox.Core.Location here)
  838. {
  839. try
  840. {
  841. TimeSpan tod = DateTime.Now - DateTime.Today;
  842. timesheet.Finish = new TimeSpan(tod.Hours, tod.Minutes, 0);
  843. timesheet.FinishLocation = here;
  844. bUpdatingTimesheet = true;
  845. new Client<TimeSheet>().Save(timesheet, "Clocking Off");
  846. App.Data.TimeSheets.Rows.Clear();
  847. midnightTimerOn = false;
  848. Timer last60Seconds = new Timer(Last60SecondsTimerCallBack, null, 5000, Timeout.Infinite);
  849. clockedOffInLast5Seconds = true;
  850. }
  851. catch { }
  852. }
  853. private void Last60SecondsTimerCallBack(object o)
  854. {
  855. clockedOffInLast5Seconds = false;
  856. }
  857. private async void ChooseNearbyJob(InABox.Core.Location here)
  858. {
  859. try
  860. {
  861. JobShell selectedJob = new JobShell();
  862. Dictionary<string, JobShell> nearbyJobs = new Dictionary<string, JobShell>();
  863. foreach (CoreRow row in App.Data.Jobs.Rows)
  864. {
  865. InABox.Core.Location jobLocation = new InABox.Core.Location() { Latitude = row.Get<Job, double>(X => X.SiteAddress.Location.Latitude), Longitude = row.Get<Job, double>(X => X.SiteAddress.Location.Longitude) };
  866. double distance = here.DistanceTo(jobLocation, UnitOfLength.Kilometers);
  867. if (distance < 1.0F)
  868. {
  869. JobShell jobShell = new JobShell();
  870. jobShell.ID = row.Get<Job, Guid>(X => X.ID);
  871. jobShell.JobNumber = row.Get<Job, String>(x => x.JobNumber);
  872. jobShell.Name = row.Get<Job, String>(x => x.Name);
  873. jobShell.DisplayName = jobShell.JobNumber + " " + jobShell.Name;
  874. nearbyJobs.Add(jobShell.DisplayName, jobShell);
  875. }
  876. }
  877. if (nearbyJobs.Count > 1)
  878. {
  879. string[] array = nearbyJobs.Keys.ToArray();
  880. string chosenOption = await DisplayActionSheet("Choose job site", "Cancel", null, array);
  881. if (string.IsNullOrEmpty(chosenOption) || chosenOption.Equals("Cancel"))
  882. {
  883. CreateTimeSheet(selectedJob.ID, selectedJob.JobNumber, selectedJob.Name, App.Data.Employee.UsualActivity.ID, App.Data.Employee.UsualActivity.Code, App.Data.Employee.UsualActivity.Description, here, App.GPS.Address, "Clocking On");
  884. return;
  885. }
  886. else
  887. {
  888. selectedJob = nearbyJobs[chosenOption];
  889. }
  890. }
  891. else if (nearbyJobs.Count == 1)
  892. {
  893. selectedJob = nearbyJobs.Values.First();
  894. }
  895. CreateTimeSheet(selectedJob.ID, selectedJob.JobNumber, selectedJob.Name, App.Data.Employee.UsualActivity.ID, App.Data.Employee.UsualActivity.Code, App.Data.Employee.UsualActivity.Description, here, App.GPS.Address, "Clocking On");
  896. }
  897. catch { }
  898. }
  899. void AddNote_Tapped(System.Object sender, System.EventArgs e)
  900. {
  901. try
  902. {
  903. TimeSheet timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  904. if (timesheet == null)
  905. return;
  906. var notepage = new NotePage(timesheet);
  907. Navigation.PushAsync(notepage);
  908. }
  909. catch { }
  910. }
  911. private void JobBtn_Tapped(object sender, EventArgs e)
  912. {
  913. try
  914. {
  915. JobSelectionPage jobSelectionPage = new JobSelectionPage();
  916. jobSelectionPage.OnItemSelected += (() =>
  917. {
  918. _job.ID = jobSelectionPage.Job.ID;
  919. _job.JobNumber = jobSelectionPage.Job.JobNumber;
  920. _job.Name = jobSelectionPage.Job.Name;
  921. JobPage_OnItemSelected(jobSelectionPage.Job);
  922. });
  923. Navigation.PushAsync(jobSelectionPage);
  924. }
  925. catch { }
  926. }
  927. private void JobPage_OnItemSelected(JobShell job)
  928. {
  929. try
  930. {
  931. TimeSheet timesheet = App.Data.TimeSheets?.Rows.FirstOrDefault()?.ToObject<TimeSheet>();
  932. if (timesheet == null)
  933. return;
  934. String auditmessage = String.Format("Changed Selected Job to: {0}: {1}", timesheet.JobLink.JobNumber, timesheet.JobLink.Name);
  935. if (ZeroLengthTimesheet())
  936. {
  937. timesheet.JobLink.ID = job.ID;
  938. timesheet.JobLink.JobNumber = job.JobNumber;
  939. timesheet.JobLink.Name = job.Name;
  940. bUpdatingTimesheet = true;
  941. new Client<TimeSheet>().Save(timesheet, auditmessage);
  942. Device.BeginInvokeOnMainThread(() =>
  943. {
  944. if (timesheet.JobLink.ID != Guid.Empty)
  945. {
  946. jobBtn.Text = "(" + timesheet.JobLink.JobNumber + ") " + timesheet.JobLink.Name;
  947. }
  948. else
  949. {
  950. jobBtn.Text = "No Job Selected";
  951. }
  952. });
  953. }
  954. else
  955. {
  956. Guid activityid = timesheet.ActivityLink.ID;
  957. String activitycode = timesheet.ActivityLink.Code;
  958. String activitydescription = timesheet.ActivityLink.Description;
  959. InABox.Core.Location here = new InABox.Core.Location()
  960. {
  961. Latitude = App.GPS.Latitude,
  962. Longitude = App.GPS.Longitude,
  963. Timestamp = DateTime.Now
  964. };
  965. TimeSpan tod = DateTime.Now - DateTime.Today;
  966. timesheet.Finish = new TimeSpan(tod.Hours, tod.Minutes, 0);
  967. timesheet.FinishLocation = here;
  968. new Client<TimeSheet>().Save(timesheet, "Changing Job");
  969. CreateTimeSheet(
  970. job.ID,
  971. job.JobNumber,
  972. job.Name,
  973. activityid,
  974. activitycode,
  975. activitydescription,
  976. here,
  977. App.GPS.Address,
  978. auditmessage
  979. );
  980. }
  981. RefreshScreen();
  982. }
  983. catch { }
  984. }
  985. private bool CheckTimeSheetAgainstGates(TimeSheet timesheet)
  986. {
  987. DateTime now = DateTime.Now;
  988. //var timesheet = CurrentTimeSheet();
  989. //Can't confirm if there is no timesheet
  990. if (timesheet == null)
  991. return false;
  992. // Can't confirm if there are no devices
  993. if (App.Bluetooth.Devices.Length == 0)
  994. return false;
  995. if (App.Data.Gates == null)
  996. return false;
  997. long tsTicks = timesheet.Date.Add(timesheet.Start).Ticks;
  998. long btTicks = App.Bluetooth.TimeStamp.Ticks;
  999. if (Math.Abs(tsTicks - btTicks) > new TimeSpan(0, 2, 0).Ticks)
  1000. return false;
  1001. CoreRow firstgate = null;
  1002. List<String> gates = new List<string>();
  1003. // Scan every located d
  1004. foreach (var device in App.Bluetooth.Devices)
  1005. {
  1006. CoreRow gate = App.Data.Gates?.Rows.FirstOrDefault(r => r.Get<JobTracker, String>(c => c.TrackerLink.DeviceID) == device);
  1007. if (gate != null)
  1008. {
  1009. if ((gate.Get<JobTracker, bool>(x => x.IsJobSite) == true) && (firstgate == null))
  1010. firstgate = gate;
  1011. gates.Add(gate.Get<JobTracker, String>(x => x.Gate));
  1012. }
  1013. }
  1014. if (gates.Any())
  1015. {
  1016. timesheet.Gate = String.Join(", ", gates.OrderBy(x => x));
  1017. if (firstgate != null)
  1018. {
  1019. timesheet.JobLink.ID = firstgate.Get<JobTracker, Guid>(x => x.JobLink.ID);
  1020. timesheet.JobLink.JobNumber = firstgate.Get<JobTracker, String>(x => x.JobLink.JobNumber);
  1021. timesheet.JobLink.Name = firstgate.Get<JobTracker, String>(x => x.JobLink.Name);
  1022. }
  1023. return true;
  1024. //new Client<TimeSheet>().Save(timesheet, "Confirmed Gate Entry by Bluetooth Tracker", (o, e) => { });
  1025. }
  1026. return false;
  1027. }
  1028. private bool ZeroLengthTimesheet()
  1029. {
  1030. try
  1031. {
  1032. if (App.Data.TimeSheets == null)
  1033. return true;
  1034. CoreRow row = App.Data.TimeSheets.Rows.FirstOrDefault();
  1035. if (row == null)
  1036. return true;
  1037. String notes = row.Get<TimeSheet, String>(x => x.Notes);
  1038. if (!String.IsNullOrWhiteSpace(notes))
  1039. return false;
  1040. DateTime date = row.Get<TimeSheet, DateTime>(x => x.Date);
  1041. TimeSpan start = row.Get<TimeSheet, TimeSpan>(x => x.Start);
  1042. if (date.Equals(DateTime.Today))
  1043. {
  1044. TimeSpan tod = DateTime.Now - DateTime.Today;
  1045. var diff = (tod - start).TotalSeconds;
  1046. if (Math.Abs(diff) < 120.0F)
  1047. return true;
  1048. }
  1049. }
  1050. catch { }
  1051. return false;
  1052. }
  1053. private async void CreateTimeSheet(Guid jobid, string jobnumber, String jobname, Guid activityid, String activitycode, String activitydescription, InABox.Core.Location location, String address, String auditmessage)
  1054. {
  1055. try
  1056. {
  1057. var timesheet = new TimeSheet();
  1058. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
  1059. {
  1060. timesheet.EmployeeID = App.Data.Employee.ID;
  1061. timesheet.EmployeeLink.ID = App.Data.Employee.ID;
  1062. timesheet.Date = DateTime.Today;
  1063. TimeSpan tod = DateTime.Now - DateTime.Today;
  1064. tod = new TimeSpan(tod.Hours, tod.Minutes, 0);
  1065. timesheet.Start = tod;
  1066. timesheet.StartLocation = location;
  1067. timesheet.JobLink.ID = jobid;
  1068. timesheet.JobLink.JobNumber = jobnumber;
  1069. timesheet.JobLink.Name = jobname;
  1070. timesheet.ActivityLink.ID = activityid;
  1071. timesheet.ActivityLink.Code = activitycode;
  1072. timesheet.ActivityLink.Description = activitydescription;
  1073. timesheet.Address = address;
  1074. timesheet.SoftwareVersion = MobileUtils.AppVersion.InstalledVersionNumber + GlobalVariables.DeviceString;
  1075. //if (ClientFactory.IsAllowed<AllowTimeSheetRollover>()) CheckTimeSheetAgainstGates(timesheet);
  1076. bUpdatingTimesheet = true;
  1077. new Client<TimeSheet>().Save(timesheet, auditmessage);
  1078. if (timesheet.ID == Guid.Empty)
  1079. {
  1080. DisplayAlert("Error creating new timesheet", "Please check your connection and try again", "OK");
  1081. return;
  1082. }
  1083. StartMidnightTimeSheetTimer();
  1084. // Don't Save Completed Timesheets!
  1085. App.Data.TimeSheets.Rows.Clear();
  1086. if (timesheet.Finish.Ticks == 0L)
  1087. {
  1088. CoreRow row = App.Data.TimeSheets.NewRow();
  1089. App.Data.TimeSheets.LoadRow(row, timesheet);
  1090. App.Data.TimeSheets.Rows.Add(row);
  1091. }
  1092. }
  1093. Device.BeginInvokeOnMainThread(() =>
  1094. {
  1095. if (timesheet.JobLink.ID != Guid.Empty)
  1096. {
  1097. jobBtn.Text = "(" + timesheet.JobLink.JobNumber + ") " + timesheet.JobLink.Name;
  1098. }
  1099. else
  1100. {
  1101. jobBtn.Text = "No Job Selected";
  1102. }
  1103. });
  1104. }
  1105. catch { }
  1106. }
  1107. private bool CheckLocation()
  1108. {
  1109. try
  1110. {
  1111. if (App.Data.CanBypassGates)
  1112. {
  1113. if (App.GPS.TimeStamp > DateTime.Now.Subtract(new TimeSpan(0, 5, 0)))
  1114. return true;
  1115. else
  1116. return false;
  1117. }
  1118. if (App.Data.Gates == null)
  1119. return false;
  1120. if (App.Bluetooth.TimeStamp < DateTime.Now.Subtract(new TimeSpan(0, 2, 0)))
  1121. return false;
  1122. if (!App.Bluetooth.Devices.Any())
  1123. return false;
  1124. return App.Data.Gates.Rows.Any(r => App.Bluetooth.Devices.Contains(r.Get<JobTracker, String>(c => c.TrackerLink.DeviceID)));
  1125. }
  1126. catch
  1127. {
  1128. return true;
  1129. }
  1130. }
  1131. private String DisplayAddress()
  1132. {
  1133. try
  1134. {
  1135. bool PRSReady = App.Data.Employee != null; // && (TimeSheet != null); // && (Activities != null);
  1136. if (!PRSReady)
  1137. return "Retrieving Data";
  1138. if (App.Data.CanBypassGates)
  1139. {
  1140. if (App.GPS.TimeStamp < DateTime.Now.Subtract(new TimeSpan(0, 5, 0)))
  1141. {
  1142. App.GPS.GetLocation(true);
  1143. return "Searching for GPS";
  1144. }
  1145. else
  1146. return App.GPS.Address;
  1147. }
  1148. else
  1149. {
  1150. // Hmm.. this can/should be simplified
  1151. // if in range of a gate
  1152. // Show Gate Description
  1153. // else
  1154. // "Looking for Gate"
  1155. if ((App.Data.Gates != null) && (App.Bluetooth.TimeStamp > DateTime.Now.Subtract(new TimeSpan(0, 2, 0))))
  1156. {
  1157. CoreRow row = App.Data.Gates.Rows.FirstOrDefault(r => App.Bluetooth.Devices.Contains(r.Get<JobTracker, String>(c => c.TrackerLink.DeviceID)));
  1158. if (row != null)
  1159. return row.Get<JobTracker, String>(x => x.Gate);
  1160. //else if ((CurrentTimeSheet() != null) && (!GPS.TimeStamp.IsEmpty()))
  1161. // return GPS.Address;
  1162. else
  1163. return "Looking for Gate";
  1164. }
  1165. //else if ((CurrentTimeSheet() != null) && (GPS.TimeStamp > DateTime.Now.Subtract(new TimeSpan(0, 2, 0))))
  1166. // return GPS.Address;
  1167. else
  1168. return "Looking for Gate";
  1169. }
  1170. }
  1171. catch
  1172. {
  1173. return "Address error";
  1174. }
  1175. }
  1176. #region Background Loading
  1177. private void LoadCacheLists()
  1178. {
  1179. GlobalVariables.ProductsLoaded = false;
  1180. GlobalVariables.JobsLoaded = false;
  1181. GlobalVariables.GetXamarinWidth();
  1182. LoadJobShells();
  1183. LoadEmployeeShells();
  1184. LoadProducts();
  1185. LoadCompanyDevices();
  1186. LoadBlueToothAddresses();
  1187. //LoadHRToDos(); to be uncommented when ready for roll out
  1188. }
  1189. private void LoadCompanyDevices()
  1190. {
  1191. Task.Run(() =>
  1192. {
  1193. if (!string.IsNullOrWhiteSpace(deviceName) && deviceName != "unknown")
  1194. {
  1195. List<Equipment> companyDevices = new List<Equipment>();
  1196. CoreTable table = new Client<Equipment>().Query
  1197. (
  1198. new Filter<Equipment>(x => x.GroupLink.Code).IsEqualTo("DEVICE"),
  1199. new Columns<Equipment>(
  1200. x => x.TrackerLink.DeviceID
  1201. )
  1202. );
  1203. if (table.Rows.Any())
  1204. {
  1205. foreach (CoreRow row in table.Rows)
  1206. {
  1207. List<object> list = row.Values;
  1208. if (list[0].ToString().Equals(deviceName))
  1209. {
  1210. matchedDeviceName = deviceName;
  1211. }
  1212. }
  1213. }
  1214. }
  1215. });
  1216. }
  1217. private async void LoadEmployeeShells()
  1218. {
  1219. await Task.Run(() =>
  1220. {
  1221. List<EmployeeShell> employeeShells = new List<EmployeeShell>();
  1222. List<EmployeeShell> teamEmployeeShells = new List<EmployeeShell>();
  1223. List<Guid> empIDs = new List<Guid>();
  1224. CoreTable table = new Client<EmployeeTeam>().Query(
  1225. new Filter<EmployeeTeam>(x => x.EmployeeLink.FinishDate).IsEqualTo(DateTime.MinValue),
  1226. new Columns<EmployeeTeam>(x => x.EmployeeLink.ID, x => x.EmployeeLink.Name, x => x.TeamLink.Name),
  1227. new SortOrder<EmployeeTeam>(x => x.EmployeeLink.Name)
  1228. );
  1229. foreach (CoreRow row in table.Rows)
  1230. {
  1231. List<object> list = row.Values;
  1232. if (list[0] == null) { list[0] = Guid.Empty; } //0
  1233. if (list[1] == null) { list[1] = ""; } //1
  1234. if (list[2] == null) { list[2] = ""; } //2
  1235. EmployeeShell employeeShell = new EmployeeShell
  1236. {
  1237. ID = Guid.Parse(list[0].ToString()),
  1238. Name = list[1].ToString(),
  1239. TeamName = list[2].ToString()
  1240. };
  1241. if (!empIDs.Contains(employeeShell.ID))
  1242. {
  1243. empIDs.Add(employeeShell.ID);
  1244. employeeShell.TeamName = AddTeamName(employeeShell.TeamName);
  1245. employeeShells.Add(employeeShell);
  1246. }
  1247. teamEmployeeShells.Add(employeeShell);
  1248. }
  1249. GlobalVariables.EmployeeShells = employeeShells;
  1250. GlobalVariables.TeamEmployeeShells = teamEmployeeShells;
  1251. });
  1252. await Task.Run(() =>
  1253. {
  1254. List<string> teamNames = new List<string>();
  1255. CoreTable table = new Client<Team>().Query(
  1256. null,
  1257. new Columns<Team>(x => x.Name),
  1258. new SortOrder<Team>(x => x.Name)
  1259. );
  1260. foreach (CoreRow row in table.Rows)
  1261. {
  1262. List<object> list = row.Values;
  1263. if (list[0] == null) { list[0] = ""; }
  1264. teamNames.Add(list[0].ToString());
  1265. }
  1266. GlobalVariables.TeamNames = teamNames;
  1267. });
  1268. }
  1269. private string AddTeamName(string s)
  1270. {
  1271. if (s.Equals("Factory Staff"))
  1272. s = "Factory";
  1273. else if (s.Equals("Office Staff"))
  1274. s = "Office";
  1275. else if (s.Equals("All Staff"))
  1276. s = "All";
  1277. else if (s.Equals("Site Staff"))
  1278. s = "Site";
  1279. return s;
  1280. }
  1281. async void LoadJobShells()
  1282. {
  1283. try
  1284. {
  1285. await Task.Run(() =>
  1286. {
  1287. List<JobShell> jobShells = new List<JobShell>();
  1288. CoreTable table = new Client<Job>().Query(
  1289. new Filter<Job>(x => x.JobStatus.Active).IsEqualTo(true),
  1290. new Columns<Job>(x => x.ID, x => x.Name, x => x.JobNumber, x => x.JobStatus.Description, x => x.Color),
  1291. new SortOrder<Job>(x => x.JobNumber)
  1292. );
  1293. foreach (CoreRow row in table.Rows)
  1294. {
  1295. List<object> list = row.Values;
  1296. if (list[0] == null) { list[0] = Guid.Empty; } //0
  1297. if (list[1] == null) { list[1] = ""; } //1
  1298. if (list[2] == null) { list[2] = ""; } //2
  1299. if (list[3] == null) { list[3] = ""; } //3
  1300. if (list[4] == null) { list[4] = ""; } //4
  1301. JobShell jobshell = new JobShell
  1302. {
  1303. ID = Guid.Parse(list[0].ToString()),
  1304. Name = list[1].ToString(),
  1305. JobNumber = list[2].ToString(),
  1306. JobStatusDescription = list[3].ToString(),
  1307. Color = Color.FromHex(list[4].ToString())
  1308. };
  1309. if (jobshell.JobStatusDescription.Equals("Active Projects"))
  1310. jobshell.JobStatusDescription = "Active";
  1311. else if (jobshell.JobStatusDescription.Equals("Projects - Hidden from View"))
  1312. jobshell.JobStatusDescription = "Hidden";
  1313. else if (jobshell.JobStatusDescription.Equals("Projects in Defect Liability Period"))
  1314. jobshell.JobStatusDescription = "Defect Liability";
  1315. jobshell.DisplayName = "(" + jobshell.JobNumber + ") " + jobshell.Name;
  1316. jobShells.Add(jobshell);
  1317. }
  1318. GlobalVariables.JobShells = jobShells;
  1319. GlobalVariables.JobShells.Insert(0, new JobShell { ID = Guid.Empty, JobNumber = "No Job", Name = "Empty Job", JobStatusDescription = "Hidden" });
  1320. GlobalVariables.JobsLoaded = true;
  1321. });
  1322. }
  1323. catch { }
  1324. }
  1325. private async void LoadHRToDos()
  1326. {
  1327. try
  1328. {
  1329. await Task.Run(() =>
  1330. {
  1331. Thread.Sleep(10000);
  1332. if (GlobalVariables.UpdateHRItemsNeedingAttention())
  1333. {
  1334. string message = "You have HR Items needing attention. Open My HR now?";
  1335. Device.BeginInvokeOnMainThread(async () =>
  1336. {
  1337. string chosenOption = await DisplayActionSheet(message, "Cancel", null, "Yes", "No");
  1338. switch (chosenOption)
  1339. {
  1340. case "Cancel":
  1341. break;
  1342. case "No":
  1343. break;
  1344. default:
  1345. break;
  1346. case "Yes":
  1347. MyHRHome myHRHome = new MyHRHome();
  1348. Navigation.PushAsync(myHRHome);
  1349. break;
  1350. }
  1351. });
  1352. }
  1353. });
  1354. }
  1355. catch { }
  1356. }
  1357. private async void LoadProducts()
  1358. {
  1359. try
  1360. {
  1361. await Task.Run(() =>
  1362. {
  1363. ProductsLoader productsLoader = new ProductsLoader();
  1364. //if (ClientFactory.IsAllowed<CanViewStoresRequisitions>())
  1365. //{
  1366. //}
  1367. });
  1368. }
  1369. catch { }
  1370. }
  1371. private async void LoadBlueToothAddresses()
  1372. {
  1373. try
  1374. {
  1375. //if (bSharedDevice)
  1376. // return;
  1377. await Task.Run(() =>
  1378. {
  1379. GlobalVariables.GPSTrackerCache = new List<GPSTracker>();
  1380. CoreTable table = new Client<GPSTracker>().Query(new Filter<GPSTracker>(x => x.Type.Description).Contains("Kontakt"));
  1381. foreach (CoreRow row in table.Rows)
  1382. {
  1383. GPSTracker tracker = row.ToObject<GPSTracker>();
  1384. GlobalVariables.GPSTrackerCache.Add(tracker);
  1385. App.Bluetooth.KnownBlueToothMACAddresses.Add(tracker.DeviceID);
  1386. }
  1387. });
  1388. }
  1389. catch { }
  1390. }
  1391. #endregion
  1392. #endregion
  1393. #region Modules
  1394. public async void InitToolEntryList()
  1395. {
  1396. try
  1397. {
  1398. await Task.Run(() =>
  1399. {
  1400. //Assignments
  1401. ToolEntry Assignments = new ToolEntry
  1402. {
  1403. Text = "Assignments",
  1404. Image = "calendar"
  1405. };
  1406. Assignments.IsVisible = PRSSecurity.CanView<Assignment>();
  1407. Assignments.OnTapped += ((object sender, EventArgs e) =>
  1408. {
  1409. var assignment_form = new AssignmentList();
  1410. Navigation.PushAsync(assignment_form);
  1411. });
  1412. toolEntries.Add(Assignments);
  1413. //Deliveries
  1414. ToolEntry Deliveries = new ToolEntry
  1415. {
  1416. Text = "Deliveries",
  1417. Image = "deliveries"
  1418. };
  1419. Deliveries.IsVisible = PRSSecurity.CanView<Delivery>();
  1420. Deliveries.OnTapped += ((object sender, EventArgs e) =>
  1421. {
  1422. var delivery_form = new DeliveryList();
  1423. Navigation.PushAsync(delivery_form);
  1424. });
  1425. toolEntries.Add(Deliveries);
  1426. //Digital Forms
  1427. ToolEntry Forms = new ToolEntry
  1428. {
  1429. Text = "Forms",
  1430. Image = "forms"
  1431. };
  1432. Forms.IsVisible = PRSSecurity.CanView<DigitalForm>();
  1433. Forms.OnTapped += ((object sender, EventArgs e) =>
  1434. {
  1435. var qaFormPicker = new DigitalFormsPicker();
  1436. Navigation.PushAsync(qaFormPicker);
  1437. });
  1438. toolEntries.Add(Forms);
  1439. //Equipment
  1440. ToolEntry Equipment = new ToolEntry
  1441. {
  1442. Text = "Equipment",
  1443. Image = "digger"
  1444. };
  1445. Equipment.IsVisible = PRSSecurity.CanView<Equipment>();
  1446. Equipment.OnTapped += (async (object sender, EventArgs e) =>
  1447. {
  1448. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
  1449. {
  1450. var equipment = new EquipmentModule();
  1451. Navigation.PushAsync(equipment);
  1452. }
  1453. });
  1454. toolEntries.Add(Equipment);
  1455. //InOut
  1456. ToolEntry InOut = new ToolEntry
  1457. {
  1458. Text = "In/Out",
  1459. Image = "inout"
  1460. };
  1461. InOut.IsVisible = PRSSecurity.IsAllowed<CanViewInOutBoard>();
  1462. InOut.OnTapped += ((object sender, EventArgs e) =>
  1463. {
  1464. var staff_form = new StaffStatusPage();
  1465. Navigation.PushAsync(staff_form);
  1466. });
  1467. toolEntries.Add(InOut);
  1468. //Manufacturing
  1469. ToolEntry Manufacturing = new ToolEntry
  1470. {
  1471. Text = "Manufacturing",
  1472. Image = "manufacturingg"
  1473. };
  1474. if (Device.RuntimePlatform.Equals(Device.iOS))
  1475. {
  1476. Manufacturing.Image = "Image";
  1477. }
  1478. Manufacturing.IsVisible = PRSSecurity.IsAllowed<CanViewManufacturingOnMobile>();
  1479. Manufacturing.OnTapped += ((object sender, EventArgs e) =>
  1480. {
  1481. ManufacturingScreen manufacturingScreen = new ManufacturingScreen();
  1482. Navigation.PushAsync(manufacturingScreen);
  1483. });
  1484. toolEntries.Add(Manufacturing);
  1485. //My HR
  1486. ToolEntry MyHR = new ToolEntry
  1487. {
  1488. Text = "My HR",
  1489. Image = "myhr"
  1490. };
  1491. MyHR.OnTapped += ((object sender, EventArgs e) =>
  1492. {
  1493. MyHRHome myHRHome = new MyHRHome();
  1494. Navigation.PushAsync(myHRHome);
  1495. });
  1496. toolEntries.Add(MyHR);
  1497. //Notifications
  1498. ToolEntry Notifications = new ToolEntry()
  1499. {
  1500. Text = "Notifications",
  1501. Image = "notifications"
  1502. };
  1503. Notifications.OnTapped += (async (object sender, EventArgs e) =>
  1504. {
  1505. NotificationList notificationList = new NotificationList();
  1506. notificationList.NotificationsClosed += (n) =>
  1507. {
  1508. NumberOfNotfications = n;
  1509. RefreshOnNotificationsChange();
  1510. };
  1511. Navigation.PushAsync(notificationList);
  1512. });
  1513. toolEntries.Add(Notifications);
  1514. ToolEntry Products = new ToolEntry()
  1515. {
  1516. Text = "Products",
  1517. Image = "products"
  1518. };
  1519. Products.OnTapped += ((object sender, EventArgs e) =>
  1520. {
  1521. if (GlobalVariables.ProductsLoaded)
  1522. {
  1523. ProductList products = new ProductList(GlobalVariables.ProductShells);
  1524. Navigation.PushAsync(products);
  1525. }
  1526. else
  1527. {
  1528. ProductList products = new ProductList();
  1529. Navigation.PushAsync(products);
  1530. }
  1531. });
  1532. toolEntries.Add(Products);
  1533. //Purchase Orders
  1534. ToolEntry PurchaseOrders = new ToolEntry()
  1535. {
  1536. Text = "Purchase Orders",
  1537. Image = "shoppingcart"
  1538. };
  1539. PurchaseOrders.IsVisible = PRSSecurity.CanView<PurchaseOrder>();
  1540. PurchaseOrders.OnTapped += ((object sender, EventArgs e) =>
  1541. {
  1542. PurchaseOrderModule page = new PurchaseOrderModule();
  1543. Navigation.PushAsync(page);
  1544. });
  1545. toolEntries.Add(PurchaseOrders);
  1546. //Scanner
  1547. ToolEntry Scanner = new ToolEntry
  1548. {
  1549. Text = "Scanner",
  1550. Image = "scanner"
  1551. };
  1552. Scanner.OnTapped += ((object sender, EventArgs e) =>
  1553. {
  1554. ScannerPage scannerPage = new ScannerPage();
  1555. Navigation.PushAsync(scannerPage);
  1556. });
  1557. toolEntries.Add(Scanner);
  1558. //Setouts
  1559. ToolEntry Setouts = new ToolEntry
  1560. {
  1561. Text = "Setouts",
  1562. Image = "setoutt"
  1563. };
  1564. Setouts.OnTapped += (object sender, EventArgs e) =>
  1565. {
  1566. SetoutsScreen setoutsScreen = new SetoutsScreen();
  1567. Navigation.PushAsync(setoutsScreen);
  1568. };
  1569. toolEntries.Add(Setouts);
  1570. //Site
  1571. ToolEntry Site = new ToolEntry
  1572. {
  1573. Text = "Site",
  1574. Image = "construction"
  1575. };
  1576. Site.OnTapped += ((object sender, EventArgs e) =>
  1577. {
  1578. Site site = new Site(_job);
  1579. Navigation.PushAsync(site);
  1580. //if (_job.ID == Guid.Empty)
  1581. //{
  1582. // JobSelectionPage jobSelectionPage = new JobSelectionPage(true);
  1583. // jobSelectionPage.OnItemSelected += (async () =>
  1584. // {
  1585. // if (jobSelectionPage.Job.ID != Guid.Empty)
  1586. // {
  1587. // Job selectedJob = new Job();
  1588. // selectedJob.ID = jobSelectionPage.Job.ID;
  1589. // selectedJob.JobNumber = jobSelectionPage.Job.JobNumber;
  1590. // selectedJob.Name = jobSelectionPage.Job.Name;
  1591. // Site site = new Site(selectedJob);
  1592. // var previousPage = Navigation.NavigationStack.LastOrDefault();
  1593. // await Navigation.PushAsync(site);
  1594. // Navigation.RemovePage(previousPage);
  1595. // }
  1596. // });
  1597. // Navigation.PushAsync(jobSelectionPage);
  1598. //}
  1599. });
  1600. toolEntries.Add(Site);
  1601. //Store Requis
  1602. ToolEntry StoreRequis = new ToolEntry
  1603. {
  1604. Text = "Store Requis",
  1605. Image = "storerequis"
  1606. };
  1607. StoreRequis.IsVisible = PRSSecurity.CanView<Requisition>();
  1608. StoreRequis.OnTapped += ((object sender, EventArgs e) =>
  1609. {
  1610. var storeRequisList = new StoreRequiList();
  1611. Navigation.PushAsync(storeRequisList);
  1612. });
  1613. toolEntries.Add(StoreRequis);
  1614. //Tasks
  1615. ToolEntry Tasks = new ToolEntry
  1616. {
  1617. Text = "Tasks",
  1618. Image = "tasks"
  1619. };
  1620. Tasks.IsVisible = PRSSecurity.IsAllowed<CanViewTasks>();
  1621. Tasks.OnTapped += ((object sender, EventArgs e) =>
  1622. {
  1623. var tasksForm = new TasksList();
  1624. Navigation.PushAsync(tasksForm);
  1625. });
  1626. toolEntries.Add(Tasks);
  1627. //Warehousing
  1628. ToolEntry Warehousing = new ToolEntry
  1629. {
  1630. Text = "Warehousing",
  1631. Image = "newwarehousing"
  1632. };
  1633. Warehousing.IsVisible = PRSSecurity.CanView<StockWarehouse>();
  1634. Warehousing.OnTapped += ((object sender, EventArgs e) =>
  1635. {
  1636. Warehousing2 locations = new Warehousing2();
  1637. Navigation.PushAsync(locations);
  1638. });
  1639. toolEntries.Add(Warehousing);
  1640. AddChildren();
  1641. });
  1642. }
  1643. catch { }
  1644. }
  1645. private void AddChildren()
  1646. {
  1647. Device.BeginInvokeOnMainThread(() =>
  1648. {
  1649. foreach (ToolEntry toolEntry in toolEntries)
  1650. {
  1651. toolEntry.Margin = new Thickness(5, 0, 5, 0);
  1652. flexLayout.Children.Add(toolEntry);
  1653. }
  1654. SearchForNewNotifications();
  1655. AddBlanks();
  1656. });
  1657. }
  1658. private void AddBlanks()
  1659. {
  1660. for (int x = 0; x < 6; x++)
  1661. {
  1662. ToolEntry toolEntry = new ToolEntry(true);
  1663. toolEntry.Margin = new Thickness(5, 0, 5, 0);
  1664. flexLayout.Children.Add(toolEntry);
  1665. }
  1666. }
  1667. private void Settings_Tapped(object sender, EventArgs e)
  1668. {
  1669. try
  1670. {
  1671. Settings settingsform = new Settings();
  1672. settingsform.Disappearing += (object sender2, EventArgs e2) =>
  1673. {
  1674. settingsform = (Settings)sender2;
  1675. if (settingsform.SettingsChanged)
  1676. Navigation.PopModalAsync();
  1677. };
  1678. Navigation.PushAsync(settingsform);
  1679. }
  1680. catch { }
  1681. }
  1682. #endregion
  1683. }
  1684. }