From 946c465f0c060bfb87eaf534fd7713197cb55fa5 Mon Sep 17 00:00:00 2001 From: Dinis Vieira Date: Thu, 2 Nov 2023 02:32:12 +0000 Subject: [PATCH] PM-3349 Added Handler that enables the ExtendedDatePicker to get IsFocused events in Android. This is a workaround for fixing the current bug where it's not possible to select the "current day" in the expiration date of a Send. Fix for TimePicker not displaying default Time Value Updated some "Device" code to the new MAUI "DeviceInfo" --- src/App/MauiProgram.cs | 1 + .../Handlers/DatePickerHandlerMappings.cs | 6 +- .../Handlers/ExtendedDatePickerHandler.cs | 76 +++++++++++++++++++ .../Handlers/TimePickerHandlerMappings.cs | 8 +- .../Controls/DateTime/DateTimePicker.xaml | 1 + .../Controls/DateTime/DateTimePicker.xaml.cs | 12 ++- .../Controls/DateTime/DateTimeViewModel.cs | 3 +- src/Core/Controls/ExtendedDatePicker.cs | 12 +-- src/Core/Controls/ExtendedTimePicker.cs | 6 +- src/Core/Pages/Send/SendAddEditPage.xaml | 3 + src/Core/Pages/Send/SendAddEditPage.xaml.cs | 29 ++++--- .../Pages/Send/SendAddEditPageViewModel.cs | 19 ++--- 12 files changed, 120 insertions(+), 56 deletions(-) create mode 100644 src/App/Platforms/Android/Handlers/ExtendedDatePickerHandler.cs diff --git a/src/App/MauiProgram.cs b/src/App/MauiProgram.cs index 5e9f88886..0822e59a7 100644 --- a/src/App/MauiProgram.cs +++ b/src/App/MauiProgram.cs @@ -29,6 +29,7 @@ Bit.App.Handlers.ToolbarHandlerMappings.Setup(); handlers.AddHandler(typeof(Bit.App.Pages.TabsPage), typeof(Bit.App.Handlers.CustomTabbedPageHandler)); + handlers.AddHandler(typeof(Bit.App.Controls.ExtendedDatePicker), typeof(Bit.App.Handlers.ExtendedDatePickerHandler)); #else iOS.Core.Utilities.iOSCoreHelpers.ConfigureMAUIHandlers(handlers); #endif diff --git a/src/App/Platforms/Android/Handlers/DatePickerHandlerMappings.cs b/src/App/Platforms/Android/Handlers/DatePickerHandlerMappings.cs index b3060acc6..1c5291119 100644 --- a/src/App/Platforms/Android/Handlers/DatePickerHandlerMappings.cs +++ b/src/App/Platforms/Android/Handlers/DatePickerHandlerMappings.cs @@ -23,12 +23,12 @@ namespace Bit.App.Handlers } }); - DatePickerHandler.Mapper.AppendToMapping(nameof(IDatePicker.Date), UpdateTextPlaceholderOnFormatLikePlacholder); + DatePickerHandler.Mapper.AppendToMapping(nameof(IDatePicker.Date), UpdateTextPlaceholderOnFormatLikePlaceholder); - DatePickerHandler.Mapper.AppendToMapping(nameof(IDatePicker.Format), UpdateTextPlaceholderOnFormatLikePlacholder); + DatePickerHandler.Mapper.AppendToMapping(nameof(IDatePicker.Format), UpdateTextPlaceholderOnFormatLikePlaceholder); } - private static void UpdateTextPlaceholderOnFormatLikePlacholder(IDatePickerHandler handler, IDatePicker datePicker) + private static void UpdateTextPlaceholderOnFormatLikePlaceholder(IDatePickerHandler handler, IDatePicker datePicker) { if (datePicker is ExtendedDatePicker extDatePicker && extDatePicker.Format == extDatePicker.PlaceHolder) { diff --git a/src/App/Platforms/Android/Handlers/ExtendedDatePickerHandler.cs b/src/App/Platforms/Android/Handlers/ExtendedDatePickerHandler.cs new file mode 100644 index 000000000..2406bb1fc --- /dev/null +++ b/src/App/Platforms/Android/Handlers/ExtendedDatePickerHandler.cs @@ -0,0 +1,76 @@ +using Android.App; +using Microsoft.Maui.Handlers; +using Microsoft.Maui.Platform; + +namespace Bit.App.Handlers +{ + // Note: This Handler exists only to allow the ExtendedDatePicker to receive IsFocused events on Android. iOS Already does this with this fix: https://github.com/dotnet/maui/pull/13321 + // If MAUI eventually implements this behavior we can remove this handler completely. There is another Handler (DatePickerHandlerMappings) for the other DatePicker customizations. + public partial class ExtendedDatePickerHandler : DatePickerHandler + { + + public static PropertyMapper PropertyMapper = new (DatePickerHandler.Mapper) + { + [nameof(IDatePicker.IsFocused)] = MapIsFocused + }; + + public ExtendedDatePickerHandler() : base(PropertyMapper) + { + } + + public static void MapIsFocused(ExtendedDatePickerHandler handler, IDatePicker datePicker) + { + if (handler.PlatformView.IsFocused == datePicker.IsFocused) return; + + if (datePicker.IsFocused) + { + handler.PlatformView.RequestFocus(); + } + else + { + handler.PlatformView.ClearFocus(); + } + } + + private DatePickerDialog? _dialog; + + protected override DatePickerDialog CreateDatePickerDialog(int year, int month, int day) + { + _dialog = base.CreateDatePickerDialog(year, month, day); + return _dialog; + } + + protected override void ConnectHandler(MauiDatePicker platformView) + { + base.ConnectHandler(platformView); + if (_dialog != null) + { + _dialog.ShowEvent += OnDialogShown; + _dialog.DismissEvent += OnDialogDismissed; + } + } + + //Currently the Disconnect Handler needs to be manually called from the App: https://github.com/dotnet/maui/issues/3604 + protected override void DisconnectHandler(MauiDatePicker platformView) + { + if (_dialog != null) + { + _dialog.ShowEvent -= OnDialogShown; + _dialog.DismissEvent -= OnDialogDismissed; + } + base.DisconnectHandler(platformView); + + _dialog = null; + } + + private void OnDialogShown(object sender, EventArgs e) + { + this.VirtualView.IsFocused = true; + } + + private void OnDialogDismissed(object sender, EventArgs e) + { + this.VirtualView.IsFocused = false; + } + } +} diff --git a/src/App/Platforms/Android/Handlers/TimePickerHandlerMappings.cs b/src/App/Platforms/Android/Handlers/TimePickerHandlerMappings.cs index 33cd4e0d6..c22474557 100644 --- a/src/App/Platforms/Android/Handlers/TimePickerHandlerMappings.cs +++ b/src/App/Platforms/Android/Handlers/TimePickerHandlerMappings.cs @@ -16,19 +16,19 @@ namespace Bit.App.Handlers handler.PlatformView.Gravity = GravityFlags.CenterHorizontal; // use placeholder until NullableTime set - if (!extTimePicker.NullableTime.HasValue) + if (!extTimePicker.NullableTime.HasValue && !string.IsNullOrWhiteSpace(extTimePicker.PlaceHolder)) { handler.PlatformView.Text = extTimePicker.PlaceHolder; } } }); - TimePickerHandler.Mapper.AppendToMapping(nameof(ITimePicker.Time), UpdateTextPlaceholderOnFormatLikePlacholder); + TimePickerHandler.Mapper.AppendToMapping(nameof(ITimePicker.Time), UpdateTextPlaceholderOnFormatLikePlaceholder); - TimePickerHandler.Mapper.AppendToMapping(nameof(ITimePicker.Format), UpdateTextPlaceholderOnFormatLikePlacholder); + TimePickerHandler.Mapper.AppendToMapping(nameof(ITimePicker.Format), UpdateTextPlaceholderOnFormatLikePlaceholder); } - private static void UpdateTextPlaceholderOnFormatLikePlacholder(ITimePickerHandler handler, ITimePicker timePicker) + private static void UpdateTextPlaceholderOnFormatLikePlaceholder(ITimePickerHandler handler, ITimePicker timePicker) { if (timePicker is ExtendedTimePicker extDatePicker && extDatePicker.Format == extDatePicker.PlaceHolder) { diff --git a/src/Core/Controls/DateTime/DateTimePicker.xaml b/src/Core/Controls/DateTime/DateTimePicker.xaml index e68d97163..173016526 100644 --- a/src/Core/Controls/DateTime/DateTimePicker.xaml +++ b/src/Core/Controls/DateTime/DateTimePicker.xaml @@ -4,6 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:controls="clr-namespace:Bit.App.Controls" x:Class="Bit.App.Controls.DateTimePicker" + Unloaded="DateTimePicker_OnUnloaded" ColumnDefinitions="*,*"> diff --git a/src/Core/Controls/DateTime/DateTimeViewModel.cs b/src/Core/Controls/DateTime/DateTimeViewModel.cs index ac940a773..160fa59ad 100644 --- a/src/Core/Controls/DateTime/DateTimeViewModel.cs +++ b/src/Core/Controls/DateTime/DateTimeViewModel.cs @@ -1,5 +1,4 @@ -using System; -using Bit.Core.Utilities; +using Bit.Core.Utilities; namespace Bit.App.Controls { diff --git a/src/Core/Controls/ExtendedDatePicker.cs b/src/Core/Controls/ExtendedDatePicker.cs index ea3e881bb..9f7a76387 100644 --- a/src/Core/Controls/ExtendedDatePicker.cs +++ b/src/Core/Controls/ExtendedDatePicker.cs @@ -1,8 +1,4 @@ -using System; -using Microsoft.Maui.Controls; -using Microsoft.Maui; - -namespace Bit.App.Controls +namespace Bit.App.Controls { public class ExtendedDatePicker : DatePicker { @@ -83,5 +79,11 @@ namespace Bit.App.Controls } } } + + //Currently the Disconnect Handler needs to be manually called from the App: https://github.com/dotnet/maui/issues/3604 + public void DisconnectHandler() + { + Handler?.DisconnectHandler(); + } } } diff --git a/src/Core/Controls/ExtendedTimePicker.cs b/src/Core/Controls/ExtendedTimePicker.cs index b2985bd1b..a9eb970a6 100644 --- a/src/Core/Controls/ExtendedTimePicker.cs +++ b/src/Core/Controls/ExtendedTimePicker.cs @@ -1,8 +1,4 @@ -using System; -using Microsoft.Maui.Controls; -using Microsoft.Maui; - -namespace Bit.App.Controls +namespace Bit.App.Controls { public class ExtendedTimePicker : TimePicker { diff --git a/src/Core/Pages/Send/SendAddEditPage.xaml b/src/Core/Pages/Send/SendAddEditPage.xaml index c83b7fdc5..fb982303d 100644 --- a/src/Core/Pages/Send/SendAddEditPage.xaml +++ b/src/Core/Pages/Send/SendAddEditPage.xaml @@ -12,6 +12,7 @@ xmlns:core="clr-namespace:Bit.Core" x:DataType="pages:SendAddEditPageViewModel" HideSoftInputOnTapped="True" + Unloaded="OnUnloaded" x:Name="_page" Title="{Binding PageTitle}"> @@ -310,6 +311,7 @@ + MainThread.BeginInvokeOnMainThread(() => { var data = message.Data as Tuple; _vm.FileData = data.Item1; @@ -141,8 +135,7 @@ namespace Bit.App.Pages protected override bool OnBackButtonPressed() { - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (_vm.IsAddFromShare && Device.RuntimePlatform == Device.Android) + if (_vm.IsAddFromShare && DeviceInfo.Platform == DevicePlatform.Android) { _appOptions.CreateSend = null; } @@ -152,13 +145,18 @@ namespace Bit.App.Pages protected override void OnDisappearing() { base.OnDisappearing(); - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (Device.RuntimePlatform != Device.iOS) + if (DeviceInfo.Platform == DevicePlatform.iOS) { _broadcasterService.Unsubscribe(nameof(SendAddEditPage)); } } + private void OnUnloaded(object sender, EventArgs e) + { + _deletionExtendedDatePicker?.DisconnectHandler(); + _expirationExtendedDatePicker?.DisconnectHandler(); + } + private async void TextType_Clicked(object sender, EventArgs eventArgs) { await _vm.TypeChangedAsync(SendType.Text); @@ -310,8 +308,7 @@ namespace Bit.App.Pages private void AdjustToolbar() { _saveItem.IsEnabled = _vm.SendEnabled; - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (!_vm.SendEnabled && _vm.EditMode && Device.RuntimePlatform == Device.Android) + if (!_vm.SendEnabled && _vm.EditMode && DeviceInfo.Platform == DevicePlatform.Android) { ToolbarItems.Remove(_removePassword); ToolbarItems.Remove(_copyLink); diff --git a/src/Core/Pages/Send/SendAddEditPageViewModel.cs b/src/Core/Pages/Send/SendAddEditPageViewModel.cs index 945e7f24a..5f97d1b74 100644 --- a/src/Core/Pages/Send/SendAddEditPageViewModel.cs +++ b/src/Core/Pages/Send/SendAddEditPageViewModel.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Bit.App.Abstractions; +using Bit.App.Abstractions; using Bit.App.Controls; using Bit.Core.Resources.Localization; using Bit.App.Utilities; @@ -11,9 +8,6 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.View; using Bit.Core.Utilities; -using Microsoft.Maui.Networking; -using Microsoft.Maui.Controls; -using Microsoft.Maui; namespace Bit.App.Pages { @@ -395,8 +389,7 @@ namespace Bit.App.Pages var sendId = await _sendService.SaveWithServerAsync(send, encryptedFileData); await _deviceActionService.HideLoadingAsync(); - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (Device.RuntimePlatform == Device.Android && IsFile) + if (DeviceInfo.Platform == DevicePlatform.Android && IsFile) { // Workaround for https://github.com/xamarin/Xamarin.Forms/issues/5418 // Exiting and returning (file picker) calls OnAppearing on list page instead of this modal, and @@ -455,8 +448,7 @@ namespace Bit.App.Pages { if (IsAddFromShare) { - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (Device.RuntimePlatform == Device.Android) + if (DeviceInfo.Platform == DevicePlatform.Android) { _deviceActionService.CloseMainApp(); return; @@ -513,8 +505,7 @@ namespace Bit.App.Pages await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired); } - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (IsAddFromShare && Device.RuntimePlatform == Device.Android) + if (IsAddFromShare && DeviceInfo.Platform == DevicePlatform.Android) { _deviceActionService.CloseMainApp(); return; @@ -630,7 +621,7 @@ namespace Bit.App.Pages internal void TriggerSendTextPropertyChanged() { - Device.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(Send))); + MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(Send))); } } }