diff --git a/Softeq.XToolkit.Bindings.Droid/Softeq.XToolkit.Bindings.Droid.csproj b/Softeq.XToolkit.Bindings.Droid/Softeq.XToolkit.Bindings.Droid.csproj index 0e094a94..d385c33e 100644 --- a/Softeq.XToolkit.Bindings.Droid/Softeq.XToolkit.Bindings.Droid.csproj +++ b/Softeq.XToolkit.Bindings.Droid/Softeq.XToolkit.Bindings.Droid.csproj @@ -1,7 +1,7 @@ - net8.0-android34.0 + net9.0-android 21.0 diff --git a/Softeq.XToolkit.Common.Droid.Tests/Softeq.XToolkit.Common.Droid.Tests.csproj b/Softeq.XToolkit.Common.Droid.Tests/Softeq.XToolkit.Common.Droid.Tests.csproj index 8cc240cf..af22fd38 100644 --- a/Softeq.XToolkit.Common.Droid.Tests/Softeq.XToolkit.Common.Droid.Tests.csproj +++ b/Softeq.XToolkit.Common.Droid.Tests/Softeq.XToolkit.Common.Droid.Tests.csproj @@ -1,7 +1,7 @@ - net8.0-android34.0 + net9.0-android Exe true disable diff --git a/Softeq.XToolkit.Common.Droid/Softeq.XToolkit.Common.Droid.csproj b/Softeq.XToolkit.Common.Droid/Softeq.XToolkit.Common.Droid.csproj index 60be0cbf..89285a00 100644 --- a/Softeq.XToolkit.Common.Droid/Softeq.XToolkit.Common.Droid.csproj +++ b/Softeq.XToolkit.Common.Droid/Softeq.XToolkit.Common.Droid.csproj @@ -1,7 +1,7 @@  - net8.0-android34.0 + net9.0-android 21.0 diff --git a/Softeq.XToolkit.Connectivity/Softeq.XToolkit.Connectivity.csproj b/Softeq.XToolkit.Connectivity/Softeq.XToolkit.Connectivity.csproj index 4f8f16ac..978b0aba 100644 --- a/Softeq.XToolkit.Connectivity/Softeq.XToolkit.Connectivity.csproj +++ b/Softeq.XToolkit.Connectivity/Softeq.XToolkit.Connectivity.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net9.0 diff --git a/Softeq.XToolkit.Permissions.Droid/Softeq.XToolkit.Permissions.Droid.csproj b/Softeq.XToolkit.Permissions.Droid/Softeq.XToolkit.Permissions.Droid.csproj index e44ebd58..bc76546d 100644 --- a/Softeq.XToolkit.Permissions.Droid/Softeq.XToolkit.Permissions.Droid.csproj +++ b/Softeq.XToolkit.Permissions.Droid/Softeq.XToolkit.Permissions.Droid.csproj @@ -1,7 +1,7 @@ - net8.0-android34.0 + net9.0-android 21.0 diff --git a/Softeq.XToolkit.PushNotifications.Droid/Softeq.XToolkit.PushNotifications.Droid.csproj b/Softeq.XToolkit.PushNotifications.Droid/Softeq.XToolkit.PushNotifications.Droid.csproj index 14bac5d7..67674f40 100644 --- a/Softeq.XToolkit.PushNotifications.Droid/Softeq.XToolkit.PushNotifications.Droid.csproj +++ b/Softeq.XToolkit.PushNotifications.Droid/Softeq.XToolkit.PushNotifications.Droid.csproj @@ -1,7 +1,7 @@ - net8.0-android34.0 + net9.0-android 26.0 diff --git a/Softeq.XToolkit.Remote/Softeq.XToolkit.Remote.csproj b/Softeq.XToolkit.Remote/Softeq.XToolkit.Remote.csproj index d01a5171..e1a6c063 100644 --- a/Softeq.XToolkit.Remote/Softeq.XToolkit.Remote.csproj +++ b/Softeq.XToolkit.Remote/Softeq.XToolkit.Remote.csproj @@ -1,7 +1,7 @@ - net8.0;net9.0 + net9.0 diff --git a/Softeq.XToolkit.WhiteLabel.Droid/Softeq.XToolkit.WhiteLabel.Droid.csproj b/Softeq.XToolkit.WhiteLabel.Droid/Softeq.XToolkit.WhiteLabel.Droid.csproj index 384b8d28..c2c8a311 100644 --- a/Softeq.XToolkit.WhiteLabel.Droid/Softeq.XToolkit.WhiteLabel.Droid.csproj +++ b/Softeq.XToolkit.WhiteLabel.Droid/Softeq.XToolkit.WhiteLabel.Droid.csproj @@ -1,7 +1,7 @@ - net8.0-android34.0 + net9.0-android 24.0 diff --git a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/DroidImagePickerService.cs b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/DroidImagePickerService.cs index 6935a438..4e4aa257 100644 --- a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/DroidImagePickerService.cs +++ b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/DroidImagePickerService.cs @@ -4,21 +4,26 @@ using System.Threading.Tasks; using Android.Content; using Android.Graphics; +using Android.Net; +using AndroidX.Activity.Result; +using AndroidX.Activity.Result.Contract; +using AndroidX.AppCompat.App; using Softeq.XToolkit.Permissions; using Softeq.XToolkit.WhiteLabel.Droid.Providers; using Softeq.XToolkit.WhiteLabel.Essentials.ImagePicker; using CameraPermission = Microsoft.Maui.ApplicationModel.Permissions.Camera; using PermissionStatus = Softeq.XToolkit.Permissions.PermissionStatus; -using PhotosPermission = Microsoft.Maui.ApplicationModel.Permissions.Photos; namespace Softeq.XToolkit.WhiteLabel.Essentials.Droid.ImagePicker { - public class DroidImagePickerService : IImagePickerService + public class DroidImagePickerService : Java.Lang.Object, IImagePickerService, IActivityResultCallback { private readonly IPermissionsManager _permissionsManager; private readonly IContextProvider _contextProvider; + private readonly ActivityResultLauncher? _activityResultLauncher; - private TaskCompletionSource? _taskCompletionSource; + private TaskCompletionSource? _pickPhotoFileUriCompletionSource; + private TaskCompletionSource? _bitmapTaskCompletionSource; public DroidImagePickerService( IPermissionsManager permissionsManager, @@ -26,15 +31,30 @@ public DroidImagePickerService( { _permissionsManager = permissionsManager; _contextProvider = contextProvider; + if (ActivityResultContracts.PickVisualMedia.InvokeIsPhotoPickerAvailable(_contextProvider.CurrentActivity) + && _contextProvider.CurrentActivity is AppCompatActivity appCompatActivity) + { + _activityResultLauncher = appCompatActivity.RegisterForActivityResult( + new ActivityResultContracts.PickVisualMedia(), this); + } } public async Task PickPhotoAsync(float quality) { - var permissionStatus = await _permissionsManager.CheckWithRequestAsync().ConfigureAwait(false); - - if (permissionStatus != PermissionStatus.Granted) + if (_activityResultLauncher != null) { - return null; + _pickPhotoFileUriCompletionSource = new TaskCompletionSource(); + + var request = new PickVisualMediaRequest.Builder() + .SetMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.Instance) + .Build(); + _activityResultLauncher.Launch(request); + + var fileUri = await _pickPhotoFileUriCompletionSource.Task + .ConfigureAwait(false); + + return await GetImageAsync(ImagePickerMode.ImageCropOnly, quality, fileUri) + .ConfigureAwait(false); } return await GetImageAsync(ImagePickerMode.Gallery, quality).ConfigureAwait(false); @@ -52,20 +72,25 @@ public DroidImagePickerService( return await GetImageAsync(ImagePickerMode.Camera, quality).ConfigureAwait(false); } - private async Task GetImageAsync(int mode, float quality) + private async Task GetImageAsync(int mode, float quality, Uri? fileUri = null) { var activity = _contextProvider.CurrentActivity; var intent = new Intent(activity, typeof(ImagePickerActivity)); intent.PutExtra(ImagePickerActivity.ModeKey, mode); - _taskCompletionSource = new TaskCompletionSource(); + if (mode == ImagePickerMode.ImageCropOnly) + { + intent.SetData(fileUri); + } + + _bitmapTaskCompletionSource = new TaskCompletionSource(); ImagePickerActivity.ImagePicked += OnImagePicked; activity.StartActivity(intent); - var bitmap = await _taskCompletionSource.Task.ConfigureAwait(false); + var bitmap = await _bitmapTaskCompletionSource.Task.ConfigureAwait(false); return new DroidImagePickerResult { @@ -78,7 +103,13 @@ private async Task GetImageAsync(int mode, float quality) private void OnImagePicked(object? sender, Bitmap? e) { ImagePickerActivity.ImagePicked -= OnImagePicked; - _taskCompletionSource!.SetResult(e); + _bitmapTaskCompletionSource!.SetResult(e); + } + + void IActivityResultCallback.OnActivityResult(Java.Lang.Object? result) + { + var uri = result as Uri; + _pickPhotoFileUriCompletionSource!.TrySetResult(uri); } } } diff --git a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/IImagePickerActivityResultHandler.cs b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/IImagePickerActivityResultHandler.cs index 743f8a66..64027e88 100644 --- a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/IImagePickerActivityResultHandler.cs +++ b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/IImagePickerActivityResultHandler.cs @@ -11,9 +11,9 @@ namespace Softeq.XToolkit.WhiteLabel.Essentials.Droid.ImagePicker { public interface IImagePickerActivityResultHandler { - Task HandleImagePickerCameraResultAsync(Activity activity, Result resultCode, Uri? fileUri); + Task HandleImagePickerCameraResultAsync(Activity activity, Uri? fileUri); - Task HandleImagePickerGalleryResultAsync(Activity activity, Result resultCode, Intent? data); + Task HandleImagePickerGalleryResultAsync(Activity activity, Uri? fileUri); Task HandleCustomResultAsync(int requestCode, Result resultCode, Intent? data); } diff --git a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivity.cs b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivity.cs index d5f68dfd..2c6288c3 100644 --- a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivity.cs +++ b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivity.cs @@ -15,7 +15,6 @@ using Softeq.XToolkit.Common.Extensions; using Softeq.XToolkit.WhiteLabel.Droid.Providers; using AUri = Android.Net.Uri; -using ImageOrientation = Android.Media.Orientation; using IOException = System.IO.IOException; namespace Softeq.XToolkit.WhiteLabel.Essentials.Droid.ImagePicker @@ -26,6 +25,7 @@ public static class ImagePickerMode { public const int Camera = 1; public const int Gallery = 2; + public const int ImageCropOnly = 3; } [Activity] @@ -36,6 +36,8 @@ internal class ImagePickerActivity : Activity private const string ImagesFolder = "Pictures"; private const string CameraFileUriKey = "CameraFileUri"; + private readonly IImagePickerActivityResultHandler _handler = Dependencies.Container.Resolve(); + private AUri? _fileUri; private Intent _pickIntent = default!; @@ -75,6 +77,9 @@ protected override void OnCreate(Bundle? savedInstanceState) case ImagePickerMode.Gallery: PickImage(); break; + case ImagePickerMode.ImageCropOnly: + HandleImagePickerGalleryResultAsync(Intent.Data).FireAndForget(); + break; } } catch @@ -83,10 +88,18 @@ protected override void OnCreate(Bundle? savedInstanceState) } finally { - _pickIntent.Dispose(); + _pickIntent?.Dispose(); } } + private async Task HandleImagePickerGalleryResultAsync(AUri? fileUri) + { + var bitmap = await _handler + .HandleImagePickerGalleryResultAsync(this, fileUri) + .ConfigureAwait(false); + OnImagePicked(bitmap); + } + protected override void OnSaveInstanceState(Bundle outState) { if (_fileUri != null) @@ -104,25 +117,34 @@ protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result private async Task HandleOnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent? data) { - var handler = Dependencies.Container.Resolve(); - if (requestCode == ImagePickerMode.Camera) { - var bitmap = await handler - .HandleImagePickerCameraResultAsync(this, resultCode, _fileUri) - .ConfigureAwait(false); - OnImagePicked(bitmap); + if (resultCode == Result.Ok) + { + var bitmap = await _handler + .HandleImagePickerCameraResultAsync(this, _fileUri) + .ConfigureAwait(false); + OnImagePicked(bitmap); + } + else + { + OnImagePicked(null); + } } else if (requestCode == ImagePickerMode.Gallery) { - var bitmap = await handler - .HandleImagePickerGalleryResultAsync(this, resultCode, data) - .ConfigureAwait(false); - OnImagePicked(bitmap); + if (resultCode == Result.Ok) + { + HandleImagePickerGalleryResultAsync(data?.Data).FireAndForget(); + } + else + { + OnImagePicked(null); + } } else { - handler.HandleCustomResultAsync(requestCode, resultCode, data).FireAndForget(); + _handler.HandleCustomResultAsync(requestCode, resultCode, data).FireAndForget(); } } @@ -143,7 +165,9 @@ private void CaptureCamera() private void PickImage() { - _pickIntent = new Intent(Intent.ActionPick); + _pickIntent = new Intent(Intent.ActionOpenDocument); + _pickIntent.AddCategory(Intent.CategoryOpenable); + _pickIntent.AddFlags(ActivityFlags.GrantReadUriPermission); _pickIntent.SetType("image/*"); StartActivityForResult(_pickIntent, ImagePickerMode.Gallery); } diff --git a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivityResultHandler.cs b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivityResultHandler.cs index 8cee6cee..364e2103 100644 --- a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivityResultHandler.cs +++ b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/ImagePicker/ImagePickerActivityResultHandler.cs @@ -13,9 +13,9 @@ namespace Softeq.XToolkit.WhiteLabel.Essentials.Droid.ImagePicker { public sealed class ImagePickerActivityResultHandler : IImagePickerActivityResultHandler { - public Task HandleImagePickerCameraResultAsync(Activity activity, Result resultCode, Uri? fileUri) + public Task HandleImagePickerCameraResultAsync(Activity activity, Uri? fileUri) { - if (resultCode == Result.Ok && fileUri != null) + if (fileUri != null) { return GetBitmapFromUriAsync(activity, fileUri); } @@ -23,11 +23,9 @@ public sealed class ImagePickerActivityResultHandler : IImagePickerActivityResul return Task.FromResult(default(Bitmap?)); } - public Task HandleImagePickerGalleryResultAsync(Activity activity, Result resultCode, Intent? data) + public Task HandleImagePickerGalleryResultAsync(Activity activity, Uri? fileUri) { - var fileUri = data?.Data; - - if (resultCode == Result.Ok && fileUri != null) + if (fileUri != null) { return GetBitmapFromUriAsync(activity, fileUri); } diff --git a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/Softeq.XToolkit.WhiteLabel.Essentials.Droid.csproj b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/Softeq.XToolkit.WhiteLabel.Essentials.Droid.csproj index 67a72c90..1ff0069e 100644 --- a/Softeq.XToolkit.WhiteLabel.Essentials.Droid/Softeq.XToolkit.WhiteLabel.Essentials.Droid.csproj +++ b/Softeq.XToolkit.WhiteLabel.Essentials.Droid/Softeq.XToolkit.WhiteLabel.Essentials.Droid.csproj @@ -1,7 +1,7 @@ - net8.0-android34.0 + net9.0-android 21.0 @@ -24,6 +24,7 @@ +