浏览代码

PRS MOBILE - changed login to use multiple connections, adjusted settings page to match

Nick-PRSDigital@bitbucket.org 2 年之前
父节点
当前提交
499c7c8c91

+ 1 - 1
prs.mobile/comal.timesheets/Assignments/AssignmentModuleSettings.cs

@@ -3,7 +3,7 @@ using InABox.Configuration;
 
 namespace comal.timesheets
 {
-    public class AssignmentModuleSettings : LocalConfigurationSettings
+    public class AssignmentModuleSettings : ILocalConfigurationSettings
     {
         public DateTime Date { get; set; }
         public AssignmentView View { get; set; }

+ 5 - 0
prs.mobile/comal.timesheets/Data Classes/GlobalVariables.cs

@@ -19,6 +19,11 @@ namespace comal.timesheets
         public const string CachePortString = "ConnectionSettingsPort";
         public const string CacheUserIDString = "ConnectionSettingsUserID";
         public const string CachePasswordString = "ConnectionSettingsPassword";
+
+        public const string CacheSettingsURL1 = "CacheSettingsURL1";
+        public const string CacheSettingsURL2 = "CacheSettingsURL2";
+        public const string CacheSettingsURL3 = "CacheSettingsURL3";
+
         public static List<ProductShell> ProductShells { get; set; }
 
         public static List<ProductFamily> ProductFamilies { get; set; }

+ 5 - 4
prs.mobile/comal.timesheets/DigitalForms/CustomUserControls/EmbeddedImageCapture.cs

@@ -172,7 +172,8 @@ namespace comal.timesheets
                         CompressionQuality = 15,
                         PhotoSize = Plugin.Media.Abstractions.PhotoSize.MaxWidthHeight,
                         Quality = Plugin.Media.Abstractions.VideoQuality.Low,
-                        DesiredLength = new TimeSpan(0, 0, 20)
+                        DesiredLength = new TimeSpan(0, 0, 20),
+                        RotateImage = false
                     });
 
                     if (file == null)
@@ -257,9 +258,9 @@ namespace comal.timesheets
                     var memoryStream = new MemoryStream();
 
 
-                    if (Device.RuntimePlatform.Equals(Device.Android))
-                        file.GetStream().CopyTo(memoryStream);
-                    else if (Device.RuntimePlatform.Equals(Device.iOS))
+                    //if (Device.RuntimePlatform.Equals(Device.Android))
+                    //    file.GetStream().CopyTo(memoryStream);
+                    //else if (Device.RuntimePlatform.Equals(Device.iOS))
                         file.GetStreamWithImageRotatedForExternalStorage().CopyTo(memoryStream);
 
                     var data = memoryStream.ToArray();

+ 1 - 1
prs.mobile/comal.timesheets/DigitalForms/DigitalFormHost.xaml.cs

