From e660c0dc743474f560370981f4a444402c743044 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Wed, 3 Aug 2022 11:09:09 +0100 Subject: [PATCH 1/4] Support Automatic Updating --- ...ransactionMobile.Maui.BusinessLogic.csproj | 5 + .../UIServices/IDialogService.cs | 44 ++++++ .../ViewModels/LoginPageViewModel.cs | 75 +++++++++ .../Features/Login.feature.cs | 22 +-- TransactionMobile.Maui/App.xaml.cs | 4 + .../Extensions/MauiAppBuilderExtensions.cs | 7 +- .../Pages/AppHome/HomePage.xaml.cs | 22 ++- .../MobileTopupSelectProductPage.xaml.cs | 1 + .../TransactionMobile.Maui.csproj | 4 + .../UIServices/DialogService.cs | 144 +++++++++++------- 10 files changed, 257 insertions(+), 71 deletions(-) create mode 100644 TransactionMobile.Maui.BusinessLogic/UIServices/IDialogService.cs diff --git a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj index 22430ca4..e4c43f7e 100644 --- a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj +++ b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj @@ -18,6 +18,7 @@ + @@ -28,6 +29,10 @@ + + + + diff --git a/TransactionMobile.Maui.BusinessLogic/UIServices/IDialogService.cs b/TransactionMobile.Maui.BusinessLogic/UIServices/IDialogService.cs new file mode 100644 index 00000000..bf68ee55 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/UIServices/IDialogService.cs @@ -0,0 +1,44 @@ +namespace TransactionMobile.Maui.BusinessLogic.UIServices; + +public interface IDialogService +{ + #region Methods + + Task ShowDialog(String title, + String message, + String acceptString); + + Task ShowDialog(String title, + String message, + String acceptString, + String cancelString); + + Task ShowErrorToast(String message, + Action? action, + String? actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken); + + Task ShowInformationToast(String message, + Action? action, + String? actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken); + + Task ShowPrompt(String title, + String message, + String acceptString, + String cancelString, + String placeHolder = "", + Int32 maxLength = -1, + Keyboard keyboard = null, + String initialValue = ""); + + Task ShowWarningToast(String message, + Action? action, + String? actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken); + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs index 39f9430c..73dea8df 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs @@ -3,6 +3,10 @@ using System.Windows.Input; using Maui.UIServices; using MediatR; + using Microsoft.AppCenter; + using Microsoft.AppCenter.Analytics; + using Microsoft.AppCenter.Crashes; + using Microsoft.AppCenter.Distribute; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; using Models; @@ -125,6 +129,7 @@ private async Task LoginCommandExecute() this.MemoryCacheService.Set("IsLoggedIn", true); await this.NavigationService.GoToHome(); + } private async void AccessTokenExpired(Object key, @@ -163,4 +168,74 @@ private void CacheAccessToken(TokenResponseModel token) #endregion } + + public class HomePageViewModel : BaseViewModel + { + private readonly IMemoryCacheService MemoryCacheService; + + private readonly IDialogService DialogService; + + public HomePageViewModel(IMemoryCacheService memoryCacheService, + IDialogService dialogService) { + this.MemoryCacheService = memoryCacheService; + this.DialogService = dialogService; + } + + public async Task Initialise(CancellationToken cancellationToken) { + + this.MemoryCacheService.TryGetValue("Configuration", out Configuration configuration); + + if (configuration.EnableAutoUpdates) { + await Distribute.SetEnabledAsync(true); + Distribute.CheckForUpdate(); + Distribute.ReleaseAvailable = OnReleaseAvailable; + Distribute.UpdateTrack = UpdateTrack.Public; + } + else { + Distribute.DisableAutomaticCheckForUpdate(); + } + + AppCenter.Start("android=f920cc96-de56-42fe-87d4-b49105761205;"+ + "ios=dd940171-ca8c-4219-9851-f83769464f37" + + "uwp=3ad27ea3-3f24-4579-a88a-530025bd00d4;" + + "macos=244fdee2-f897-431a-8bab-5081fc90b329", typeof(Analytics), typeof(Crashes), typeof(Distribute)); + } + + private Boolean OnReleaseAvailable(ReleaseDetails releaseDetails) { + // Look at releaseDetails public properties to get version information, release notes text or release notes URL + String versionName = releaseDetails.ShortVersion; + String versionCodeOrBuildNumber = releaseDetails.Version; + String releaseNotes = releaseDetails.ReleaseNotes; + Uri releaseNotesUrl = releaseDetails.ReleaseNotesUrl; + + // custom dialog + String title = "Version " + versionName + " available!"; + Task answer; + + // On mandatory update, user can't postpone + if (releaseDetails.MandatoryUpdate) { + answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install"); + } + else { + answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install", "Later"); + } + + answer.ContinueWith(task => { + // If mandatory or if answer was positive + if (releaseDetails.MandatoryUpdate || (task as Task).Result) { + // Notify SDK that user selected update + Distribute.NotifyUpdateAction(UpdateAction.Update); + } + else { + // Notify SDK that user selected postpone (for 1 day) + // This method call is ignored by the SDK if the update is mandatory + Distribute.NotifyUpdateAction(UpdateAction.Postpone); + } + }); + + // Return true if you're using your own dialog, false otherwise + return true; + } + + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.UiTests/Features/Login.feature.cs b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs index 40b52487..33446da6 100644 --- a/TransactionMobile.Maui.UiTests/Features/Login.feature.cs +++ b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs @@ -85,14 +85,12 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Login as Merchant")] - [NUnit.Framework.CategoryAttribute("PRTest")] public void LoginAsMerchant() { - string[] tagsOfScenario = new string[] { - "PRTest"}; + string[] tagsOfScenario = ((string[])(null)); System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Login as Merchant", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 7 +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) @@ -105,22 +103,22 @@ public void LoginAsMerchant() #line 4 this.FeatureBackground(); #line hidden -#line 8 +#line 7 testRunner.Given("I am on the Login Screen", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line hidden -#line 9 +#line 8 testRunner.And("the application is in training mode", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden -#line 10 +#line 9 testRunner.When("I enter \'merchantuser@testmerchant1.co.uk\' as the Email Address", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line hidden -#line 11 +#line 10 testRunner.And("I enter \'123456\' as the Password", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden -#line 12 +#line 11 testRunner.And("I tap on Login", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden -#line 13 +#line 12 testRunner.Then("the Merchant Home Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden } @@ -129,9 +127,11 @@ public void LoginAsMerchant() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Logout")] + [NUnit.Framework.CategoryAttribute("PRTest")] public void Logout() { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = new string[] { + "PRTest"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Logout", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 15 diff --git a/TransactionMobile.Maui/App.xaml.cs b/TransactionMobile.Maui/App.xaml.cs index 12cb5d6a..47b85309 100644 --- a/TransactionMobile.Maui/App.xaml.cs +++ b/TransactionMobile.Maui/App.xaml.cs @@ -6,6 +6,10 @@ namespace TransactionMobile.Maui; using BusinessLogic.ViewModels; +using Microsoft.AppCenter; +using Microsoft.AppCenter.Analytics; +using Microsoft.AppCenter.Crashes; +using Microsoft.AppCenter.Distribute; using Microsoft.Maui.Handlers; using Pages; using Pages.AppHome; diff --git a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs index 3640565e..407352e4 100644 --- a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs +++ b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs @@ -26,6 +26,7 @@ using TransactionMobile.Maui.Pages.Support; using System; using BusinessLogic.ViewModels.MyAccount; + using Pages.AppHome; using Pages.MyAccount; public static class MauiAppBuilderExtensions @@ -182,7 +183,7 @@ public static MauiAppBuilder ConfigureTrainingServices(this MauiAppBuilder build public static MauiAppBuilder ConfigureUIServices(this MauiAppBuilder builder) { - // builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); return builder; @@ -232,6 +233,8 @@ public static MauiAppBuilder ConfigureViewModels(this MauiAppBuilder builder) builder.Services.AddTransient(); + builder.Services.AddTransient(); + return builder; } @@ -258,6 +261,8 @@ public static MauiAppBuilder ConfigurePages(this MauiAppBuilder builder) builder.Services.AddTransient(); + builder.Services.AddTransient(); + return builder; } diff --git a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs index eb959471..ef3fbcef 100644 --- a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs +++ b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs @@ -1,13 +1,25 @@ namespace TransactionMobile.Maui.Pages.AppHome; +using BusinessLogic.ViewModels; +using Microsoft.AppCenter; +using Microsoft.AppCenter.Analytics; +using Microsoft.AppCenter.Crashes; +using Microsoft.AppCenter.Distribute; + public partial class HomePage : ContentPage { - public HomePage() - { - InitializeComponent(); - } + private HomePageViewModel viewModel => BindingContext as HomePageViewModel; + + public HomePage(HomePageViewModel vm) + { + InitializeComponent(); + BindingContext = vm; + } - protected override void OnAppearing() { + protected override async void OnAppearing() + { base.OnAppearing(); + await this.viewModel.Initialise(CancellationToken.None); } + } \ No newline at end of file diff --git a/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs b/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs index 0ff595db..937c3783 100644 --- a/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs +++ b/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs @@ -19,6 +19,7 @@ protected override async void OnAppearing() base.OnAppearing(); await viewModel.Initialise(CancellationToken.None); this.LoadProducts(viewModel); + } private void LoadProducts(MobileTopupSelectProductPageViewModel viewModel) diff --git a/TransactionMobile.Maui/TransactionMobile.Maui.csproj b/TransactionMobile.Maui/TransactionMobile.Maui.csproj index e14274ac..a639408d 100644 --- a/TransactionMobile.Maui/TransactionMobile.Maui.csproj +++ b/TransactionMobile.Maui/TransactionMobile.Maui.csproj @@ -55,6 +55,10 @@ + + + + diff --git a/TransactionMobile.Maui/UIServices/DialogService.cs b/TransactionMobile.Maui/UIServices/DialogService.cs index 022f159b..e4b1cf74 100644 --- a/TransactionMobile.Maui/UIServices/DialogService.cs +++ b/TransactionMobile.Maui/UIServices/DialogService.cs @@ -6,58 +6,94 @@ namespace TransactionMobile.Maui.UIServices { - //using CommunityToolkit.Maui.Alerts; - //using CommunityToolkit.Maui.Core; - - //public interface IDialogService - //{ - // Task ShowInfoToast(String message, - // Action? action, - // String? actionButtonText, - // TimeSpan? duration, - // CancellationToken cancellationToken); - //} - - //public class DialogService : IDialogService - //{ - // public async Task ShowInfoToast(String message, Action? action, String? actionButtonText, TimeSpan? duration, CancellationToken cancellationToken) - // { - // await Application.Current.MainPage.DisplaySnackbar(message, - // action, - // actionButtonText, - // duration, - // SnackBarOptionsHelper.GetInfoSnackbarOptions()); - // } - //} - - //public static class SnackBarOptionsHelper - //{ - // public static SnackbarOptions GetInfoSnackbarOptions() - // { - // return new SnackbarOptions - // { - // BackgroundColor = Colors.Blue, - // TextColor = Colors.White - // }; - // } - - // public static SnackbarOptions GetWarningSnackbarOptions() - // { - // return new SnackbarOptions - // { - // BackgroundColor = Colors.Orange, - // TextColor = Colors.White - // }; - // } - - // public static SnackbarOptions GetErrorSnackbarOptions() - // { - // return new SnackbarOptions - // { - // BackgroundColor = Colors.Red, - // TextColor = Colors.White - // }; - // } - //} - + using BusinessLogic.UIServices; + using CommunityToolkit.Maui.Alerts; + using CommunityToolkit.Maui.Core; + + public class DialogService : IDialogService + { + public async Task ShowInformationToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { + await Application.Current.MainPage.DisplaySnackbar(message, + action, + actionButtonText, + duration, + SnackBarOptionsHelper.GetInfoSnackbarOptions, + cancellationToken); + } + + public async Task ShowWarningToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { + await Application.Current.MainPage.DisplaySnackbar(message, + action, + actionButtonText, + duration, + SnackBarOptionsHelper.GetInfoSnackbarOptions, + cancellationToken); + } + + public async Task ShowErrorToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { + await Application.Current.MainPage.DisplaySnackbar(message, + action, + actionButtonText, + duration, + SnackBarOptionsHelper.GetInfoSnackbarOptions, + cancellationToken); + } + + public async Task ShowDialog(String title, + String message, + String cancelString) { + + await Application.Current.MainPage.DisplayAlert(title, message, cancelString); + } + public async Task ShowDialog(String title, + String message, + String acceptString, + String cancelString) + { + return await Application.Current.MainPage.DisplayAlert(title, message, acceptString, cancelString); + } + + public async Task ShowPrompt(String title, + String message, + String acceptString, + String cancelString, + String placeHolder = "", + Int32 maxLength = -1, + Keyboard keyboard = null, + String initialValue = "") { + return await Application.Current.MainPage.DisplayPromptAsync(title, message, acceptString, cancelString, placeHolder, maxLength,keyboard,initialValue); + } + } + + public static class SnackBarOptionsHelper + { + public static SnackbarOptions GetInfoSnackbarOptions => new SnackbarOptions { + BackgroundColor = Colors.Blue, + TextColor = Colors.White + }; + + public static SnackbarOptions GetWarningSnackbarOptions => new SnackbarOptions + { + BackgroundColor = Colors.Orange, + TextColor = Colors.White + }; + + public static SnackbarOptions GetErrorSnackbarOptions => new SnackbarOptions + { + BackgroundColor = Colors.Red, + TextColor = Colors.White, + }; + } } From 5b0365e8ef8b9a86274ec3929717d4cb4e0bc03a Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Thu, 4 Aug 2022 10:28:19 +0100 Subject: [PATCH 2/4] Disable app centre on iOS --- .../TransactionMobile.Maui.BusinessLogic.csproj | 7 ++----- .../ViewModels/LoginPageViewModel.cs | 16 ++++++++++------ TransactionMobile.Maui/App.xaml.cs | 4 ---- .../Pages/AppHome/HomePage.xaml.cs | 4 ---- .../TransactionMobile.Maui.csproj | 6 ++---- 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj index e4c43f7e..524cd17e 100644 --- a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj +++ b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj @@ -18,7 +18,6 @@ - @@ -29,10 +28,8 @@ - - - - + + diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs index 73dea8df..1e14a399 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs @@ -4,11 +4,10 @@ using Maui.UIServices; using MediatR; using Microsoft.AppCenter; - using Microsoft.AppCenter.Analytics; - using Microsoft.AppCenter.Crashes; using Microsoft.AppCenter.Distribute; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; + using Microsoft.Maui.Devices; using Models; using MvvmHelpers; using MvvmHelpers.Commands; @@ -195,12 +194,17 @@ public async Task Initialise(CancellationToken cancellationToken) { Distribute.DisableAutomaticCheckForUpdate(); } - AppCenter.Start("android=f920cc96-de56-42fe-87d4-b49105761205;"+ - "ios=dd940171-ca8c-4219-9851-f83769464f37" + - "uwp=3ad27ea3-3f24-4579-a88a-530025bd00d4;" + - "macos=244fdee2-f897-431a-8bab-5081fc90b329", typeof(Analytics), typeof(Crashes), typeof(Distribute)); + if (this.IsIOS() == false) { + // TODO: Move the keys to config service + AppCenter.Configure("android=f920cc96-de56-42fe-87d4-b49105761205;" + "ios=dd940171-ca8c-4219-9851-f83769464f37;" + + "uwp=3ad27ea3-3f24-4579-a88a-530025bd00d4;" + "macos=244fdee2-f897-431a-8bab-5081fc90b329;"); + AppCenter.Start(typeof(Distribute)); + } } + private bool IsIOS() => + DeviceInfo.Current.Platform == DevicePlatform.iOS; + private Boolean OnReleaseAvailable(ReleaseDetails releaseDetails) { // Look at releaseDetails public properties to get version information, release notes text or release notes URL String versionName = releaseDetails.ShortVersion; diff --git a/TransactionMobile.Maui/App.xaml.cs b/TransactionMobile.Maui/App.xaml.cs index 47b85309..12cb5d6a 100644 --- a/TransactionMobile.Maui/App.xaml.cs +++ b/TransactionMobile.Maui/App.xaml.cs @@ -6,10 +6,6 @@ namespace TransactionMobile.Maui; using BusinessLogic.ViewModels; -using Microsoft.AppCenter; -using Microsoft.AppCenter.Analytics; -using Microsoft.AppCenter.Crashes; -using Microsoft.AppCenter.Distribute; using Microsoft.Maui.Handlers; using Pages; using Pages.AppHome; diff --git a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs index ef3fbcef..ebb6cafa 100644 --- a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs +++ b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs @@ -1,10 +1,6 @@ namespace TransactionMobile.Maui.Pages.AppHome; using BusinessLogic.ViewModels; -using Microsoft.AppCenter; -using Microsoft.AppCenter.Analytics; -using Microsoft.AppCenter.Crashes; -using Microsoft.AppCenter.Distribute; public partial class HomePage : ContentPage { diff --git a/TransactionMobile.Maui/TransactionMobile.Maui.csproj b/TransactionMobile.Maui/TransactionMobile.Maui.csproj index a639408d..7eebd4ce 100644 --- a/TransactionMobile.Maui/TransactionMobile.Maui.csproj +++ b/TransactionMobile.Maui/TransactionMobile.Maui.csproj @@ -55,10 +55,8 @@ - - - - + + From 12f8ef07a2f66ef0869277f6681533c40706cd30 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Thu, 4 Aug 2022 10:55:07 +0100 Subject: [PATCH 3/4] Fix manifest file --- .../Platforms/Android/AndroidManifest.xml | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml b/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml index 5a333cd1..e7c3c589 100644 --- a/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml +++ b/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml @@ -1,6 +1,20 @@  - - - - - + + + + + + + + + + + + + \ No newline at end of file From c4771cd66ba17a7c5721c692699d05e804581068 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Thu, 4 Aug 2022 13:18:36 +0100 Subject: [PATCH 4/4] Fix android build and code review issues --- .../ViewModels/HomePageViewModel.cs | 92 ++++++++++++++++ .../ViewModels/LoginPageViewModel.cs | 78 ------------- .../Pages/AppHome/HomePage.xaml.cs | 23 ++-- .../MobileTopupSelectProductPage.xaml.cs | 69 ++++++------ .../Platforms/Android/AndroidManifest.xml | 6 +- .../TransactionMobile.Maui.csproj | 24 ++-- .../UIServices/DialogService.cs | 104 +++++++----------- .../UIServices/SnackBarOptionsHelper.cs | 28 +++++ 8 files changed, 229 insertions(+), 195 deletions(-) create mode 100644 TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs create mode 100644 TransactionMobile.Maui/UIServices/SnackBarOptionsHelper.cs diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs new file mode 100644 index 00000000..cd354890 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs @@ -0,0 +1,92 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels; + +using Microsoft.AppCenter; +using Microsoft.AppCenter.Distribute; +using Models; +using MvvmHelpers; +using Services; +using UIServices; + +public class HomePageViewModel : BaseViewModel +{ + #region Fields + + private readonly IDialogService DialogService; + + private readonly IMemoryCacheService MemoryCacheService; + + #endregion + + #region Constructors + + public HomePageViewModel(IMemoryCacheService memoryCacheService, + IDialogService dialogService) { + this.MemoryCacheService = memoryCacheService; + this.DialogService = dialogService; + } + + #endregion + + #region Methods + + public async Task Initialise(CancellationToken cancellationToken) { + this.MemoryCacheService.TryGetValue("Configuration", out Configuration configuration); + + if (configuration.EnableAutoUpdates) { + await Distribute.SetEnabledAsync(true); + Distribute.CheckForUpdate(); + Distribute.ReleaseAvailable = this.OnReleaseAvailable; + Distribute.UpdateTrack = UpdateTrack.Public; + } + else { + Distribute.DisableAutomaticCheckForUpdate(); + } + + if (this.IsIOS() == false) { + // TODO: Move the keys to config service + AppCenter.Configure("android=f920cc96-de56-42fe-87d4-b49105761205;" + "ios=dd940171-ca8c-4219-9851-f83769464f37;" + + "uwp=3ad27ea3-3f24-4579-a88a-530025bd00d4;" + "macos=244fdee2-f897-431a-8bab-5081fc90b329;"); + AppCenter.Start(typeof(Distribute)); + } + } + + private Boolean IsIOS() => DeviceInfo.Current.Platform == DevicePlatform.iOS; + + private Boolean OnReleaseAvailable(ReleaseDetails releaseDetails) { + // Look at releaseDetails public properties to get version information, release notes text or release notes URL + String versionName = releaseDetails.ShortVersion; + String versionCodeOrBuildNumber = releaseDetails.Version; + String releaseNotes = releaseDetails.ReleaseNotes; + Uri releaseNotesUrl = releaseDetails.ReleaseNotesUrl; + + // custom dialog + String title = "Version " + versionName + " available!"; + Task answer; + + // On mandatory update, user can't postpone + if (releaseDetails.MandatoryUpdate) { + answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install"); + } + else { + answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install", "Later"); + } + + answer.ContinueWith(task => { + // If mandatory or if answer was positive + if (releaseDetails.MandatoryUpdate || (task as Task).Result) { + // Notify SDK that user selected update + Distribute.NotifyUpdateAction(UpdateAction.Update); + } + else { + // Notify SDK that user selected postpone (for 1 day) + // This method call is ignored by the SDK if the update is mandatory + Distribute.NotifyUpdateAction(UpdateAction.Postpone); + } + }); + + // Return true if you're using your own dialog, false otherwise + return true; + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs index 1e14a399..b2a20f3d 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs @@ -3,11 +3,8 @@ using System.Windows.Input; using Maui.UIServices; using MediatR; - using Microsoft.AppCenter; - using Microsoft.AppCenter.Distribute; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; - using Microsoft.Maui.Devices; using Models; using MvvmHelpers; using MvvmHelpers.Commands; @@ -167,79 +164,4 @@ private void CacheAccessToken(TokenResponseModel token) #endregion } - - public class HomePageViewModel : BaseViewModel - { - private readonly IMemoryCacheService MemoryCacheService; - - private readonly IDialogService DialogService; - - public HomePageViewModel(IMemoryCacheService memoryCacheService, - IDialogService dialogService) { - this.MemoryCacheService = memoryCacheService; - this.DialogService = dialogService; - } - - public async Task Initialise(CancellationToken cancellationToken) { - - this.MemoryCacheService.TryGetValue("Configuration", out Configuration configuration); - - if (configuration.EnableAutoUpdates) { - await Distribute.SetEnabledAsync(true); - Distribute.CheckForUpdate(); - Distribute.ReleaseAvailable = OnReleaseAvailable; - Distribute.UpdateTrack = UpdateTrack.Public; - } - else { - Distribute.DisableAutomaticCheckForUpdate(); - } - - if (this.IsIOS() == false) { - // TODO: Move the keys to config service - AppCenter.Configure("android=f920cc96-de56-42fe-87d4-b49105761205;" + "ios=dd940171-ca8c-4219-9851-f83769464f37;" + - "uwp=3ad27ea3-3f24-4579-a88a-530025bd00d4;" + "macos=244fdee2-f897-431a-8bab-5081fc90b329;"); - AppCenter.Start(typeof(Distribute)); - } - } - - private bool IsIOS() => - DeviceInfo.Current.Platform == DevicePlatform.iOS; - - private Boolean OnReleaseAvailable(ReleaseDetails releaseDetails) { - // Look at releaseDetails public properties to get version information, release notes text or release notes URL - String versionName = releaseDetails.ShortVersion; - String versionCodeOrBuildNumber = releaseDetails.Version; - String releaseNotes = releaseDetails.ReleaseNotes; - Uri releaseNotesUrl = releaseDetails.ReleaseNotesUrl; - - // custom dialog - String title = "Version " + versionName + " available!"; - Task answer; - - // On mandatory update, user can't postpone - if (releaseDetails.MandatoryUpdate) { - answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install"); - } - else { - answer = this.DialogService.ShowDialog(title, releaseNotes, "Download and Install", "Later"); - } - - answer.ContinueWith(task => { - // If mandatory or if answer was positive - if (releaseDetails.MandatoryUpdate || (task as Task).Result) { - // Notify SDK that user selected update - Distribute.NotifyUpdateAction(UpdateAction.Update); - } - else { - // Notify SDK that user selected postpone (for 1 day) - // This method call is ignored by the SDK if the update is mandatory - Distribute.NotifyUpdateAction(UpdateAction.Postpone); - } - }); - - // Return true if you're using your own dialog, false otherwise - return true; - } - - } } \ No newline at end of file diff --git a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs index ebb6cafa..8ca53558 100644 --- a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs +++ b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs @@ -4,18 +4,27 @@ namespace TransactionMobile.Maui.Pages.AppHome; public partial class HomePage : ContentPage { - private HomePageViewModel viewModel => BindingContext as HomePageViewModel; + #region Constructors - public HomePage(HomePageViewModel vm) - { - InitializeComponent(); - BindingContext = vm; + public HomePage(HomePageViewModel vm) { + this.InitializeComponent(); + this.BindingContext = vm; } - protected override async void OnAppearing() - { + #endregion + + #region Properties + + private HomePageViewModel viewModel => this.BindingContext as HomePageViewModel; + + #endregion + + #region Methods + + protected override async void OnAppearing() { base.OnAppearing(); await this.viewModel.Initialise(CancellationToken.None); } + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs b/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs index 937c3783..256366ea 100644 --- a/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs +++ b/TransactionMobile.Maui/Pages/Transactions/MobileTopup/MobileTopupSelectProductPage.xaml.cs @@ -6,50 +6,51 @@ namespace TransactionMobile.Maui.Pages.Transactions.MobileTopup; public partial class MobileTopupSelectProductPage : ContentPage { - private MobileTopupSelectProductPageViewModel viewModel => BindingContext as MobileTopupSelectProductPageViewModel; + #region Constructors - public MobileTopupSelectProductPage(MobileTopupSelectProductPageViewModel vm) - { - InitializeComponent(); - BindingContext = vm; + public MobileTopupSelectProductPage(MobileTopupSelectProductPageViewModel vm) { + this.InitializeComponent(); + this.BindingContext = vm; } - protected override async void OnAppearing() - { + #endregion + + #region Properties + + private MobileTopupSelectProductPageViewModel viewModel => this.BindingContext as MobileTopupSelectProductPageViewModel; + + #endregion + + #region Methods + + protected override async void OnAppearing() { base.OnAppearing(); - await viewModel.Initialise(CancellationToken.None); - this.LoadProducts(viewModel); - + await this.viewModel.Initialise(CancellationToken.None); + this.LoadProducts(this.viewModel); } - private void LoadProducts(MobileTopupSelectProductPageViewModel viewModel) - { + private void LoadProducts(MobileTopupSelectProductPageViewModel viewModel) { this.ProductsList.Clear(); - + Int32 rowCount = 0; - foreach (ContractProductModel modelProduct in viewModel.Products) - { - Button button = new Button - { - Text = modelProduct.ProductDisplayText, - HorizontalOptions = LayoutOptions.FillAndExpand, - AutomationId = modelProduct.ProductDisplayText, - }; + foreach (ContractProductModel modelProduct in viewModel.Products) { + Button button = new Button { + Text = modelProduct.ProductDisplayText, + HorizontalOptions = LayoutOptions.FillAndExpand, + AutomationId = modelProduct.ProductDisplayText, + }; button.SetDynamicResource(VisualElement.StyleProperty, "MobileTopupButtonStyle"); - Binding commandParameter = new Binding() - { - Source = new ItemSelected - { - SelectedItem = modelProduct, - SelectedItemIndex = rowCount - } - }; + Binding commandParameter = new Binding { + Source = new ItemSelected { + SelectedItem = modelProduct, + SelectedItemIndex = rowCount + } + }; - Binding command = new Binding - { - Source = viewModel.ProductSelectedCommand - }; + Binding command = new Binding { + Source = viewModel.ProductSelectedCommand + }; button.SetBinding(Button.CommandProperty, command); button.SetBinding(Button.CommandParameterProperty, commandParameter); @@ -59,4 +60,6 @@ private void LoadProducts(MobileTopupSelectProductPageViewModel viewModel) rowCount++; } } + + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml b/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml index e7c3c589..654ff35c 100644 --- a/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml +++ b/TransactionMobile.Maui/Platforms/Android/AndroidManifest.xml @@ -7,11 +7,7 @@ android:label="Transaction Processing POS"> - - - - + android:exported="false"> diff --git a/TransactionMobile.Maui/TransactionMobile.Maui.csproj b/TransactionMobile.Maui/TransactionMobile.Maui.csproj index 7eebd4ce..62f07bcd 100644 --- a/TransactionMobile.Maui/TransactionMobile.Maui.csproj +++ b/TransactionMobile.Maui/TransactionMobile.Maui.csproj @@ -202,27 +202,35 @@ apk - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link - None + SdkOnly + link diff --git a/TransactionMobile.Maui/UIServices/DialogService.cs b/TransactionMobile.Maui/UIServices/DialogService.cs index e4b1cf74..2868f5e9 100644 --- a/TransactionMobile.Maui/UIServices/DialogService.cs +++ b/TransactionMobile.Maui/UIServices/DialogService.cs @@ -1,18 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace TransactionMobile.Maui.UIServices +namespace TransactionMobile.Maui.UIServices { using BusinessLogic.UIServices; using CommunityToolkit.Maui.Alerts; - using CommunityToolkit.Maui.Core; public class DialogService : IDialogService { - public async Task ShowInformationToast(String message, + #region Methods + + public async Task ShowDialog(String title, + String message, + String cancelString) { + await Application.Current.MainPage.DisplayAlert(title, message, cancelString); + } + + public async Task ShowDialog(String title, + String message, + String acceptString, + String cancelString) { + return await Application.Current.MainPage.DisplayAlert(title, message, acceptString, cancelString); + } + + public async Task ShowErrorToast(String message, Action action, String actionButtonText, TimeSpan? duration, @@ -25,11 +33,11 @@ await Application.Current.MainPage.DisplaySnackbar(message, cancellationToken); } - public async Task ShowWarningToast(String message, - Action action, - String actionButtonText, - TimeSpan? duration, - CancellationToken cancellationToken) { + public async Task ShowInformationToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { await Application.Current.MainPage.DisplaySnackbar(message, action, actionButtonText, @@ -38,11 +46,22 @@ await Application.Current.MainPage.DisplaySnackbar(message, cancellationToken); } - public async Task ShowErrorToast(String message, - Action action, - String actionButtonText, - TimeSpan? duration, - CancellationToken cancellationToken) { + public async Task ShowPrompt(String title, + String message, + String acceptString, + String cancelString, + String placeHolder = "", + Int32 maxLength = -1, + Keyboard keyboard = null, + String initialValue = "") { + return await Application.Current.MainPage.DisplayPromptAsync(title, message, acceptString, cancelString, placeHolder, maxLength, keyboard, initialValue); + } + + public async Task ShowWarningToast(String message, + Action action, + String actionButtonText, + TimeSpan? duration, + CancellationToken cancellationToken) { await Application.Current.MainPage.DisplaySnackbar(message, action, actionButtonText, @@ -51,49 +70,6 @@ await Application.Current.MainPage.DisplaySnackbar(message, cancellationToken); } - public async Task ShowDialog(String title, - String message, - String cancelString) { - - await Application.Current.MainPage.DisplayAlert(title, message, cancelString); - } - public async Task ShowDialog(String title, - String message, - String acceptString, - String cancelString) - { - return await Application.Current.MainPage.DisplayAlert(title, message, acceptString, cancelString); - } - - public async Task ShowPrompt(String title, - String message, - String acceptString, - String cancelString, - String placeHolder = "", - Int32 maxLength = -1, - Keyboard keyboard = null, - String initialValue = "") { - return await Application.Current.MainPage.DisplayPromptAsync(title, message, acceptString, cancelString, placeHolder, maxLength,keyboard,initialValue); - } - } - - public static class SnackBarOptionsHelper - { - public static SnackbarOptions GetInfoSnackbarOptions => new SnackbarOptions { - BackgroundColor = Colors.Blue, - TextColor = Colors.White - }; - - public static SnackbarOptions GetWarningSnackbarOptions => new SnackbarOptions - { - BackgroundColor = Colors.Orange, - TextColor = Colors.White - }; - - public static SnackbarOptions GetErrorSnackbarOptions => new SnackbarOptions - { - BackgroundColor = Colors.Red, - TextColor = Colors.White, - }; + #endregion } -} +} \ No newline at end of file diff --git a/TransactionMobile.Maui/UIServices/SnackBarOptionsHelper.cs b/TransactionMobile.Maui/UIServices/SnackBarOptionsHelper.cs new file mode 100644 index 00000000..6c7cd866 --- /dev/null +++ b/TransactionMobile.Maui/UIServices/SnackBarOptionsHelper.cs @@ -0,0 +1,28 @@ +namespace TransactionMobile.Maui.UIServices; + +using CommunityToolkit.Maui.Core; + +public static class SnackBarOptionsHelper +{ + #region Properties + + public static SnackbarOptions GetErrorSnackbarOptions => + new SnackbarOptions { + BackgroundColor = Colors.Red, + TextColor = Colors.White, + }; + + public static SnackbarOptions GetInfoSnackbarOptions => + new SnackbarOptions { + BackgroundColor = Colors.Blue, + TextColor = Colors.White + }; + + public static SnackbarOptions GetWarningSnackbarOptions => + new SnackbarOptions { + BackgroundColor = Colors.Orange, + TextColor = Colors.White + }; + + #endregion +} \ No newline at end of file