From 721b434acddfa2c8385e526de04c5ad46be46017 Mon Sep 17 00:00:00 2001 From: eriklimakc Date: Fri, 4 Oct 2024 13:00:46 +0100 Subject: [PATCH] chore: Update MediaGallery to Uno.Sdk 5.5 --- .../src/MediaGallerySample/MainPage.xaml | 37 ++-- .../MediaGallery/MediaFileType.cs | 16 +- .../MediaGallery/MediaGallery.Android.cs | 180 +++++++++--------- .../MediaGallery/MediaGallery.cs | 54 +++--- .../MediaGallery/MediaGallery.iOS.cs | 132 ++++++------- .../MediaGallerySample.csproj | 5 +- UI/MediaGallery/src/global.json | 2 +- 7 files changed, 219 insertions(+), 207 deletions(-) diff --git a/UI/MediaGallery/src/MediaGallerySample/MainPage.xaml b/UI/MediaGallery/src/MediaGallerySample/MainPage.xaml index 664480438..1f2c7aa35 100644 --- a/UI/MediaGallery/src/MediaGallerySample/MainPage.xaml +++ b/UI/MediaGallery/src/MediaGallerySample/MainPage.xaml @@ -1,16 +1,25 @@ - - - - - - + + + + + + + diff --git a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaFileType.cs b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaFileType.cs index e3b85c050..04eac2713 100644 --- a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaFileType.cs +++ b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaFileType.cs @@ -6,14 +6,14 @@ namespace Uno.Samples; /// public enum MediaFileType { - /// - /// Image media file type. - /// - Image, + /// + /// Image media file type. + /// + Image, - /// - /// Video media file type. - /// - Video, + /// + /// Video media file type. + /// + Video, } #endif diff --git a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.Android.cs b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.Android.cs index f6a4da43b..2f9961727 100644 --- a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.Android.cs +++ b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.Android.cs @@ -20,105 +20,105 @@ namespace Uno.Samples; partial class MediaGallery { - private static async Task CheckAccessAsyncImpl() - { - if ((int)Build.VERSION.SdkInt < 29) - { - return await PermissionsHelper.CheckWriteExternalStoragePermission(default); - } - else - { - return true; - } - } - - private static async Task SavePlatformAsyncImpl(MediaFileType type, Stream sourceStream, string targetFileName) - { - var context = Android.App.Application.Context; - var contentResolver = context.ContentResolver ?? throw new InvalidOperationException("ContentResolver is not set."); - - var appFolderName = Package.Current.DisplayName; - // Ensure folder name is file system safe - appFolderName = string.Join("_", appFolderName.Split(Path.GetInvalidFileNameChars())); - - var dateTimeNow = DateTime.Now; - - var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(targetFileName); - var extension = Path.GetExtension(targetFileName).ToLower(); - - using var values = new ContentValues(); - - values.Put(IMediaColumns.DateAdded, GetUnixTimestampInSeconds(dateTimeNow)); - values.Put(IMediaColumns.Title, fileNameWithoutExtension); - values.Put(IMediaColumns.DisplayName, targetFileName); - - var mimeTypeMap = MimeTypeMap.Singleton ?? throw new InvalidOperationException("MimeTypeMap is not set."); - - var mimeType = mimeTypeMap.GetMimeTypeFromExtension(extension.Replace(".", string.Empty)); - if (!string.IsNullOrWhiteSpace(mimeType)) - values.Put(IMediaColumns.MimeType, mimeType); - - using var externalContentUri = type == MediaFileType.Image - ? Images.Media.ExternalContentUri - : Video.Media.ExternalContentUri; - - if (externalContentUri is null) - { - throw new InvalidOperationException($"External Content URI for {type} is not available."); - } - - var relativePath = type == MediaFileType.Image - ? Environment.DirectoryPictures - : Environment.DirectoryMovies; - - if (relativePath is null) - { - throw new InvalidOperationException($"Relative path for {type} is not available."); - } - - if ((int)Build.VERSION.SdkInt >= 29) - { - values.Put(IMediaColumns.RelativePath, Path.Combine(relativePath, appFolderName)); - values.Put(IMediaColumns.IsPending, true); - - using var uri = contentResolver.Insert(externalContentUri, values) ?? - throw new InvalidOperationException("Could not generate new content URI"); - - using var stream = contentResolver.OpenOutputStream(uri) ?? - throw new InvalidOperationException("Could not open output stream"); - - await sourceStream.CopyToAsync(stream); - stream.Close(); - - values.Put(IMediaColumns.IsPending, false); - context.ContentResolver.Update(uri, values, null, null); - } - else - { + private static async Task CheckAccessAsyncImpl() + { + if ((int)Build.VERSION.SdkInt < 29) + { + return await PermissionsHelper.CheckWriteExternalStoragePermission(default); + } + else + { + return true; + } + } + + private static async Task SavePlatformAsyncImpl(MediaFileType type, Stream sourceStream, string targetFileName) + { + var context = Android.App.Application.Context; + var contentResolver = context.ContentResolver ?? throw new InvalidOperationException("ContentResolver is not set."); + + var appFolderName = Package.Current.DisplayName; + // Ensure folder name is file system safe + appFolderName = string.Join("_", appFolderName.Split(Path.GetInvalidFileNameChars())); + + var dateTimeNow = DateTime.Now; + + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(targetFileName); + var extension = Path.GetExtension(targetFileName).ToLower(); + + using var values = new ContentValues(); + + values.Put(IMediaColumns.DateAdded, GetUnixTimestampInSeconds(dateTimeNow)); + values.Put(IMediaColumns.Title, fileNameWithoutExtension); + values.Put(IMediaColumns.DisplayName, targetFileName); + + var mimeTypeMap = MimeTypeMap.Singleton ?? throw new InvalidOperationException("MimeTypeMap is not set."); + + var mimeType = mimeTypeMap.GetMimeTypeFromExtension(extension.Replace(".", string.Empty)); + if (!string.IsNullOrWhiteSpace(mimeType)) + values.Put(IMediaColumns.MimeType, mimeType); + + using var externalContentUri = type == MediaFileType.Image + ? Images.Media.ExternalContentUri + : Video.Media.ExternalContentUri; + + if (externalContentUri is null) + { + throw new InvalidOperationException($"External Content URI for {type} is not available."); + } + + var relativePath = type == MediaFileType.Image + ? Environment.DirectoryPictures + : Environment.DirectoryMovies; + + if (relativePath is null) + { + throw new InvalidOperationException($"Relative path for {type} is not available."); + } + + if ((int)Build.VERSION.SdkInt >= 29) + { + values.Put(IMediaColumns.RelativePath, Path.Combine(relativePath, appFolderName)); + values.Put(IMediaColumns.IsPending, true); + + using var uri = contentResolver.Insert(externalContentUri, values) ?? + throw new InvalidOperationException("Could not generate new content URI"); + + using var stream = contentResolver.OpenOutputStream(uri) ?? + throw new InvalidOperationException("Could not open output stream"); + + await sourceStream.CopyToAsync(stream); + stream.Close(); + + values.Put(IMediaColumns.IsPending, false); + context.ContentResolver.Update(uri, values, null, null); + } + else + { #pragma warning disable CS0618 // Type or member is obsolete - using var directory = new NativeFile(Environment.GetExternalStoragePublicDirectory(relativePath), appFolderName); - directory.Mkdirs(); + using var directory = new NativeFile(Environment.GetExternalStoragePublicDirectory(relativePath), appFolderName); + directory.Mkdirs(); - using var file = new NativeFile(directory, targetFileName); + using var file = new NativeFile(directory, targetFileName); - using var fileOutputStream = System.IO.File.Create(file.AbsolutePath); - await sourceStream.CopyToAsync(fileOutputStream); - fileOutputStream.Close(); + using var fileOutputStream = System.IO.File.Create(file.AbsolutePath); + await sourceStream.CopyToAsync(fileOutputStream); + fileOutputStream.Close(); - values.Put(IMediaColumns.Data, file.AbsolutePath); - contentResolver.Insert(externalContentUri, values); + values.Put(IMediaColumns.Data, file.AbsolutePath); + contentResolver.Insert(externalContentUri, values); #pragma warning disable CA1422 // Validate platform compatibility - using var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile); + using var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile); #pragma warning restore CA1422 // Validate platform compatibility - mediaScanIntent.SetData(NativeUri.FromFile(file)); - context.SendBroadcast(mediaScanIntent); + mediaScanIntent.SetData(NativeUri.FromFile(file)); + context.SendBroadcast(mediaScanIntent); #pragma warning restore CS0618 // Type or member is obsolete - } - } + } + } - private static long GetUnixTimestampInSeconds(DateTime current) => (long)GetTimeDifference(current).TotalSeconds; + private static long GetUnixTimestampInSeconds(DateTime current) => (long)GetTimeDifference(current).TotalSeconds; - private static TimeSpan GetTimeDifference(DateTime current) => current.ToUniversalTime() - DateTime.UnixEpoch; + private static TimeSpan GetTimeDifference(DateTime current) => current.ToUniversalTime() - DateTime.UnixEpoch; } #endif diff --git a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.cs b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.cs index 123a05540..d9a0b23f3 100644 --- a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.cs +++ b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.cs @@ -13,34 +13,34 @@ namespace Uno.Samples; /// public static partial class MediaGallery { - /// - /// Checks the user's permission to access the device's gallery. - /// Will trigger the permission request if not already granted. - /// - /// A value indicating whether the user has access. - public static async Task CheckAccessAsync() => await CheckAccessAsyncImpl(); + /// + /// Checks the user's permission to access the device's gallery. + /// Will trigger the permission request if not already granted. + /// + /// A value indicating whether the user has access. + public static async Task CheckAccessAsync() => await CheckAccessAsyncImpl(); - /// - /// Saves a media file to the device's gallery. - /// - /// Media file type. - /// Byte array representing the file. - /// Target file name. - /// Task representing the progress of the operation. - public static async Task SaveAsync(MediaFileType type, byte[] data, string targetFileName) - { - using var memoryStream = new MemoryStream(data); - await SaveAsync(type, memoryStream, targetFileName); - } + /// + /// Saves a media file to the device's gallery. + /// + /// Media file type. + /// Byte array representing the file. + /// Target file name. + /// Task representing the progress of the operation. + public static async Task SaveAsync(MediaFileType type, byte[] data, string targetFileName) + { + using var memoryStream = new MemoryStream(data); + await SaveAsync(type, memoryStream, targetFileName); + } - /// - /// Saves a media file to the device's gallery. - /// - /// Media file type. - /// Stream representing the file. - /// Target file name. - /// Task representing the progress of the operation. - public static async Task SaveAsync(MediaFileType type, Stream stream, string targetFileName) => - await SavePlatformAsyncImpl(type, stream, targetFileName); + /// + /// Saves a media file to the device's gallery. + /// + /// Media file type. + /// Stream representing the file. + /// Target file name. + /// Task representing the progress of the operation. + public static async Task SaveAsync(MediaFileType type, Stream stream, string targetFileName) => + await SavePlatformAsyncImpl(type, stream, targetFileName); } #endif diff --git a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.iOS.cs b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.iOS.cs index 5a7dd8a93..14dcc3731 100644 --- a/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.iOS.cs +++ b/UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.iOS.cs @@ -10,87 +10,87 @@ namespace Uno.Samples; public partial class MediaGallery { - private static readonly string _requiredInfoPlistKey = CheckSystemVersion(14) ? "NSPhotoLibraryAddUsageDescription" : "NSPhotoLibraryUsageDescription"; + private static readonly string _requiredInfoPlistKey = CheckSystemVersion(14) ? "NSPhotoLibraryAddUsageDescription" : "NSPhotoLibraryUsageDescription"; - private static async Task CheckAccessAsyncImpl() - { - if (!NSBundle.MainBundle.InfoDictionary.ContainsKey(new NSString(_requiredInfoPlistKey))) - { - throw new InvalidOperationException($"The Info.plist file is missing the required key '{_requiredInfoPlistKey}'."); - } + private static async Task CheckAccessAsyncImpl() + { + if (!NSBundle.MainBundle.InfoDictionary.ContainsKey(new NSString(_requiredInfoPlistKey))) + { + throw new InvalidOperationException($"The Info.plist file is missing the required key '{_requiredInfoPlistKey}'."); + } - var authorizationStatus = CheckSystemVersion(14) ? - PHPhotoLibrary.GetAuthorizationStatus(PHAccessLevel.AddOnly) : + var authorizationStatus = CheckSystemVersion(14) ? + PHPhotoLibrary.GetAuthorizationStatus(PHAccessLevel.AddOnly) : #pragma warning disable CA1422 // Deprecated API - PHPhotoLibrary.AuthorizationStatus; + PHPhotoLibrary.AuthorizationStatus; #pragma warning restore CA1422 // Deprecated API - if (!IsAuthorized(authorizationStatus)) - { - authorizationStatus = CheckSystemVersion(14) ? - await PHPhotoLibrary.RequestAuthorizationAsync(PHAccessLevel.AddOnly) : + if (!IsAuthorized(authorizationStatus)) + { + authorizationStatus = CheckSystemVersion(14) ? + await PHPhotoLibrary.RequestAuthorizationAsync(PHAccessLevel.AddOnly) : #pragma warning disable CA1422 // Deprecated API - await PHPhotoLibrary.RequestAuthorizationAsync(); + await PHPhotoLibrary.RequestAuthorizationAsync(); #pragma warning restore CA1422 // Deprecated API - } + } - return IsAuthorized(authorizationStatus); - } + return IsAuthorized(authorizationStatus); + } - private static async Task SavePlatformAsyncImpl(MediaFileType type, Stream sourceStream, string targetFileName) - { - var tempFile = Path.Combine(Path.GetTempPath(), targetFileName); - try - { - // Write stream copy to temp - using var fileStream = File.Create(tempFile); - await sourceStream.CopyToAsync(fileStream); + private static async Task SavePlatformAsyncImpl(MediaFileType type, Stream sourceStream, string targetFileName) + { + var tempFile = Path.Combine(Path.GetTempPath(), targetFileName); + try + { + // Write stream copy to temp + using var fileStream = File.Create(tempFile); + await sourceStream.CopyToAsync(fileStream); - // get the file uri - var fileUri = new NSUrl(tempFile); + // get the file uri + var fileUri = new NSUrl(tempFile); - await PhotoLibraryPerformChanges(() => - { - using var request = type == MediaFileType.Image ? - PHAssetChangeRequest.FromImage(fileUri) : - PHAssetChangeRequest.FromVideo(fileUri); - }).ConfigureAwait(false); - } - finally - { - // Attempt to delete the temp file - File.Delete(tempFile); - } - } + await PhotoLibraryPerformChanges(() => + { + using var request = type == MediaFileType.Image ? + PHAssetChangeRequest.FromImage(fileUri) : + PHAssetChangeRequest.FromVideo(fileUri); + }).ConfigureAwait(false); + } + finally + { + // Attempt to delete the temp file + File.Delete(tempFile); + } + } - static async Task PhotoLibraryPerformChanges(Action action) - { - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + static async Task PhotoLibraryPerformChanges(Action action) + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - PHPhotoLibrary.SharedPhotoLibrary.PerformChanges( - () => - { - try - { - action.Invoke(); - } - catch (Exception ex) - { - tcs.TrySetResult(ex); - } - }, - (success, error) => - tcs.TrySetResult(error is not null ? new NSErrorException(error) : null)); + PHPhotoLibrary.SharedPhotoLibrary.PerformChanges( + () => + { + try + { + action.Invoke(); + } + catch (Exception ex) + { + tcs.TrySetResult(ex); + } + }, + (success, error) => + tcs.TrySetResult(error is not null ? new NSErrorException(error) : null)); - var exception = await tcs.Task; - if (exception is not null) - { - throw exception; - } - } + var exception = await tcs.Task; + if (exception is not null) + { + throw exception; + } + } - private static bool CheckSystemVersion(int major) => UIDevice.CurrentDevice.CheckSystemVersion(major, 0); + private static bool CheckSystemVersion(int major) => UIDevice.CurrentDevice.CheckSystemVersion(major, 0); - private static bool IsAuthorized(PHAuthorizationStatus status) => status is PHAuthorizationStatus.Authorized or PHAuthorizationStatus.Limited; + private static bool IsAuthorized(PHAuthorizationStatus status) => status is PHAuthorizationStatus.Authorized or PHAuthorizationStatus.Limited; } #endif diff --git a/UI/MediaGallery/src/MediaGallerySample/MediaGallerySample.csproj b/UI/MediaGallery/src/MediaGallerySample/MediaGallerySample.csproj index f3adc7e0a..59ab52a70 100644 --- a/UI/MediaGallery/src/MediaGallerySample/MediaGallerySample.csproj +++ b/UI/MediaGallery/src/MediaGallerySample/MediaGallerySample.csproj @@ -27,7 +27,10 @@ the "Microsoft.Windows.SDK.BuildTools" package above, and the "revision" version number must be the highest found in https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref. --> - + + + + 10.0.19041.38