@@ -51,7 +51,7 @@ namespace comal.timesheets
 
             Model.OnDigitalFormHostModelSaved += async (responseRequest) =>
             {
-                DisplayAlert("Success", "Form completed " + Model.DigitalFormDataModel.Instance.Form.Description, "OK");
+                DisplayAlert("Success", "Form saved " + Model.DigitalFormDataModel.Instance.Form.Description, "OK");
                 if (responseRequest == DigitalFormHostResponseRequest.CloseKanban)
                 {
                     string chosenOption = await DisplayActionSheet("Input Required", "All forms for this task are complete. Complete this task as well?", null, "Yes", "No");

+ 3 - 1
prs.mobile/comal.timesheets/DigitalForms/Renderer/QAFormViewer.cs

@@ -1237,7 +1237,9 @@ namespace comal.timesheets.QAForms
                 vidField.Properties.Quality == DFLayoutVideoFieldProperties.VideoQuality.Default ? Plugin.Media.Abstractions.VideoQuality.Medium :
                 Plugin.Media.Abstractions.VideoQuality.Medium;
 
-            embeddedImageCapture.VideoLength =
+            embeddedImageCapture.VideoQuality = Plugin.Media.Abstractions.VideoQuality.Low;
+
+           embeddedImageCapture.VideoLength =
                 vidField.Properties.MaximumVideoLength == 0 ? new TimeSpan(0, 0, 20) :
                 new TimeSpan(0, 0, vidField.Properties.MaximumVideoLength);
 

+ 47 - 10
prs.mobile/comal.timesheets/Main/App.xaml.cs

@@ -14,6 +14,9 @@ using XF.Material.Forms.UI;
 using Xamarin.Essentials;
 using SkiaSharp;
 using System.Threading;
+using Java.Util;
+using System.Linq;
+using System.Collections.Generic;
 
 //[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
 namespace comal.timesheets
@@ -40,6 +43,7 @@ namespace comal.timesheets
         public const string MessageOnResume = "OnResume";
 
         public static ConnectionSettings Settings = null;
+        public static DatabaseSettings DBSettings = null;
 
         public App()
         {
@@ -62,22 +66,45 @@ namespace comal.timesheets
 
                 Settings = new LocalConfiguration<ConnectionSettings>().Load();
 
+                DBSettings = new LocalConfiguration<DatabaseSettings>().Load();
 
-                if (string.IsNullOrWhiteSpace(Settings.URL) || Settings.URL == "http://demo.prsdigital.com.au")
+                if (DBSettings.URLs.Count() == 0 || string.IsNullOrWhiteSpace(DBSettings.URLs[0]))
                 {
-                    TryLoadFromSecureCache();
-                    if (String.IsNullOrWhiteSpace(Settings.URL))
-                        MobileUtils.LoadDemoSettings(Settings);
-                }
-                else
-                    MobileUtils.SaveToSecureStorage();
+                    if (string.IsNullOrWhiteSpace(Settings.URL))
+                        TryLoadFromSecureCache();
 
-                if (!string.IsNullOrWhiteSpace(GlobalVariables.LoadFromLinkString))
+                    if (string.IsNullOrWhiteSpace(Settings.URL))
+                        MobileUtils.LoadDemoSettings(DBSettings);
+                    else
+                    {
+                        DBSettings.URLs[0] = Settings.URL + ":" + Settings.Port;
+                        if (Settings.URL.ToUpper().Contains("COM-AL"))
+                        {
+                            List<string> list = new List<string>();
+                            list.Add(DBSettings.URLs[0]);
+                            list.Add("http://remote2.com-al.com.au:" + Settings.Port);
+                            list.Add("http://remote3.com-al.com.au:" + Settings.Port);
+
+                            DBSettings.URLs = list.ToArray();
+                        }
+                        DBSettings.UserID = Settings.UserID;
+                        DBSettings.Password = Settings.Password;
+                    }                    
+                }
+                if (DBSettings.URLs.Count() > 0)
                 {
-                    MobileUtils.LoadFromLink();
+                    new LocalConfiguration<DatabaseSettings>().Save(DBSettings);
+                    MobileUtils.SaveToSecureStorage();
                 }
 
-                ClientFactory.SetClientType(typeof(JsonClient<>), "TimeBench", MobileUtils.AppVersion.InstalledVersionNumber + deviceString, new object[] { Settings.URL, Settings.Port, true });
+                //if (!string.IsNullOrWhiteSpace(GlobalVariables.LoadFromLinkString))
+                //{
+                //    MobileUtils.LoadFromLink();
+                //}
+
+                var result = JsonClient<User>.Ping(DBSettings.URLs, out DatabaseInfo info);
+
+                ClientFactory.SetClientType(typeof(JsonClient<>), "TimeBench", MobileUtils.AppVersion.InstalledVersionNumber + deviceString, result, true);
 
                 GlobalVariables.InternalOnAppearing = true;
                 GlobalVariables.ChangeUser = false;
@@ -185,4 +212,14 @@ namespace comal.timesheets
             IsInForeground = true;
         }
     }
+
+    public class ConnectionSettings : ILocalConfigurationSettings
+    {
+        public string URL { get; set; }
+        public int Port { get; set; }
+        public SerializerProtocol Protocol { get; set; }
+        public string UserID { get; set; }
+        public string Password { get; set; }
+
+    }
 }

