Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Update MediaGallery to Uno.Sdk 5.5 #843

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
37 changes: 23 additions & 14 deletions UI/MediaGallery/src/MediaGallerySample/MainPage.xaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
<Page
x:Class="MediaGallerySample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MediaGallerySample"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="8">
<TextBlock Style="{ThemeResource TitleTextBlockStyle}" Text="MediaGallery Sample" />
<Button HorizontalAlignment="Center" Click="CheckAccessClick">Check access</Button>
<Button HorizontalAlignment="Center" Click="SaveClick">Save UnoLogo.png to gallery</Button>
<Button HorizontalAlignment="Center" Click="SaveRandomNameClick">Save with random name to gallery</Button>
<Page x:Class="MediaGallerySample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MediaGallerySample"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="8">
<TextBlock Style="{ThemeResource TitleTextBlockStyle}"
Text="MediaGallery Sample" />
<Button HorizontalAlignment="Center"
Click="CheckAccessClick">
Check access
</Button>
<Button HorizontalAlignment="Center"
Click="SaveClick">
Save UnoLogo.png to gallery
</Button>
<Button HorizontalAlignment="Center"
Click="SaveRandomNameClick">
Save with random name to gallery
</Button>
</StackPanel>
</Page>
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ namespace Uno.Samples;
/// </summary>
public enum MediaFileType
{
/// <summary>
/// Image media file type.
/// </summary>
Image,
/// <summary>
/// Image media file type.
/// </summary>
Image,

/// <summary>
/// Video media file type.
/// </summary>
Video,
/// <summary>
/// Video media file type.
/// </summary>
Video,
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -20,105 +20,105 @@ namespace Uno.Samples;

partial class MediaGallery
{
private static async Task<bool> 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<bool> 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
54 changes: 27 additions & 27 deletions UI/MediaGallery/src/MediaGallerySample/MediaGallery/MediaGallery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,34 @@ namespace Uno.Samples;
/// </summary>
public static partial class MediaGallery
{
/// <summary>
/// Checks the user's permission to access the device's gallery.
/// Will trigger the permission request if not already granted.
/// </summary>
/// <returns>A value indicating whether the user has access.</returns>
public static async Task<bool> CheckAccessAsync() => await CheckAccessAsyncImpl();
/// <summary>
/// Checks the user's permission to access the device's gallery.
/// Will trigger the permission request if not already granted.
/// </summary>
/// <returns>A value indicating whether the user has access.</returns>
public static async Task<bool> CheckAccessAsync() => await CheckAccessAsyncImpl();

/// <summary>
/// Saves a media file to the device's gallery.
/// </summary>
/// <param name="type">Media file type.</param>
/// <param name="data">Byte array representing the file.</param>
/// <param name="targetFileName">Target file name.</param>
/// <returns>Task representing the progress of the operation.</returns>
public static async Task SaveAsync(MediaFileType type, byte[] data, string targetFileName)
{
using var memoryStream = new MemoryStream(data);
await SaveAsync(type, memoryStream, targetFileName);
}
/// <summary>
/// Saves a media file to the device's gallery.
/// </summary>
/// <param name="type">Media file type.</param>
/// <param name="data">Byte array representing the file.</param>
/// <param name="targetFileName">Target file name.</param>
/// <returns>Task representing the progress of the operation.</returns>
public static async Task SaveAsync(MediaFileType type, byte[] data, string targetFileName)
{
using var memoryStream = new MemoryStream(data);
await SaveAsync(type, memoryStream, targetFileName);
}

/// <summary>
/// Saves a media file to the device's gallery.
/// </summary>
/// <param name="type">Media file type.</param>
/// <param name="stream">Stream representing the file.</param>
/// <param name="targetFileName">Target file name.</param>
/// <returns>Task representing the progress of the operation.</returns>
public static async Task SaveAsync(MediaFileType type, Stream stream, string targetFileName) =>
await SavePlatformAsyncImpl(type, stream, targetFileName);
/// <summary>
/// Saves a media file to the device's gallery.
/// </summary>
/// <param name="type">Media file type.</param>
/// <param name="stream">Stream representing the file.</param>
/// <param name="targetFileName">Target file name.</param>
/// <returns>Task representing the progress of the operation.</returns>
public static async Task SaveAsync(MediaFileType type, Stream stream, string targetFileName) =>
await SavePlatformAsyncImpl(type, stream, targetFileName);
}
#endif
Loading