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/AndroidManifest.xml b/src/App/Platforms/Android/AndroidManifest.xml index 62208bf8b..22d585242 100644 --- a/src/App/Platforms/Android/AndroidManifest.xml +++ b/src/App/Platforms/Android/AndroidManifest.xml @@ -1,5 +1,5 @@  - + diff --git a/src/App/Platforms/Android/Constants.cs b/src/App/Platforms/Android/Constants.cs index 398bfb708..b6fe75d1e 100644 --- a/src/App/Platforms/Android/Constants.cs +++ b/src/App/Platforms/Android/Constants.cs @@ -3,5 +3,11 @@ public static class Constants { public const string PACKAGE_NAME = "com.x8bit.bitwarden"; + public const string TEMP_CAMERA_IMAGE_NAME = "temp_camera_image.jpg"; + + /// + /// This directory must also be declared in filepaths.xml + /// + public const string TEMP_CAMERA_IMAGE_DIR = "camera_temp"; } } 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/App/Platforms/Android/MainActivity.cs b/src/App/Platforms/Android/MainActivity.cs index 1f5f0fc51..ea72a165c 100644 --- a/src/App/Platforms/Android/MainActivity.cs +++ b/src/App/Platforms/Android/MainActivity.cs @@ -235,18 +235,22 @@ namespace Bit.Droid string fileName = null; if (data != null && data.Data != null) { - uri = data.Data; - fileName = AndroidHelpers.GetFileName(ApplicationContext, uri); + if (data.Data.ToString()?.Contains(Constants.PACKAGE_NAME) != true) + { + uri = data.Data; + fileName = AndroidHelpers.GetFileName(ApplicationContext, uri); + } } else { // camera - var file = new Java.IO.File(FilesDir, "temp_camera_photo.jpg"); + var tmpDir = new Java.IO.File(FilesDir, Constants.TEMP_CAMERA_IMAGE_DIR); + var file = new Java.IO.File(tmpDir, Constants.TEMP_CAMERA_IMAGE_NAME); uri = FileProvider.GetUriForFile(this, "com.x8bit.bitwarden.fileprovider", file); fileName = $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg"; } - if (uri == null) + if (uri == null || fileName == null) { return; } diff --git a/src/App/Platforms/Android/Resources/values/styles.xml b/src/App/Platforms/Android/Resources/values/styles.xml index 7cfde6e72..dadf1819c 100644 --- a/src/App/Platforms/Android/Resources/values/styles.xml +++ b/src/App/Platforms/Android/Resources/values/styles.xml @@ -4,8 +4,8 @@