+ 2 - 8
prs.mobile/comal.timesheets/Main/MainPage.xaml.cs

@@ -1703,14 +1703,8 @@ namespace comal.timesheets
         {
             try
             {
-                Settings settingsform = new Settings();
-                settingsform.Disappearing += (object sender2, EventArgs e2) =>
-                {
-                    settingsform = (Settings)sender2;
-                    if (settingsform.SettingsChanged)
-                        Navigation.PopModalAsync();
-                };
-                Navigation.PushAsync(settingsform);
+                var page = new SettingsPage(); 
+                Navigation.PushAsync(page);
             }
             catch { }
         }

+ 8 - 9
prs.mobile/comal.timesheets/Main/PINLoginPage.xaml.cs

@@ -91,7 +91,7 @@ namespace comal.timesheets
                         }
                         else
                         {
-                            if (String.IsNullOrWhiteSpace(App.Settings.UserID) || String.IsNullOrWhiteSpace(App.Settings.Password))
+                            if (String.IsNullOrWhiteSpace(App.DBSettings.UserID) || String.IsNullOrWhiteSpace(App.DBSettings.Password))
                             {
                                 DisplayAlert("Unable to log in", "User ID or password is blank", "OK");
                                 ShowPINPad();
@@ -121,7 +121,7 @@ namespace comal.timesheets
                 if (usePIN)
                     result = ClientFactory.Validate(pin, sessionID);
                 else
-                    result = ClientFactory.Validate(App.Settings.UserID, App.Settings.Password, sessionID);
+                    result = ClientFactory.Validate(App.DBSettings.UserID, App.DBSettings.Password, sessionID);
                 switch (result)
                 {
                     case ValidationResult.INVALID:
@@ -130,7 +130,7 @@ namespace comal.timesheets
                         return;
                     case ValidationResult.PASSWORD_EXPIRED:
                         await DisplayAlert("Alert", "Your password has expired - please change it", "OK");
-                        PasswordResetPage passwordResetPage = new PasswordResetPage(App.Settings.UserID);
+                        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;
@@ -147,7 +147,7 @@ namespace comal.timesheets
                                 switch (chosenOption)
                                 {
                                     case "Yes":
-                                        PasswordResetPage passwordResetPage2 = new PasswordResetPage(App.Settings.UserID);
+                                        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;
@@ -680,7 +680,7 @@ namespace comal.timesheets
             ToolbarItems.Clear();
             ToolbarItems.Add(new ToolbarItem("Settings", "", () =>
             {
-                Settings settingsform = new Settings();
+                SettingsPage settingsform = new SettingsPage();
                 settingsform.Disappearing += Settingsform_Disappearing;
                 Navigation.PushAsync(settingsform);
             }));
@@ -700,7 +700,7 @@ namespace comal.timesheets
             ToolbarItems.Clear();
             ToolbarItems.Add(new ToolbarItem("Settings", "", () =>
             {
-                Settings settingsform = new Settings();
+                SettingsPage settingsform = new SettingsPage();
                 settingsform.Disappearing += Settingsform_Disappearing;
                 Navigation.PushAsync(settingsform);
             }));
@@ -737,7 +737,7 @@ namespace comal.timesheets
 
         void Settings_Click(System.Object sender, System.EventArgs e)
         {
-            Settings settingsform = new Settings();
+            SettingsPage settingsform = new SettingsPage();
             settingsform.Disappearing += Settingsform_Disappearing;
             Navigation.PushAsync(settingsform);
         }
@@ -745,8 +745,7 @@ namespace comal.timesheets
         private async void Settingsform_Disappearing(object sender, EventArgs e)
         {
             try
-            {
-                Settings settingsform = (Settings)sender;
+            {       
                 if (App.IsUserLoggedIn)
                     await Navigation.PushAsync(new MainPage());
             }

+ 4 - 3
prs.mobile/comal.timesheets/Main/PasswordResetPage.xaml.cs

@@ -1,4 +1,5 @@
-using InABox.Clients;
+using Comal.Classes;
+using InABox.Clients;
 using InABox.Configuration;
 using InABox.Core;
 using System;
@@ -52,8 +53,8 @@ namespace comal.timesheets
                     user.Password = newPasswordEnt1.Text;
                     new Client<User>().Save(user, "Password updated by User from Timebench");
                     ClientFactory.UnsetBypass();
-                    App.Settings.Password = user.Password;
-                    new LocalConfiguration<ConnectionSettings>().Save(App.Settings);
+                    App.DBSettings.Password = user.Password;
+                    new LocalConfiguration<DatabaseSettings>().Save(App.DBSettings);
                 }
                 DisplayAlert("Success", "Password changed", "OK");
                 Navigation.PopAsync();

+ 2 - 83
prs.mobile/comal.timesheets/Main/Settings.xaml.cs

@@ -25,7 +25,6 @@ namespace comal.timesheets
         public void Load(ConnectionSettings settings)
         {
             URL = settings.URL;
-            Port = settings.Port;
             UserID = settings.UserID;
             Password = settings.Password;
         }
@@ -33,7 +32,6 @@ namespace comal.timesheets
         public void Unload(ConnectionSettings settings)
         {
             settings.URL = URL;
-            settings.Port = Port;
             settings.UserID = UserID;
             settings.Password = Password;
         }
@@ -133,14 +131,6 @@ namespace comal.timesheets
                 sendErrorsBtn.IsEnabled = true;
             }
 
-            //if (Application.Current.Properties.ContainsKey("IsSharedDevice"))
-            //{
-            //    if (Application.Current.Properties["IsSharedDevice"].Equals("True"))
-            //    {
-            //        sharedDeviceRb.IsChecked = true;
-            //    }
-            //}
-
             base.OnAppearing();
         }
 
@@ -198,80 +188,9 @@ namespace comal.timesheets
             if (!string.IsNullOrWhiteSpace(UserID.Text))
             {
                 Password.Text = Password.Text.Trim();
-            }
-            //if (!string.IsNullOrWhiteSpace(Password.Value.ToString()))
-            //{
-            //    Password.Value = Password.Value.ToString().Trim();
-            //    s = Password.Value.ToString();
-            //    showPassword.Text = s;
-            //}
-            //else
-            //    s = "";
-        }
-
-        async void BackgroundLoadProducts()
-        {
-            await Task.Run(() =>
-            {
-                if (!GlobalVariables.ProductsLoaded/* && ClientFactory.IsAllowed<CanViewStockLocations>()*/)
-                {
-                    GlobalVariables.ProductsLoaded = false;
-                    ProductsLoader productsLoader = new ProductsLoader();
-
-                    GlobalVariables.ProductShells = productsLoader.ProductShells;
-                    GlobalVariables.ProductsLoaded = true;
-                }
-            });
+            }          
         }
 
-        private void sharedDeviceRb_Changed(object sender, EventArgs e)
-        {
-            //string isSharedDevice = "IsSharedDevice";
-            //if ((sender as CheckBox).IsChecked)
-            //{
-            //    if (!Application.Current.Properties.ContainsKey(isSharedDevice))
-            //    {
-            //        Application.Current.Properties.Add(isSharedDevice, "True");
-            //        URL.Text = "http://mobile.com-al.com.au";
-            //        UserID.Text = "";
-            //        Password.Value = "";
-            //    }
-            //    else
-            //    {
-            //        Application.Current.Properties[isSharedDevice] = "True";
-            //        URL.Text = "http://mobile.com-al.com.au";
-            //        UserID.Text = "";
-            //        Password.Value = "";
-            //    }
-            //}
-            //else
-            //{
-            //    if (Application.Current.Properties.ContainsKey(isSharedDevice))
-            //    {
-            //        Application.Current.Properties[isSharedDevice] = "False";
-            //    }
-            //}
-        }
-
-        private void ShowPW_Tapped(object sender, EventArgs e)
-        {
-            //passwordOne.Width = new GridLength(0, GridUnitType.Absolute);
-            //passwordTwo.Width = new GridLength(1, GridUnitType.Star);
-            //Task.Run(() =>
-            //{
-            //    Thread.Sleep(2500);
-            //    ShowMaskedText();
-            //});
-        }
-
-        private void ShowMaskedText()
-        {
-            //Device.BeginInvokeOnMainThread(() =>
-            //{
-            //    passwordTwo.Width = new GridLength(0, GridUnitType.Absolute);
-            //    passwordOne.Width = new GridLength(1, GridUnitType.Star);
-            //});
-        }
 
         private async void ClearStorageBtn_Clicked(object sender, EventArgs e)
         {
@@ -310,7 +229,7 @@ namespace comal.timesheets
             try
             {
                 new LocalConfiguration<ConnectionSettings>().Delete();
-                MobileUtils.LoadDemoSettings(App.Settings);
+                MobileUtils.LoadDemoSettings(App.DBSettings);
                 binding.Load(App.Settings);
             }
             catch { }

+ 78 - 0
prs.mobile/comal.timesheets/SettingsPage.xaml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:timesheets="clr-namespace:comal.timesheets"
+             x:Class="comal.timesheets.SettingsPage">
+
+    <NavigationPage.TitleView>
+        <Grid Margin="0" Padding="0">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="*"/>
+                <ColumnDefinition Width="*"/>
+                <ColumnDefinition Width="*"/>
+            </Grid.ColumnDefinitions>
+            <Button Grid.Column="0" HorizontalOptions="Start" VerticalOptions="Center" TextColor="White" BackgroundColor="Transparent" Margin="0" Padding="0"
+                Text="Exit" Clicked="ExitBtn_Clicked"/>
+            <Label Grid.Column="1"  Text="Settings" VerticalOptions="Center" x:Name="titleLbl"
+                   HorizontalOptions="Center" HorizontalTextAlignment="Center" TextColor="White" FontSize="Medium" FontAttributes="Bold"/>
+            <Button Grid.Column="2" HorizontalOptions="End" VerticalOptions="Center" TextColor="White" BackgroundColor="Transparent" Margin="0" Padding="0"
+                Text="Save" Clicked="SaveBtn_Clicked"/>
+        </Grid>
+    </NavigationPage.TitleView>
+    <ContentPage.Content>
+        <ScrollView>
+            <StackLayout>
+                
+                <Frame HorizontalOptions="FillAndExpand" Margin="5" Padding="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto"/>
+                            <RowDefinition Height="auto"/>
+                            <RowDefinition Height="auto"/>
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="auto"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Label Grid.Row="0" Grid.Column="0" Text="URLs:" HorizontalOptions="Start" VerticalOptions="Center"/>
+                        <Label Grid.Row="1" Grid.Column="0" Text="User ID:" HorizontalOptions="Start" VerticalOptions="Center"/>
+                        <Label Grid.Row="2" Grid.Column="0" Text="Password:" HorizontalOptions="Start" VerticalOptions="Center"/>
+                        
+                        <timesheets:StringList Grid.Row="0" Grid.Column="1" HorizontalOptions="Fill" x:Name="stringList" VerticalOptions="Start"/>
+                        <Entry Grid.Row="1" Grid.Column="1" HorizontalOptions="Fill" x:Name="userIDEnt" VerticalOptions="Start" FontSize="16"/>
+                        <Entry Grid.Row="2" Grid.Column="1" HorizontalOptions="Fill" x:Name="passwordEnt" VerticalOptions="Start" FontSize="16"/>
+                    </Grid>
+                </Frame>
+
+                <Frame HorizontalOptions="FillAndExpand" Margin="5" Padding="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto"/>
+                            <RowDefinition Height="auto"/>
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="auto"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Label Grid.Row="0" Grid.Column="0" Text="Device ID:" HorizontalOptions="Start" VerticalOptions="Center"/>
+                        <Label Grid.Row="1" Grid.Column="0" Text="App Version:" HorizontalOptions="Start" VerticalOptions="Center"/>
+
+                        <Entry Grid.Row="0" Grid.Column="1" HorizontalOptions="Fill" IsEnabled="False" x:Name="deviceIDEnt" VerticalOptions="Start" FontSize="16"/>
+                        <Entry Grid.Row="1" Grid.Column="1" HorizontalOptions="Fill" IsEnabled="False" x:Name="appVersionEnt" VerticalOptions="Start" FontSize="16"/>
+                    </Grid>
+                </Frame>
+
+                <Button x:Name="updateVersionBtn" IsEnabled="False" Clicked="UpdateVersionBtn_Clicked" Margin="5"
+               Text="Checking Version" Padding="3" FontSize="Medium" TextColor="White" BackgroundColor="#15C7C1" FontAttributes="Bold" CornerRadius="10"/>
+
+                <Button x:Name="sendErrorsBtn" IsEnabled="False" Clicked="SendErrorsBtn_Clicked" VerticalOptions="Start" Margin="5"
+               Text="Email Error Log" Padding="3" FontSize="Medium" TextColor="White" BackgroundColor="#15C7C1" FontAttributes="Bold" CornerRadius="10"/>
+
+                <Button x:Name="changePasswordBtn" Clicked="ChangePasswordBtn_Clicked" IsVisible="False" Margin="5"
+               Text="Change Password" Padding="3" FontSize="Medium" TextColor="White" BackgroundColor="#15C7C1" FontAttributes="Bold" CornerRadius="10"/>
+                
+            </StackLayout>
+        </ScrollView>
+    </ContentPage.Content>
+</ContentPage>

+ 212 - 0
prs.mobile/comal.timesheets/SettingsPage.xaml.cs

@@ -0,0 +1,212 @@
+using InABox.Clients;
+using InABox.Configuration;
+using InABox.Mobile;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Xamarin.Essentials;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using XF.Material.Forms.UI.Dialogs;
+using Comal.Classes;
+using Email = Xamarin.Essentials.Email;
+using InABox.Core;
+using System.Linq;
+
+namespace comal.timesheets
+{
+    [XamlCompilation(XamlCompilationOptions.Compile)]
+    public partial class SettingsPage : ContentPage
+    {
+        private int count;
+
+        public SettingsPage()
+        {
+            InitializeComponent();
+
+            NavigationPage.SetHasBackButton(this, false);
+
+            Populate();
+        }
+
+        private void ExitBtn_Clicked(object sender, EventArgs e)
+        {
+            Navigation.PopAsync();
+        }
+
+        private void SaveBtn_Clicked(object sender, EventArgs e)
+        {
+            ValidateData();
+        }
+
+        private async void ValidateData()
+        {
+            try
+            {
+                App.DBSettings.URLs = stringList.SaveItems();
+                App.DBSettings.UserID = userIDEnt.Text.Trim();
+                App.DBSettings.Password = passwordEnt.Text.Trim();
+            }
+            catch { }
+
+            try
+            {
+                using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Validating"))
+                {
+                    ClientFactory.InvalidateUser();
+                    new LocalConfiguration<DatabaseSettings>().Delete();
+                    new LocalConfiguration<DatabaseSettings>().Save(App.DBSettings);
+                    App.DBSettings = new LocalConfiguration<DatabaseSettings>().Load();
+
+                    if (App.Current.Properties.ContainsKey("SessionID"))
+                        App.Current.Properties.Remove("SessionID");
+
+                    var result = JsonClient<User>.Ping(App.DBSettings.URLs, out DatabaseInfo info);
+
+                    ClientFactory.SetClientType(typeof(JsonClient<>), "TimeBench", MobileUtils.AppVersion.InstalledVersionNumber + GlobalVariables.DeviceString, result, true);
+
+                    GlobalVariables.ChangeUser = true;
+                    GlobalVariables.InternalOnAppearing = true;
+
+                    Navigation.PopToRootAsync(true);
+                    PINLoginPage pin = new PINLoginPage();
+                    Navigation.PushAsync(pin, true);
+                }
+                return;
+            }
+            catch (Exception ex)
+            {
+                DisplayAlert("Error", ex.Message, "OK");
+            }
+            Navigation.PopAsync();
+        }
+
+        #region Populate Screen
+
+        private void Populate()
+        {
+            try
+            {
+                stringList.LoadList(App.DBSettings.URLs);
+                userIDEnt.Text = App.DBSettings.UserID;
+                passwordEnt.Text = App.DBSettings.Password;
+
+                deviceIDEnt.Text = MobileUtils.GetDeviceID();
+                appVersionEnt.Text = MobileUtils.AppVersion.InstalledVersionNumber;
+            }
+            catch { }
+        }
+
+        protected override async void OnAppearing()
+        {
+            bool isLatest = true;
+            try
+            {
+                isLatest = await MobileUtils.AppVersion.IsUsingLatestVersion();
+            }
+            catch (Exception eLatest)
+            {
+
+            }
+            if (isLatest)
+            {
+                updateVersionBtn.IsEnabled = false;
+                updateVersionBtn.Text = "App is up to date";
+            }
+            else if (!isLatest)
+            {
+                string latestVersionNumber = await MobileUtils.AppVersion.GetLatestVersionNumber();
+                updateVersionBtn.IsEnabled = true;
+                updateVersionBtn.Text = "Update App Version (" + latestVersionNumber + ")";
+            }
+
+            const string errorFilename = "Fatal.log";
+            var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
+            var errorFilePath = Path.Combine(libraryPath, errorFilename);
+
+            if (File.Exists(errorFilePath))
+            {
+                sendErrorsBtn.IsEnabled = true;
+            }
+
+            base.OnAppearing();
+        }
+        #endregion
+
+
+        #region Buttons
+        private async void SendErrorsBtn_Clicked(object sender, EventArgs e)
+        {
+            try
+            {
+                const string errorFilename = "Fatal.log";
+                string libraryPath = "";
+                if (Device.RuntimePlatform.Equals(Device.Android))
+                    libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
+                else if (Device.RuntimePlatform.Equals(Device.iOS))
+                    libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Resources);
+
+                var errorFilePath = Path.Combine(libraryPath, errorFilename);
+
+                if (!File.Exists(errorFilePath))
+                {
+                    return;
+                }
+
+                var errorText = File.ReadAllText(errorFilePath);
+
+                var message = new EmailMessage
+                {
+                    Subject = "Error Logs from " + GlobalVariables.EmpName,
+                    Body = errorText,
+                    To = new List<string> { "support@prsdigital.com.au" }
+                };
+
+                await Email.ComposeAsync(message);
+
+                File.Delete(errorFilePath);
+            }
+            catch { }
+        }
+        private void UpdateVersionBtn_Clicked(Object sender, EventArgs e)
+        {
+            Dispatcher.BeginInvokeOnMainThread(() => { MobileUtils.AppVersion.OpenAppInStore(); });
+        }
+        private async void ChangePasswordBtn_Clicked(object sender, EventArgs e)
+        {
+            if (count == 3)
+            {
+                DisplayAlert("Alert", "You have attempted incorrectly " + count + " times and cannot try again. Please contact your system administrator.", "OK");
+                return;
+            }
+            CoreTable table = new Client<User>().Query(new Filter<User>(x => x.UserID).IsEqualTo(ClientFactory.UserID),
+                new Columns<User>(x => x.Password));
+            if (table.Rows.Any())
+            {
+                string p = table.Rows.FirstOrDefault().Values[0].ToString();
+                string userInput = await DisplayPromptAsync("Alert", "Enter Current Password", "OK", "Cancel");
+                if (!string.IsNullOrWhiteSpace(userInput) && userInput != "Cancel")
+                {
+                    if (p != userInput)
+                    {
+                        count++;
+                        DisplayAlert("Alert", "Password is incorrect. You have attempted " + count + " times out of 3.", "OK");
+                        return;
+                    }
+                    else if (p == userInput)
+                    {
+                        PasswordResetPage page = new PasswordResetPage(ClientFactory.UserID);
+                        page.OnPasswordReset += (() =>
+                        {
+                            Navigation.PopToRootAsync();
+                            PINLoginPage pinpage = new PINLoginPage();
+                            Navigation.PushAsync(pinpage);
+                        });
+                        Navigation.PushAsync(page);
+                    }
+                }
+            }
+        }
+        #endregion
+    }
+}

+ 19 - 0
prs.mobile/comal.timesheets/StringList.xaml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
+             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+             x:Class="comal.timesheets.StringList">
+    <ContentView.Content>
+        <Frame Margin="0" Padding="2" CornerRadius="3" BorderColor="LightGray">
+            <Grid>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="auto"/>
+                    <RowDefinition Height="auto"/>
+                </Grid.RowDefinitions>
+
+                <StackLayout Grid.Row="0" x:Name="stackLayout" VerticalOptions="CenterAndExpand"/>
+
+                <Button Grid.Row="1" Text="Add" HorizontalOptions="Center" Clicked="AddBtn_Clicked"/>
+            </Grid>
+        </Frame>
+    </ContentView.Content>
+</ContentView>

+ 95 - 0
prs.mobile/comal.timesheets/StringList.xaml.cs

@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using static Android.Content.ClipData;
+
+namespace comal.timesheets
+{
+    [XamlCompilation(XamlCompilationOptions.Compile)]
+    public partial class StringList : ContentView
+    {
+        public StringList()
+        {
+            InitializeComponent();
+        }
+
+        private void AddBtn_Clicked(object sender, EventArgs e)
+        {
+            stackLayout.Children.Add(
+                CreateEditor()
+                );
+        }
+
+        private DeleteableEditor CreateEditor(string text = "")
+        {
+            DeleteableEditor editor = new DeleteableEditor(text);
+            editor.OnEditorDeleted += () => { stackLayout.Children.Remove(editor); };
+            return editor;
+        }
+
+        public void LoadList(string[] items)
+        {
+            foreach (var item in items)
+            {
+                stackLayout.Children.Add(CreateEditor(item));
+            }
+        }
+
+        public string[] SaveItems()
+        {
+            List<string> items = new List<string>();
+            foreach (DeleteableEditor entry in stackLayout.Children)
+            {
+                if (!string.IsNullOrEmpty(entry.Text))
+                    items.Add(entry.Text);
+            }
+            return items.ToArray();
+        }
+    }
+
+    public delegate void EditorDeleted();
+
+    public class DeleteableEditor : Grid
+    {
+        public event EditorDeleted OnEditorDeleted;
+        public string Text { get; set; }
+        public DeleteableEditor(string text = "")
+        {
+            Text = text;
+            Setup();
+        }
+
+        private void Setup()
+        {
+            ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
+            ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
+
+            var edt = new Entry();
+            edt.Text = Text;
+            edt.FontSize = 16;
+            edt.TextChanged += (sender, e) =>
+            {
+                Text = edt.Text;
+            };
+            Grid.SetColumn(edt, 0);
+
+            Image img = new Image { Source = "closee.png", HeightRequest = 20, WidthRequest = 20 };
+            img.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(Close) });
+            Grid.SetColumn(img, 1);
+
+            Children.Add(edt);
+            Children.Add(img);
+        }
+
+
+        private void Close(object obj)
+        {
+            OnEditorDeleted?.Invoke();
+        }
+    }
+}

+ 18 - 0
prs.mobile/comal.timesheets/comal.timesheets.projitems

@@ -240,6 +240,9 @@
     </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)Grids\JobFormsGrid.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Grids\KanbanGrid.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)SettingsPage.xaml.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)Site\JobDocFilterItem.xaml.cs">
       <DependentUpon>JobDocFilterItem.xaml</DependentUpon>
       <SubType>Code</SubType>
@@ -357,6 +360,9 @@
     <Compile Include="$(MSBuildThisFileDirectory)DataGrid\Views\Templates\ViewCell4ColumnsWithImage.xaml.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="$(MSBuildThisFileDirectory)StringList.xaml.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)ViewCellFrame.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Warehousing\Receivals\Receivals.xaml.cs">
       <DependentUpon>Receivals.xaml</DependentUpon>
@@ -1132,4 +1138,16 @@
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="$(MSBuildThisFileDirectory)SettingsPage.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="$(MSBuildThisFileDirectory)StringList.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+    </EmbeddedResource>
+  </ItemGroup>
 </Project>