Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,57 @@
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<Bitmap?>? _taskCompletionSource;
private TaskCompletionSource<Uri?>? _pickPhotoFileUriCompletionSource;
private TaskCompletionSource<Bitmap?>? _bitmapTaskCompletionSource;

public DroidImagePickerService(
IPermissionsManager permissionsManager,
IContextProvider contextProvider)
{
_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<ImagePickerResult?> PickPhotoAsync(float quality)
{
var permissionStatus = await _permissionsManager.CheckWithRequestAsync<PhotosPermission>().ConfigureAwait(false);

if (permissionStatus != PermissionStatus.Granted)
if (_activityResultLauncher != null)
{
return null;
_pickPhotoFileUriCompletionSource = new TaskCompletionSource<Uri?>();

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);
Expand All @@ -52,20 +72,25 @@ public DroidImagePickerService(
return await GetImageAsync(ImagePickerMode.Camera, quality).ConfigureAwait(false);
}

private async Task<ImagePickerResult> GetImageAsync(int mode, float quality)
private async Task<ImagePickerResult> 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<Bitmap?>();
if (mode == ImagePickerMode.ImageCropOnly)
{
intent.SetData(fileUri);
}

_bitmapTaskCompletionSource = new TaskCompletionSource<Bitmap?>();

ImagePickerActivity.ImagePicked += OnImagePicked;

activity.StartActivity(intent);

var bitmap = await _taskCompletionSource.Task.ConfigureAwait(false);
var bitmap = await _bitmapTaskCompletionSource.Task.ConfigureAwait(false);

return new DroidImagePickerResult
{
Expand All @@ -78,7 +103,13 @@ private async Task<ImagePickerResult> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ namespace Softeq.XToolkit.WhiteLabel.Essentials.Droid.ImagePicker
{
public interface IImagePickerActivityResultHandler
{
Task<Bitmap?> HandleImagePickerCameraResultAsync(Activity activity, Result resultCode, Uri? fileUri);
Task<Bitmap?> HandleImagePickerCameraResultAsync(Activity activity, Uri? fileUri);

Task<Bitmap?> HandleImagePickerGalleryResultAsync(Activity activity, Result resultCode, Intent? data);
Task<Bitmap?> HandleImagePickerGalleryResultAsync(Activity activity, Uri? fileUri);

Task HandleCustomResultAsync(int requestCode, Result resultCode, Intent? data);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,6 +25,7 @@ public static class ImagePickerMode
{
public const int Camera = 1;
public const int Gallery = 2;
public const int ImageCropOnly = 3;
}

[Activity]
Expand All @@ -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<IImagePickerActivityResultHandler>();

private AUri? _fileUri;
private Intent _pickIntent = default!;

Expand Down Expand Up @@ -75,6 +77,9 @@ protected override void OnCreate(Bundle? savedInstanceState)
case ImagePickerMode.Gallery:
PickImage();
break;
case ImagePickerMode.ImageCropOnly:
HandleImagePickerGalleryResultAsync(Intent.Data).FireAndForget();
break;
}
}
catch
Expand All @@ -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)
Expand All @@ -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<IImagePickerActivityResultHandler>();

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();
}
}

Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,19 @@ namespace Softeq.XToolkit.WhiteLabel.Essentials.Droid.ImagePicker
{
public sealed class ImagePickerActivityResultHandler : IImagePickerActivityResultHandler
{
public Task<Bitmap?> HandleImagePickerCameraResultAsync(Activity activity, Result resultCode, Uri? fileUri)
public Task<Bitmap?> HandleImagePickerCameraResultAsync(Activity activity, Uri? fileUri)
{
if (resultCode == Result.Ok && fileUri != null)
if (fileUri != null)
{
return GetBitmapFromUriAsync(activity, fileUri);
}

return Task.FromResult(default(Bitmap?));
}

public Task<Bitmap?> HandleImagePickerGalleryResultAsync(Activity activity, Result resultCode, Intent? data)
public Task<Bitmap?> HandleImagePickerGalleryResultAsync(Activity activity, Uri? fileUri)
{
var fileUri = data?.Data;

if (resultCode == Result.Ok && fileUri != null)
if (fileUri != null)
{
return GetBitmapFromUriAsync(activity, fileUri);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Xamarin.AndroidX.Activity" Version="1.8.2.1" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.0" />
<PackageReference Include="Microsoft.Maui.Essentials" Version="$(MauiVersion)" />
</ItemGroup>
Expand Down