using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.Mobile; using Plugin.BLE.Abstractions.EventArgs; using Xamarin.Essentials; using Xamarin.Forms; using XF.Material.Forms.UI.Dialogs; namespace comal.timesheets { public partial class PINLoginPage : ContentPage { String pin = ""; bool autoChange = false; bool twoFAvisible = false; bool checkingClipBoard = false; string loadFromIOSLink = ""; bool timerRunning = false; Timer timer; public PINLoginPage(string logindetails = "") { InitializeComponent(); //ShowSplashPage(); try { ShowPINPad(); CheckForLatestVersion(); if (!string.IsNullOrWhiteSpace(logindetails)) loadFromIOSLink = logindetails; } catch { ShowPINPad(); } } protected override void OnAppearing() { try { CheckLoadFromLink(); if (!CheckAndPaste2FAText()) return; SetPinAndShowSplash(); if (App.IsUserLoggedIn && GlobalVariables.ChangeUser == false) { LaunchMainPage(); return; } else { if (String.IsNullOrWhiteSpace(App.DBSettings.UserID) || String.IsNullOrWhiteSpace(App.DBSettings.Password)) { DisplayAlert("Unable to log in", "User ID or password is blank", "OK"); ShowPINPad(); return; } RunValidate(); } } catch { ShowPINPad(); } base.OnAppearing(); } private void SetPinAndShowSplash() { try { GlobalVariables.InternalOnAppearing = false; pin = ""; PIN.Text = ""; ShowSplashPage(); } catch { } } private void CheckLoadFromLink() { try { if (!string.IsNullOrWhiteSpace(loadFromIOSLink)) { GlobalVariables.LoadFromLinkString = loadFromIOSLink.Remove(0, 17); MobileUtils.LoadFromLink(); } if (!string.IsNullOrWhiteSpace(GlobalVariables.LoadFromLinkString)) { if (GlobalVariables.LoadFromLinkString == "Link has expired") DisplayAlert("Alert", "Link has expired", "OK"); } } catch { } } private bool CheckAndPaste2FAText() { try { if (!GlobalVariables.InternalOnAppearing) { if (!twoFAvisible) return false; Device.BeginInvokeOnMainThread(async () => { try { string text = await Clipboard.GetTextAsync(); if (text.Length != 6) return; else ent0.Text = text; } catch { } }); return false; } return true; } catch { return true; } } private async void RunValidate(bool usePIN = false, string pin = "") { try { //ShowSplashPage(); Guid sessionID = Guid.Empty; if (App.Current.Properties.ContainsKey("SessionID")) sessionID = Guid.Parse(App.Current.Properties["SessionID"].ToString()); ValidationResult result; if (usePIN) result = ClientFactory.Validate(pin, sessionID); else result = ClientFactory.Validate(App.DBSettings.UserID, App.DBSettings.Password, sessionID); switch (result) { case ValidationResult.INVALID: ShowPINPad(); DisplayAlert("Error logging in", "Invalid User ID, Password or PIN", "OK"); return; case ValidationResult.PASSWORD_EXPIRED: await DisplayAlert("Alert", "Your password has expired - please change it", "OK"); PasswordResetPage passwordResetPage = new PasswordResetPage(App.DBSettings.UserID); passwordResetPage.OnPasswordReset += () => { Task.Run(() => { Thread.Sleep(1500); Device.BeginInvokeOnMainThread(() => { DisplayAlert("Success", "Password changed", "OK"); RunValidate(); }); }); }; Navigation.PushAsync(passwordResetPage); return; case ValidationResult.REQUIRE_2FA: Check2FA(); return; case ValidationResult.VALID: if (ClientFactory.PasswordExpiration != DateTime.MinValue) { var timeUntilExpiration = ClientFactory.PasswordExpiration - DateTime.Now; if (timeUntilExpiration.Days < 14) { string chosenOption = await DisplayActionSheet("Alert", $"Password will expire in {timeUntilExpiration.Days} days. Change password now?", null, "Yes", "No"); switch (chosenOption) { case "Yes": PasswordResetPage passwordResetPage2 = new PasswordResetPage(App.DBSettings.UserID); passwordResetPage2.OnPasswordReset += () => { Task.Run(() => { Thread.Sleep(1500); Device.BeginInvokeOnMainThread(() => { DisplayAlert("Success", "Password changed", "OK"); RunValidate(); }); }); }; Navigation.PushAsync(passwordResetPage2); return; case "No": break; default: break; } } } LaunchMainPage(); return; default: ShowPINPad(); return; } } catch (Exception ex) { string chosenOption = await DisplayActionSheet("Connection error - please check your connection. Try again?", "Cancel", null, "Yes", "No"); switch (chosenOption) { case "Yes": Task.Run(() => { Thread.Sleep(1000); Device.BeginInvokeOnMainThread(() => { RunValidate(); }); }); break; default: break; } } } private async void LaunchMainPage() { if (!App.Current.Properties.ContainsKey("SessionID")) App.Current.Properties.Add("SessionID", ClientFactory.SessionID); else App.Current.Properties["SessionID"] = ClientFactory.SessionID; if (timerRunning) timer.Dispose(); App.Data.Refresh(true); Device.BeginInvokeOnMainThread(async () => { await Navigation.PushAsync(new MainPage()); }); } private async void Check2FA() { try { string address2FA = ""; if (!string.IsNullOrWhiteSpace(ClientFactory.Recipient2FA)) { address2FA = ClientFactory.Recipient2FA; } Show2FAScreen(address2FA); } catch { } } #region 2FA Logic private void Show2FAScreen(string address2FA) { twoFAvisible = true; twoFAGrid.IsVisible = true; SplashLayout.IsVisible = false; PINLayout.IsVisible = false; twoFALbl.Text = address2FA; MasterGrid.ColumnDefinitions[0].Width = new GridLength(0, GridUnitType.Absolute); MasterGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Absolute); MasterGrid.ColumnDefinitions[2].Width = new GridLength(1, GridUnitType.Star); AnimateIcons(); if (Device.RuntimePlatform == Device.iOS) Run2FACheckTimer(); } private void Run2FACheckTimer() { try { timer = new Timer(Callback, null, 4000, 500); timerRunning = true; } catch { } } private void Callback(object state) { try { if (checkingClipBoard) return; checkingClipBoard = true; Device.BeginInvokeOnMainThread(async () => { if (string.IsNullOrWhiteSpace(ent0.Text)) { if (Clipboard.HasText) { string text = await Clipboard.GetTextAsync(); int i = 0; int.TryParse(text, out i); if (i != 0) { if (text.Length != 6) return; else { ent0.Text = text; timer.Dispose(); timer = new Timer(Callback, null, 4000, 500); } } } } checkingClipBoard = false; }); } catch { checkingClipBoard = false; } } private void AnimateIcons() { image1.TranslateTo(-70, 0, 0); image2.TranslateTo(-70, 0, 0); image3.TranslateTo(-70, 0, 0); image4.TranslateTo(-70, 0, 0); image5.TranslateTo(-70, 0, 0); image6.TranslateTo(-70, 0, 0); image7.TranslateTo(-70, 0, 0); image8.TranslateTo(-70, 0, 0); image9.TranslateTo(-70, 0, 0); Task.Run(() => { for (int i = 0; i < 10; i++) { Device.BeginInvokeOnMainThread(() => { image1.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image2.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image3.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image4.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image5.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image6.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image7.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image8.TranslateTo(1200, 0, 30000); }); Thread.Sleep(2500); Device.BeginInvokeOnMainThread(() => { image9.TranslateTo(1200, 0, 30000); }); Thread.Sleep(10000); Device.BeginInvokeOnMainThread(() => { image1.TranslateTo(-70, 0, 0); image2.TranslateTo(-70, 0, 0); image3.TranslateTo(-70, 0, 0); image4.TranslateTo(-70, 0, 0); image5.TranslateTo(-70, 0, 0); image6.TranslateTo(-70, 0, 0); image7.TranslateTo(-70, 0, 0); image8.TranslateTo(-70, 0, 0); image9.TranslateTo(-70, 0, 0); }); } }); } private void ClearBtn_Clicked(object sender, EventArgs e) { ent0.Text = ""; ent0.Focus(); timer.Dispose(); timer = new Timer(Callback, null, 4000, 500); } private async void TwoFAEntry0_Changed(object sender, EventArgs e) { if (autoChange) return; Device.BeginInvokeOnMainThread(async () => { if (Clipboard.HasText) { string clipboard = await Clipboard.GetTextAsync(); if (clipboard.Length == 6 && ent0.Text.Length == 6) { string s = ent0.Text; autoChange = true; ent0.Text = s[0].ToString(); ent1.Text = s[1].ToString(); ent2.Text = s[2].ToString(); ent3.Text = s[3].ToString(); ent4.Text = s[4].ToString(); ent5.Text = s[5].ToString(); ent1.IsEnabled = true; ent2.IsEnabled = true; ent3.IsEnabled = true; ent4.IsEnabled = true; ent5.IsEnabled = true; Animate(true, true, true, true, true, true); ent5.Focus(); autoChange = false; CheckEntries(); return; } } }); if (!string.IsNullOrWhiteSpace(ent0.Text)) { if (ent0.Text.Length == 1) { ent1.IsEnabled = true; ent1.Focus(); Animate(true); } if (ent0.Text.Length == 2) { autoChange = true; string s = ent0.Text; ent0.Text = s[0].ToString(); ent1.Text = s[1].ToString(); ent1.IsEnabled = true; ent2.IsEnabled = true; ent2.Focus(); Animate(true, true); autoChange = false; } if (ent0.Text.Length == 3) { string s = ent0.Text; autoChange = true; ent0.Text = s[0].ToString(); ent1.Text = s[1].ToString(); ent2.Text = s[2].ToString(); ent1.IsEnabled = true; ent2.IsEnabled = true; ent3.IsEnabled = true; Animate(true, true, true); ent3.Focus(); autoChange = false; } if (ent0.Text.Length == 4) { string s = ent0.Text; autoChange = true; ent0.Text = s[0].ToString(); ent1.Text = s[1].ToString(); ent2.Text = s[2].ToString(); ent3.Text = s[3].ToString(); ent1.IsEnabled = true; ent2.IsEnabled = true; ent3.IsEnabled = true; ent4.IsEnabled = true; ent4.Focus(); Animate(true, true, true, true); autoChange = false; } if (ent0.Text.Length == 5) { string s = ent0.Text; autoChange = true; ent0.Text = s[0].ToString(); ent1.Text = s[1].ToString(); ent2.Text = s[2].ToString(); ent3.Text = s[3].ToString(); ent4.Text = s[4].ToString(); ent1.IsEnabled = true; ent2.IsEnabled = true; ent3.IsEnabled = true; ent4.IsEnabled = true; ent5.IsEnabled = true; ent5.Focus(); Animate(true, true, true, true, true); autoChange = false; } if (ent0.Text.Length == 6) { string s = ent0.Text; autoChange = true; ent0.Text = s[0].ToString(); ent1.Text = s[1].ToString(); ent2.Text = s[2].ToString(); ent3.Text = s[3].ToString(); ent4.Text = s[4].ToString(); ent5.Text = s[5].ToString(); ent5.Focus(); ent1.IsEnabled = true; ent2.IsEnabled = true; ent3.IsEnabled = true; ent4.IsEnabled = true; ent5.IsEnabled = true; ent5.Focus(); Animate(true, true, true, true, true, true); autoChange = false; CheckEntries(); } ent0.MaxLength = 1; } else { ent0.MaxLength = 6; ent1.Text = ""; ent1.IsEnabled = false; } } private async void TwoFAEntry1_Changed(object sender, EventArgs e) { if (autoChange) return; if (!string.IsNullOrWhiteSpace(ent1.Text)) { await frame1.TranslateTo(0, -15, 150); frame1.TranslateTo(0, 0, 150); ent2.IsEnabled = true; ent2.Focus(); } else { ent2.IsEnabled = false; ent2.Text = ""; } } private async void TwoFAEntry2_Changed(object sender, EventArgs e) { if (autoChange) return; if (!string.IsNullOrWhiteSpace(ent2.Text)) { await frame2.TranslateTo(0, -15, 150); frame2.TranslateTo(0, 0, 150); ent3.IsEnabled = true; ent3.Focus(); } else { ent3.IsEnabled = false; ent3.Text = ""; } } private async void TwoFAEntry3_Changed(object sender, EventArgs e) { if (autoChange) return; if (!string.IsNullOrWhiteSpace(ent3.Text)) { await frame3.TranslateTo(0, -15, 150); frame3.TranslateTo(0, 0, 150); ent4.IsEnabled = true; ent4.Focus(); } else { ent4.IsEnabled = false; ent4.Text = ""; } } private async void TwoFAEntry4_Changed(object sender, EventArgs e) { if (autoChange) return; if (!string.IsNullOrWhiteSpace(ent4.Text)) { await frame4.TranslateTo(0, -15, 150); frame4.TranslateTo(0, 0, 150); ent5.IsEnabled = true; ent5.Focus(); } else { ent5.IsEnabled = false; ent5.Text = ""; } } private async void TwoFAEntry5_Changed(object sender, EventArgs e) { if (autoChange) return; if (!string.IsNullOrWhiteSpace(ent5.Text)) { Animate(true, true, true, true, true, true); CheckEntries(); } } private void Animate(bool zero = false, bool one = false, bool two = false, bool three = false, bool four = false, bool five = false) { Device.BeginInvokeOnMainThread(async () => { if (zero) { await frame0.TranslateTo(0, -15, 150); frame0.TranslateTo(0, 0, 150); } if (one) { await frame1.TranslateTo(0, -15, 150); frame1.TranslateTo(0, 0, 150); } if (two) { await frame2.TranslateTo(0, -15, 150); frame2.TranslateTo(0, 0, 150); } if (three) { await frame3.TranslateTo(0, -15, 150); frame3.TranslateTo(0, 0, 150); } if (four) { await frame4.TranslateTo(0, -15, 150); frame4.TranslateTo(0, 0, 150); } if (five) { await frame5.TranslateTo(0, -15, 150); frame5.TranslateTo(0, 0, 150); } }); } //only when last entry is filled private void CheckEntries() { Task.Run(() => { Thread.Sleep(750); Device.BeginInvokeOnMainThread(async () => { using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Checking")) { try { string s0 = ent0.Text; string s1 = ent1.Text; string s2 = ent2.Text; string s3 = ent3.Text; string s4 = ent4.Text; string s5 = ent5.Text; string fullstring = s0 + s1 + s2 + s3 + s4 + s5; Device.BeginInvokeOnMainThread(() => { clearBtn.Focus(); }); bool check2FA = ClientFactory.Check2FA(fullstring); if (check2FA) Device.BeginInvokeOnMainThread(() => { LaunchMainPage(); }); else return; } catch (Exception ex) { DisplayAlert("Error with 2FA", ex.Message, "OK"); } } }); }); } #endregion private void CheckForLatestVersion() { Task.Run(async () => { try { bool isLatest = true; try { isLatest = await MobileUtils.AppVersion.IsUsingLatestVersion(); } catch (Exception eLatest) { string s = eLatest.Message; } if (!isLatest) { string latestVersionNumber = await MobileUtils.AppVersion.GetLatestVersionNumber(); Device.BeginInvokeOnMainThread(async () => { string chosenOption = await DisplayActionSheet(String.Format("Version {0} Available. Update now?", latestVersionNumber), "You will be reminded again in 10 minutes.", null, "Yes", "No"); switch (chosenOption) { case "No": break; case "Cancel": break; case "Yes": Dispatcher.BeginInvokeOnMainThread(() => { MobileUtils.AppVersion.OpenAppInStore(); }); break; default: break; } }); } } catch { } }); } protected override void OnDisappearing() { base.OnDisappearing(); } private void ShowSplashPage() { twoFAvisible = false; SplashLayout.IsVisible = true; PINLayout.IsVisible = false; twoFAGrid.IsVisible = false; ToolbarItems.Clear(); ToolbarItems.Add(new ToolbarItem("Settings", "", () => { SettingsPage settingsform = new SettingsPage(); settingsform.Disappearing += Settingsform_Disappearing; Navigation.PushAsync(settingsform); })); MasterGrid.ColumnDefinitions[0].Width = new GridLength(1, GridUnitType.Star); MasterGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Absolute); MasterGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Absolute); } private void ShowPINPad() { PINLayout.IsVisible = true; SplashLayout.IsVisible = false; SplashLayout.IsVisible = false; MasterGrid.ColumnDefinitions[0].Width = new GridLength(0, GridUnitType.Absolute); MasterGrid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star); MasterGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Absolute); ToolbarItems.Clear(); ToolbarItems.Add(new ToolbarItem("Settings", "", () => { SettingsPage settingsform = new SettingsPage(); settingsform.Disappearing += Settingsform_Disappearing; Navigation.PushAsync(settingsform); })); pin = ""; PIN.Text = ""; OK.IsEnabled = false; Back.IsEnabled = false; } void Button_Click(object sender, EventArgs args) { pin = pin + (sender as Button).Text; PIN.Text = new string('*', pin.Length); OK.IsEnabled = true; Back.IsEnabled = true; ; } void Back_Click(object sender, EventArgs args) { // renove the last one if (String.IsNullOrEmpty(pin)) return; pin = pin.Substring(0, pin.Length - 1); PIN.Text = new string('*', pin.Length); OK.IsEnabled = !String.IsNullOrEmpty(pin); Back.IsEnabled = !String.IsNullOrEmpty(pin); } async void OK_Click(object sender, EventArgs args) { RunValidate(true, pin); } void Settings_Click(System.Object sender, System.EventArgs e) { SettingsPage settingsform = new SettingsPage(); settingsform.Disappearing += Settingsform_Disappearing; Navigation.PushAsync(settingsform); } private async void Settingsform_Disappearing(object sender, EventArgs e) { try { if (App.IsUserLoggedIn) await Navigation.PushAsync(new MainPage()); } catch { } } } }