diff --git a/src/HuaweiHMSInstaller.csproj b/src/HuaweiHMSInstaller.csproj index 798b3f5..11b477e 100644 --- a/src/HuaweiHMSInstaller.csproj +++ b/src/HuaweiHMSInstaller.csproj @@ -113,6 +113,7 @@ + diff --git a/src/Pages/DownloadandInstallPage.xaml.cs b/src/Pages/DownloadandInstallPage.xaml.cs index f1cd6e0..922c18e 100644 --- a/src/Pages/DownloadandInstallPage.xaml.cs +++ b/src/Pages/DownloadandInstallPage.xaml.cs @@ -1,5 +1,6 @@ using AdvancedSharpAdbClient; using HuaweiHMSInstaller.Helper; +using HuaweiHMSInstaller.Integrations.Analytics; using HuaweiHMSInstaller.Models; using HuaweiHMSInstaller.Services; using HuaweiHMSInstaller.ViewModels; @@ -54,8 +55,10 @@ public partial class DownloadandInstallPage : ContentPage, IQueryAttributable private readonly ILocalizationResourceManager _localizationResourceManager; private readonly IHttpClientFactory _httpClient; private readonly DownloadAndInstallPageViewModel _viewModel; + private readonly AnalyticsSubject _analyticsSubject; - public DownloadandInstallPage(DownloadAndInstallPageViewModel viewModel) + + public DownloadandInstallPage(DownloadAndInstallPageViewModel viewModel, AnalyticsSubject analyticsSubject) { Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged; @@ -65,6 +68,7 @@ public DownloadandInstallPage(DownloadAndInstallPageViewModel viewModel) _localizationResourceManager = ServiceProvider.GetService(); _options = ServiceProvider.GetService>().Value; _httpClient = ServiceProvider.GetService(); + _analyticsSubject = analyticsSubject; _viewModel = viewModel; this.NavigatedTo += (s, e) => Initialize(); @@ -78,7 +82,6 @@ public void ApplyQueryAttributes(IDictionary query) } OnPropertyChanged(nameof(SearchListItem)); } - private void Initialize() { // Assign the game item @@ -137,11 +140,15 @@ private async Task DownloadAndInstallOperationAsync() var checkHuaweiService = await _appGalleryService.CheckAppGalleryCloudServiceAsync(); if (checkInternet && checkHuaweiService) { - await DownloadandInstallHmsAppsAndNavigateAsync(adbDevices.First()); + foreach (var device in adbDevices) + { + await DownloadandInstallHmsAppsAndNavigateAsync(device); + } } else { var msg = checkInternet && !checkHuaweiService ? _localizationResourceManager.GetValue("huawei_server_unreachable") : _localizationResourceManager.GetValue("internet_connection_error"); + await _analyticsSubject.NotifyAsync(msg); await AlertForNotHaveInternetAsync(msg); } } @@ -279,21 +286,11 @@ private void UpdateCommentLabel() { // Update the message // Update the comment label text based on the current progress and message index - var adbProgressMsg = AdbProgressMessages.Where(x => x.Value).ToDictionary(x => x.Key, x => x.Value).Keys.ToList(); - if (adbProgressMsg.Count > currentThresholdIndex) + var commentString = _viewModel.UpdateCommentLabel(AdbProgressMessages, currentThresholdIndex, GameName); + Dispatcher.Dispatch(delegate { - commentBuilder.Clear(); - var value = adbProgressMsg[currentThresholdIndex]; - if (value == AdbMessagesConst.DownloadingGame || value == AdbMessagesConst.InstallingGame) - { - value += ": " + GameName; - } - commentBuilder.Append(value); - Dispatcher.Dispatch(delegate - { - this.commentLabel.Text = commentBuilder.ToString(); - }); - } + this.commentLabel.Text = commentString; + }); } private void UpdateHMSInfoLabel() { @@ -320,15 +317,10 @@ await Task.Run(() => } }); } - private async ValueTask CheckFileSize(FileInfo fileInfo, string url) - { - long fileSize = fileInfo.Length; - var downloadFileSize = await HttpClientExtensions.GetFileSizeAsync(_httpClient, url); - return fileSize == downloadFileSize; - } private async Task AlertForNotHaveAdbDevices() { await Task.Delay(1000); + await _analyticsSubject.NotifyAsync("No Adb Devices"); var errorLanguage = _localizationResourceManager.GetValue("error"); var emulatorConnectLanguage = _localizationResourceManager.GetValue("emulator_try_again_connect"); var cancelLanguage = _localizationResourceManager.GetValue("cancel"); @@ -445,7 +437,7 @@ private async Task CheckApkFileSizeAsync(InstallApkModel model) if (exist) { FileInfo fileInfo = new(filePath); - var checkFileSize = await CheckFileSize(fileInfo, model.DownloadUrl); + var checkFileSize = await _viewModel.CheckFileSize(fileInfo, model.DownloadUrl); if (checkFileSize) { model.IsDownloaded = true; diff --git a/src/Pages/MainPage.xaml.cs b/src/Pages/MainPage.xaml.cs index 9f3f2db..da596cf 100644 --- a/src/Pages/MainPage.xaml.cs +++ b/src/Pages/MainPage.xaml.cs @@ -1,4 +1,5 @@ using HuaweiHMSInstaller.Helper; +using HuaweiHMSInstaller.Integrations.Analytics; using HuaweiHMSInstaller.Models; using HuaweiHMSInstaller.Pages; using HuaweiHMSInstaller.Services; @@ -10,7 +11,6 @@ using System.Collections.ObjectModel; using System.Globalization; using Path = Microsoft.Maui.Controls.Shapes.Path; -using ServiceProvider = HuaweiHMSInstaller.Services.ServiceProvider; namespace HuaweiHMSInstaller; public partial class MainPage : ContentPage @@ -19,21 +19,30 @@ public partial class MainPage : ContentPage private Button footerButton = new(); private readonly IAppGalleryService _appGalleryService; private ObservableCollection FilteredItems = new(); - + private delegate void WorkerCompletedEventHandler(); private SearchListItem SelectedItem { get; set; } private Frame SearchListFrame; private readonly ILocalizationResourceManager _localizationResourceManager; private readonly GlobalOptions _options; private readonly MainViewModel _mainViewModel; - public MainPage(MainViewModel mainViewModel) + private readonly AnalyticsSubject _analyticsSubject; + + + public MainPage( + MainViewModel mainViewModel, + IAppGalleryService appGalleryService, + ILocalizationResourceManager localizationResourceManager, + AnalyticsSubject analyticsSubject, + IOptions options) { Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged; InitializeComponent(); - _appGalleryService = ServiceProvider.GetService(); - _localizationResourceManager = ServiceProvider.GetService(); - _options = ServiceProvider.GetService>().Value; + _appGalleryService = appGalleryService; + _localizationResourceManager = localizationResourceManager; + _options = options.Value; _mainViewModel = mainViewModel; + _analyticsSubject = analyticsSubject; Init(); } private void Init() @@ -43,48 +52,20 @@ private void Init() this.SearchListFrameGrid.BackgroundColor = Color.FromRgba(255, 255, 255, 0.05); this.VersionNum.Text = $"{_localizationResourceManager.GetValue("version")}: {_options.VersionNumber}"; - _ = CheckInternetAndAppGalleryService(AfterEventInternetAndHuaweiServiceCheck); + _mainViewModel.Init(CheckInternetConnectionAction); + _mainViewModel.AfterEventInternetAndHuaweiServiceCheck = AfterEventInternetAndHuaweiServiceCheck; } #region Internet&Huawei server check operation - private bool CheckNetworkConnectionInit() + private void CheckInternetConnectionAction() => Dispatcher.StartTimer(TimeSpan.FromSeconds(2), HasNoInternetConnection); + private bool HasNoInternetConnection() { - - var current = Connectivity.NetworkAccess; - if (current == NetworkAccess.Internet) return true; - + this.sponsorGameLoader.IsVisible = false; + this.sponsorGameNotInternetorHuaweiService.IsVisible = true; + this.sponsorGameNotInternetorHuaweiServiceLabel.Text = _localizationResourceManager.GetValue("internet_connection_error"); + this.sponsorGameStackLayout.IsVisible = false; + InstallButtonEnable(false); return false; } - private void CheckHuaweiService(Worker.WorkCompletedEventHandler func) => - CheckService(func, _appGalleryService.CheckAppGalleryServiceAsync); - private void CheckHuaweiCloudService(Worker.WorkCompletedEventHandler func) => - CheckService(func, _appGalleryService.CheckAppGalleryCloudServiceAsync); - private void CheckService(Worker.WorkCompletedEventHandler func, Func> check) - { - var worker = new Worker(); - worker.WorkCompleted += func; - _ = worker.DoWorkAsync(async () => await check()); - } - private async Task CheckInternetAndAppGalleryService(Worker.WorkCompletedEventHandler func) - { - var networkState = CheckNetworkConnectionInit(); - var internetState = await NetworkUtils.CheckForInternetConnectionAsync(); - if (networkState && internetState) - { - CheckHuaweiService(func); - } - else - { - Dispatcher.StartTimer(TimeSpan.FromSeconds(2), () => - { - this.sponsorGameLoader.IsVisible = false; - this.sponsorGameNotInternetorHuaweiService.IsVisible = true; - this.sponsorGameNotInternetorHuaweiServiceLabel.Text = _localizationResourceManager.GetValue("internet_connection_error"); - this.sponsorGameStackLayout.IsVisible = false; - InstallButtonEnable(false); - return false; - }); - } - } private void AfterEventInternetAndHuaweiServiceCheck(object sender, bool result) { if (result) @@ -98,7 +79,7 @@ private void AfterEventInternetAndHuaweiServiceCheck(object sender, bool result) } else { - CheckHuaweiService(SposorGameCheck); + _mainViewModel.CheckHuaweiService(SposorGameCheck); } } private void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e) @@ -180,6 +161,14 @@ private async Task GetSponsorGameInfo() this.sponsorGameLoader.IsVisible = false; this.sponsorGameStackLayout.IsVisible = true; SelectedItem = selectedGame; + await _analyticsSubject.NotifyAsync("Main Page Loaded", + new Dictionary + { + { "SelectedGame", selectedGame.Name }, + { "SelectedGameId", selectedGame.AppId } + } + ); + InstallButtonEnable(true); } } @@ -267,6 +256,7 @@ void OkayInternetandHuaweiService() void NotInternetAndHuweiServiceState(string langKey) { + _analyticsSubject.Notify("Unable to connect to the internet or Huawei service"); label.FontSize = 19; label.Margin = new Thickness(0, 0, 0, 10); label.Text = _localizationResourceManager.GetValue(langKey); @@ -296,7 +286,7 @@ void CheckHuaweiCloudServicekCallBack(object sender, bool result) if (resultCheckingInternetConnection) { - CheckHuaweiCloudService(CheckHuaweiCloudServicekCallBack); + _mainViewModel.CheckHuaweiCloudService(CheckHuaweiCloudServicekCallBack); } else { @@ -477,6 +467,13 @@ private async void SearchBarPressedAsync(object sender, EventArgs e) SearchLoader.IsVisible = true; + await _analyticsSubject.NotifyAsync("Search Bar Pressed", + new Dictionary + { + { "SearchQuery", query } + } + ); + var searchResult = await _appGalleryService.SearchAppGalleryApp(query); var items = searchResult.layoutData.SelectMany(t => t.dataList.Select(x => @@ -598,7 +595,6 @@ private void CreateListView() }); } - private Image CreateImage() { var image = new Image @@ -613,7 +609,6 @@ private Image CreateImage() return image; } - private Label CreateLabel() { var label = new Label @@ -635,7 +630,6 @@ private Label CreateLabel() return label; } - private StackLayout CreateStackLayout(Image image, Label label) { return new StackLayout @@ -661,8 +655,6 @@ public void SearchBar_TextChanged(object sender, TextChangedEventArgs e) InstallButtonEnable(false); SelectedItem = null; selectedItemLabel.Text = string.Empty; - - } } private void langPicker_SelectedIndexChanged(object sender, EventArgs e) @@ -685,6 +677,7 @@ private void Button_ChangeGame_Clicked(object sender, EventArgs e) this.selectedGameFrame.IsVisible = false; this.searchBar.IsVisible = true; InstallButtonEnable(false); + _analyticsSubject.Notify("Change Game Button Clicked"); } } diff --git a/src/Pages/ThanksPage.xaml.cs b/src/Pages/ThanksPage.xaml.cs index 7e2bfdd..ca84204 100644 --- a/src/Pages/ThanksPage.xaml.cs +++ b/src/Pages/ThanksPage.xaml.cs @@ -1,4 +1,5 @@ using HuaweiHMSInstaller.Helper; +using HuaweiHMSInstaller.Integrations.Analytics; using HuaweiHMSInstaller.ViewModels; using LocalizationResourceManager.Maui; using Syncfusion.Maui.Popup; @@ -11,16 +12,20 @@ public partial class ThanksPage : ContentPage private SfPopup _sfPopup; private readonly ILocalizationResourceManager _localizationResourceManager; private readonly ThanksPageViewModel _viewModel; + private readonly AnalyticsSubject _analyticsSubject; - public ThanksPage(ThanksPageViewModel viewModel) + public ThanksPage(ThanksPageViewModel viewModel, AnalyticsSubject analyticsSubject) { InitializeComponent(); _localizationResourceManager = ServiceProvider.GetService(); _viewModel = viewModel; BindingContext = _viewModel; + _analyticsSubject = analyticsSubject; + _analyticsSubject.Notify("Thanks Page Loaded"); } private void OnFinishButtonClicked(object sender, EventArgs e) { + _analyticsSubject.Notify("Thanks Page Finish Button Clicked"); //Create popup and push it to the navigation stack //Initialize the popup var popup = new SfPopup(); @@ -89,7 +94,8 @@ private void CloseButton_Clicked(object sender, EventArgs e) private async void ButtonBack_Clicked(object sender, EventArgs e) { - _viewModel.NavigateToMainPage(); + await _analyticsSubject.NotifyAsync("Thanks Page Back Button Clicked"); + await _viewModel.NavigateToMainPage(); } } diff --git a/src/Services/AdbOperationService.cs b/src/Services/AdbOperationService.cs index 3ed7b24..9dd7b90 100644 --- a/src/Services/AdbOperationService.cs +++ b/src/Services/AdbOperationService.cs @@ -101,12 +101,10 @@ public async Task DownloadAdbFromInternetAsync(IProgress progress = null) } public async Task> GetDevices() { + await GetDevicesDefaultAsync(); var devices = await _adbClient.GetDevicesAsync(); - if (devices.Count == 0) - { - var defaultDevice = await GetDevicesDefaultAsync(); - if(defaultDevice) devices = await _adbClient.GetDevicesAsync(); - } + devices = devices.Where(t => t.State == DeviceState.Online).ToList(); + return devices; } public async Task DownloadApkFromInternetAsync(string apkUrl, string apkName, IProgress progress = null, CancellationToken cancellationToken = default) @@ -187,12 +185,18 @@ public void InstallApkToDevice(string packageFilePath, ProgressHandler installPr continue; } - if(ex.Message.Contains("INSTALL_FAILED_UPDATE_INCOMPATIBLE")) + if (ex.Message.Contains("INSTALL_FAILED_UPDATE_INCOMPATIBLE")) { //TODO : Find uninstall package name using adb command manager.UninstallPackage("com.huawei.hwid"); } + if (ex.Message.Contains("INSTALL_FAILED_VERSION_DOWNGRADE")) + { + installed = true; + continue; + } + // Catch any exceptions that occur during installation and restart the ADB server. Debug.WriteLine(ex.Message); RestartAdbServer().ConfigureAwait(false).GetAwaiter().GetResult(); diff --git a/src/Services/IAppGalleryService.cs b/src/Services/IAppGalleryService.cs index 079e72b..30d2a3b 100644 --- a/src/Services/IAppGalleryService.cs +++ b/src/Services/IAppGalleryService.cs @@ -2,7 +2,7 @@ namespace HuaweiHMSInstaller.Services { - internal interface IAppGalleryService + public interface IAppGalleryService { Task SearchAppGalleryApp(string keyword); Task GetAppDetail(string appId); diff --git a/src/ViewModels/DownloadAndInstallPageViewModel.cs b/src/ViewModels/DownloadAndInstallPageViewModel.cs index 6317f83..c7b2eca 100644 --- a/src/ViewModels/DownloadAndInstallPageViewModel.cs +++ b/src/ViewModels/DownloadAndInstallPageViewModel.cs @@ -1,11 +1,13 @@ using AdvancedSharpAdbClient; using CommunityToolkit.Mvvm.ComponentModel; using HuaweiHMSInstaller.Helper; +using HuaweiHMSInstaller.Integrations.Analytics; using HuaweiHMSInstaller.Models; using HuaweiHMSInstaller.Pages; using HuaweiHMSInstaller.Services; using Microsoft.Extensions.Options; using System.Diagnostics; +using System.Text; namespace HuaweiHMSInstaller.ViewModels { @@ -13,6 +15,9 @@ public sealed partial class DownloadAndInstallPageViewModel : BaseViewModel, IQu { private readonly IAdbOperationService _adbOperationService; private readonly GlobalOptions _options; + private readonly AnalyticsSubject _analyticsSubject; + private readonly IHttpClientFactory _httpClient; + public SearchListItem SearchListItem { get; set; } @@ -32,12 +37,20 @@ public sealed partial class DownloadAndInstallPageViewModel : BaseViewModel, IQu public Action OnProgressChanged; public Action OnThresholdReached; - public DownloadAndInstallPageViewModel(INavigationService navigationService, IAdbOperationService adbOperationService, IOptions options) + public DownloadAndInstallPageViewModel( + INavigationService navigationService, + IAdbOperationService adbOperationService, + IOptions options, + AnalyticsSubject analyticsSubject, + IHttpClientFactory httpClient) : base(navigationService) { Debug.WriteLine($"**** {this.GetType().Name}.{nameof(DownloadAndInstallPageViewModel)}: ctor"); _adbOperationService = adbOperationService; _options = options.Value; + _httpClient = httpClient; + _analyticsSubject = analyticsSubject; + _analyticsSubject.Notify("Download and Install Page Loaded"); Init(); } @@ -157,5 +170,32 @@ private void DisableAdbProgressMessages() AdbProgressMessages[AdbMessagesConst.InstallingADBDriver] = false; OnThresholdReached?.Invoke(); } + + public async ValueTask CheckFileSize(FileInfo fileInfo, string url) + { + long fileSize = fileInfo.Length; + var downloadFileSize = await HttpClientExtensions.GetFileSizeAsync(_httpClient, url); + return fileSize == downloadFileSize; + } + + public string UpdateCommentLabel(Dictionary adbProgressMessages, int currentThresholdIndex, string gameName) + { + StringBuilder commentBuilder = new StringBuilder(); + // Update the message + // Update the comment label text based on the current progress and message index + var adbProgressMsg = adbProgressMessages.Where(x => x.Value).ToDictionary(x => x.Key, x => x.Value).Keys.ToList(); + if (adbProgressMsg.Count > currentThresholdIndex) + { + commentBuilder.Clear(); + var value = adbProgressMsg[currentThresholdIndex]; + if (value == AdbMessagesConst.DownloadingGame || value == AdbMessagesConst.InstallingGame) + { + value += ": " + gameName; + } + commentBuilder.Append(value); + } + + return commentBuilder.ToString(); + } } } \ No newline at end of file diff --git a/src/ViewModels/MainViewModel.cs b/src/ViewModels/MainViewModel.cs index 2200bc5..f90b063 100644 --- a/src/ViewModels/MainViewModel.cs +++ b/src/ViewModels/MainViewModel.cs @@ -1,16 +1,34 @@ -using HuaweiHMSInstaller.Models; +using HuaweiHMSInstaller.Helper; +using HuaweiHMSInstaller.Integrations.Analytics; +using HuaweiHMSInstaller.Models; using HuaweiHMSInstaller.Pages; using HuaweiHMSInstaller.Services; +using System.Text; namespace HuaweiHMSInstaller.ViewModels { public class MainViewModel : BaseViewModel { + private readonly AnalyticsSubject _analyticsSubject; + private readonly IAppGalleryService _appGalleryService; + public SearchListItem SearchListItem { get; set; } + public Worker.WorkCompletedEventHandler AfterEventInternetAndHuaweiServiceCheck { get; set; } + - public MainViewModel(INavigationService navigationService) + public MainViewModel( + INavigationService navigationService, + AnalyticsSubject analyticsSubject, + IAppGalleryService appGalleryService) : base(navigationService) { + _appGalleryService = appGalleryService; + _analyticsSubject = analyticsSubject; + } + + public void Init(Action nonInternetAction) + { + _ = CheckInternetAndAppGalleryService(AfterEventInternetAndHuaweiServiceCheck, nonInternetAction); } //public async void NavigateToSettingsPage() @@ -23,5 +41,41 @@ public async void NavigateToDownloadAndInstallPage(SearchListItem listItem) await NavigateToAsync(listItem); } + public async Task CheckInternetAndAppGalleryService(Worker.WorkCompletedEventHandler func, Action nonInternetAction) + { + var networkState = CheckNetworkConnectionInit(); + var internetState = await NetworkUtils.CheckForInternetConnectionAsync(); + if (networkState && internetState) + { + CheckHuaweiService(func); + } + else + { + nonInternetAction?.Invoke(); + await _analyticsSubject.NotifyAsync("No Internet Connection"); + } + } + + private bool CheckNetworkConnectionInit() + { + + var current = Connectivity.NetworkAccess; + if (current == NetworkAccess.Internet) return true; + + return false; + } + + public void CheckHuaweiService(Worker.WorkCompletedEventHandler func) => + CheckService(func, _appGalleryService.CheckAppGalleryServiceAsync); + public void CheckHuaweiCloudService(Worker.WorkCompletedEventHandler func) => + CheckService(func, _appGalleryService.CheckAppGalleryCloudServiceAsync); + public void CheckService(Worker.WorkCompletedEventHandler func, Func> check) + { + var worker = new Worker(); + worker.WorkCompleted += func; + _ = worker.DoWorkAsync(async () => await check()); + } + + } } diff --git a/src/ViewModels/ThanksPageViewModel.cs b/src/ViewModels/ThanksPageViewModel.cs index 69e1a41..cda07bc 100644 --- a/src/ViewModels/ThanksPageViewModel.cs +++ b/src/ViewModels/ThanksPageViewModel.cs @@ -1,12 +1,15 @@ -using HuaweiHMSInstaller.Services; +using HuaweiHMSInstaller.Integrations.Analytics; +using HuaweiHMSInstaller.Services; namespace HuaweiHMSInstaller.ViewModels { public class ThanksPageViewModel : BaseViewModel { + public ThanksPageViewModel(INavigationService navigationService) : base(navigationService) { + } //public async void NavigateToSettingsPage()