Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
c370fe0
Refactor media artwork handling and async updates
ne0rrmatrix Jan 21, 2025
9c3ca06
Refactor and clean up media manager methods
ne0rrmatrix Feb 10, 2025
dadb767
Fix merge conflict
ne0rrmatrix Feb 10, 2025
8d934c6
Merge branch 'CommunityToolkit:main' into MetaDataSource
ne0rrmatrix Feb 10, 2025
49ad676
Fix incorrect logging
ne0rrmatrix Feb 10, 2025
9347a3a
Merge branch 'MetaDataSource' of https://github.com/ne0rrmatrix/MauiO…
ne0rrmatrix Feb 10, 2025
5618e4d
Merge branch 'main' into MetaDataSource
ne0rrmatrix Feb 12, 2025
193ac8a
Fix incorrect accesibility modifier
ne0rrmatrix Feb 12, 2025
a192ebd
Merge branch 'MetaDataSource' of https://github.com/ne0rrmatrix/MauiO…
ne0rrmatrix Feb 12, 2025
0cd2546
Merge branch 'main' into MetaDataSource
ne0rrmatrix Feb 13, 2025
9492160
Merge branch 'main' into MetaDataSource
ne0rrmatrix Feb 15, 2025
8b5176d
Merge branch 'main' into MetaDataSource
ne0rrmatrix Feb 17, 2025
6fcf06f
Merge branch 'main' into MetaDataSource
ne0rrmatrix Mar 7, 2025
f7b18da
Merge branch 'main' into MetaDataSource
ne0rrmatrix Mar 15, 2025
9bcccbb
Merge branch 'main' into MetaDataSource
ne0rrmatrix Apr 1, 2025
4487557
Fix merge Conflict
ne0rrmatrix May 4, 2025
81ad1a8
Revert merge error in file that was not supposed to be changed
ne0rrmatrix May 4, 2025
47979ef
Merge branch 'main' into MetaDataSource
ne0rrmatrix May 7, 2025
00c5378
Fix another merge error
ne0rrmatrix May 7, 2025
6837979
Fix merge conflict
ne0rrmatrix Jun 20, 2025
fb9ac11
Merge branch 'main' into MetaDataSource
ne0rrmatrix Jun 20, 2025
db57d38
Merge branch 'main' into MetaDataSource
ne0rrmatrix Jun 25, 2025
5bfea37
Fix Merge Conflict
ne0rrmatrix Jul 3, 2025
c82cd56
Merge branch 'main' into MetaDataSource
ne0rrmatrix Jul 3, 2025
ba13d17
Merge branch 'main' into MetaDataSource
ne0rrmatrix Jul 8, 2025
67600dd
Fix `MetadataArtworksource`
ne0rrmatrix Jul 8, 2025
2ed7d98
Merge branch 'main' into MetaDataSource
ne0rrmatrix Jul 30, 2025
07219ca
Merge branch 'main' into MetaDataSource
ne0rrmatrix Aug 3, 2025
296ddf2
merge main into MetaDataSource
ne0rrmatrix Jan 29, 2026
ef1cb25
Refactor MediaElement artwork to use MediaSource type
ne0rrmatrix Jan 29, 2026
a83e7f8
Add file picker support and improve metadata handling
ne0rrmatrix Jan 30, 2026
63ceb1b
Refactor GetSource logic; remove LoadBitmapImageAsync
ne0rrmatrix Jan 30, 2026
9866b61
Merge branch 'main' into MetaDataSource
ne0rrmatrix Jan 30, 2026
df180ff
Add flag for Android foreground service enablement
ne0rrmatrix Jan 30, 2026
a38dfd8
Remove trace log when notification service not running
ne0rrmatrix Jan 30, 2026
b7c83bd
Separate MediaOpened and notification update conditions
ne0rrmatrix Jan 30, 2026
5653940
Improve async stream handling and resource disposal
ne0rrmatrix Jan 30, 2026
3a655dd
Refactor media source handling and metadata updates
ne0rrmatrix Jan 30, 2026
f13d6d6
Update src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.wind…
ne0rrmatrix Jan 30, 2026
48f192c
Update src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.wind…
ne0rrmatrix Jan 30, 2026
0eea2d7
Improve media service checks and asset loading on Android/Win
ne0rrmatrix Jan 30, 2026
1baf9b1
Add CancellationToken support to GetArtworkFromMediasource
ne0rrmatrix Jan 30, 2026
41ad3e3
Apply suggestion from @Copilot
ne0rrmatrix Jan 30, 2026
9970721
Rename MetadataArtworkUrl to MetadataArtworkMediaSource
ne0rrmatrix Jan 30, 2026
aebcfc3
Refactor artwork source handling and add cancellation support
ne0rrmatrix Jan 30, 2026
eed4cd9
Fix merge conflict
ne0rrmatrix Feb 18, 2026
2067ead
Refactor MediaElement artwork handling and cleanup
ne0rrmatrix Feb 18, 2026
8faa0bf
Update MediaElement artwork handling to use MediaSource
ne0rrmatrix Feb 18, 2026
1dcc96d
Refactor media source handling for file/URL distinction
ne0rrmatrix Feb 18, 2026
73ea461
Merge branch 'main' into MetaDataSource
ne0rrmatrix Feb 18, 2026
96bc04c
Improve metadata handling and file access robustness
ne0rrmatrix Feb 18, 2026
cdcbd0c
Add CancellationToken to SetMetadata and minor cleanups
ne0rrmatrix Feb 18, 2026
d2e0dfe
Improve file error handling and async metadata updates
ne0rrmatrix Feb 18, 2026
2e1c4f6
Update src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.maci…
ne0rrmatrix Feb 18, 2026
6061f57
Update src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.maci…
ne0rrmatrix Feb 18, 2026
531a4d1
Update src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.andr…
ne0rrmatrix Feb 18, 2026
7d1224a
Update src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.andr…
ne0rrmatrix Feb 18, 2026
9b00703
Update src/CommunityToolkit.Maui.MediaElement/Primitives/Metadata.mac…
ne0rrmatrix Feb 18, 2026
dd89258
Update src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.andr…
ne0rrmatrix Feb 18, 2026
7cee939
Add check for Android foreground service enable flag
ne0rrmatrix Feb 18, 2026
d56063b
Improve error logging in file picker exception handler
ne0rrmatrix Feb 18, 2026
63a24d8
Fix ToggleCommand logic, update logging, remove file path IO
ne0rrmatrix Feb 18, 2026
f2bfc77
Remove CancellationToken from SetMetadata call
ne0rrmatrix Feb 18, 2026
f5104c2
Update samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/…
ne0rrmatrix Feb 18, 2026
de54a84
Update src/CommunityToolkit.Maui.MediaElement/Primitives/Metadata.mac…
ne0rrmatrix Feb 18, 2026
1d6d224
Refactor metadata artwork handling and logging
ne0rrmatrix Feb 18, 2026
0d6ae86
Add null check before accessing url.AbsoluteString
ne0rrmatrix Feb 18, 2026
08bcbe2
Make HttpClient and artwork methods instance-based
ne0rrmatrix Feb 18, 2026
162679b
Add null/empty check for artwork file path in metadata
ne0rrmatrix Feb 19, 2026
98b1fb1
Merge branch 'main' into MetaDataSource
ne0rrmatrix Feb 27, 2026
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 @@ -31,7 +31,7 @@
x:Name="MediaElement"
ShouldAutoPlay="True"
Source="{x:Static constants:StreamingVideoUrls.BuckBunny}"
MetadataArtworkUrl="https://lh3.googleusercontent.com/pw/AP1GczNRrebWCJvfdIau1EbsyyYiwAfwHS0JXjbioXvHqEwYIIdCzuLodQCZmA57GADIo5iB3yMMx3t_vsefbfoHwSg0jfUjIXaI83xpiih6d-oT7qD_slR0VgNtfAwJhDBU09kS5V2T5ZML-WWZn8IrjD4J-g=w1792-h1024-s-no-gm"
MetadataArtworkSource="https://lh3.googleusercontent.com/pw/AP1GczNRrebWCJvfdIau1EbsyyYiwAfwHS0JXjbioXvHqEwYIIdCzuLodQCZmA57GADIo5iB3yMMx3t_vsefbfoHwSg0jfUjIXaI83xpiih6d-oT7qD_slR0VgNtfAwJhDBU09kS5V2T5ZML-WWZn8IrjD4J-g=w1792-h1024-s-no-gm"
MetadataTitle="Big Buck Bunny"
MetadataArtist="Blender Foundation"
MediaEnded="OnMediaEnded"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public partial class MediaElementPage : BasePage<MediaElementViewModel>
const string loadLocalResource = "Load Local Resource";
const string resetSource = "Reset Source to null";
const string loadMusic = "Load Music";

const string loadFromFile = "Load from File";
const string botImageUrl = "https://lh3.googleusercontent.com/pw/AP1GczNRrebWCJvfdIau1EbsyyYiwAfwHS0JXjbioXvHqEwYIIdCzuLodQCZmA57GADIo5iB3yMMx3t_vsefbfoHwSg0jfUjIXaI83xpiih6d-oT7qD_slR0VgNtfAwJhDBU09kS5V2T5ZML-WWZn8IrjD4J-g=w1792-h1024-s-no-gm";
const string hlsStreamTestUrl = "https://mtoczko.github.io/hls-test-streams/test-gap/playlist.m3u8";
const string hal9000AudioUrl = "https://github.com/prof3ssorSt3v3/media-sample-files/raw/master/hal-9000.mp3";
Expand Down Expand Up @@ -166,7 +166,7 @@ await DisplayAlertAsync("Error Loading URL Source", "No value was found to load
async void ChangeSourceClicked(object? sender, EventArgs? e)
{
var result = await DisplayActionSheetAsync("Choose a source", "Cancel", null,
loadOnlineMp4, loadHls, loadLocalResource, resetSource, loadMusic);
loadOnlineMp4, loadHls, loadLocalResource, resetSource, loadMusic, loadFromFile);

MediaElement.Stop();
MediaElement.Source = null;
Expand All @@ -175,28 +175,28 @@ async void ChangeSourceClicked(object? sender, EventArgs? e)
{
case loadOnlineMp4:
MediaElement.MetadataTitle = "Big Buck Bunny";
MediaElement.MetadataArtworkUrl = botImageUrl;
MediaElement.MetadataArtworkSource = botImageUrl;
MediaElement.MetadataArtist = "Big Buck Bunny Album";
MediaElement.Source =
MediaSource.FromUri(StreamingVideoUrls.BuckBunny);
return;

case loadHls:
MediaElement.MetadataArtist = "HLS Album";
MediaElement.MetadataArtworkUrl = botImageUrl;
MediaElement.MetadataArtworkSource = botImageUrl;
MediaElement.MetadataTitle = "HLS Title";
MediaElement.Source = MediaSource.FromUri(hlsStreamTestUrl);
return;

case resetSource:
MediaElement.MetadataArtworkUrl = string.Empty;
MediaElement.MetadataArtworkSource = null;
MediaElement.MetadataTitle = string.Empty;
MediaElement.MetadataArtist = string.Empty;
MediaElement.Source = null;
return;

case loadLocalResource:
MediaElement.MetadataArtworkUrl = botImageUrl;
MediaElement.MetadataArtworkSource = MediaSource.FromResource("robot.jpg");
MediaElement.MetadataTitle = "Local Resource Title";
MediaElement.MetadataArtist = "Local Resource Album";

Expand All @@ -218,9 +218,32 @@ async void ChangeSourceClicked(object? sender, EventArgs? e)
case loadMusic:
MediaElement.MetadataTitle = "HAL 9000";
MediaElement.MetadataArtist = "HAL 9000 Album";
MediaElement.MetadataArtworkUrl = botImageUrl;
MediaElement.MetadataArtworkSource = botImageUrl;
MediaElement.Source = MediaSource.FromUri(hal9000AudioUrl);
return;
case loadFromFile:
var fileResult = await PickAndShow(new PickOptions
{
FileTypes = FilePickerFileType.Images,
PickerTitle = "Please select an image file"
});
if (fileResult is not null)
{
MediaElement.MetadataArtworkSource = MediaSource.FromFile(fileResult.FullPath);
}
MediaElement.MetadataTitle = "Downloaded file";
MediaElement.MetadataArtist = "From File Album";

fileResult = await PickAndShow(new PickOptions
{
FileTypes = FilePickerFileType.Videos,
PickerTitle = "Please select a video file"
});
if (fileResult is not null)
{
MediaElement.Source = MediaSource.FromFile(fileResult.FullPath);
}
return;
}
}

Expand Down Expand Up @@ -278,7 +301,7 @@ async void DisplayPopup(object? sender, EventArgs? e)
HeightRequest = 400,
AndroidViewType = AndroidViewType.SurfaceView,
Source = source,
MetadataArtworkUrl = botImageUrl,
MetadataArtworkSource = botImageUrl,
ShouldAutoPlay = true,
ShouldShowPlaybackControls = true,
};
Expand All @@ -288,4 +311,20 @@ async void DisplayPopup(object? sender, EventArgs? e)
popupMediaElement.Stop();
popupMediaElement.Source = null;
}

async Task<FileResult?> PickAndShow(PickOptions options)
{
try
{
var result = await FilePicker.Default.PickAsync(options);
return result;
}
catch (Exception ex)
{
// The user canceled or something went wrong
logger.LogError(ex, "Error picking file");
}

return null;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public interface IMediaElement : IView, IAsynchronousMediaElementHandler
/// </summary>
event EventHandler<MediaPositionChangedEventArgs> PositionChanged;

/// <summary>
/// Gets or sets the artwork Image source.
/// </summary>
MediaSource? MetadataArtworkSource { get; set; }

/// <summary>
/// Gets the media aspect ratio.
/// </summary>
Expand Down Expand Up @@ -106,11 +111,6 @@ public interface IMediaElement : IView, IAsynchronousMediaElementHandler
/// </summary>
string MetadataArtist { get; set; }

/// <summary>
/// Gets or sets the artwork Image Url.
/// </summary>
string MetadataArtworkUrl { get; set; }

/// <summary>
/// Occurs when the media has ended playing successfully.
/// </summary>
Expand Down
15 changes: 7 additions & 8 deletions src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ public partial class MediaElement : View, IMediaElement, IDisposable
public static readonly BindableProperty MetadataArtistProperty = BindableProperty.Create(nameof(MetadataArtist), typeof(string), typeof(MediaElement), MediaElementDefaults.MetadataArtist);

/// <summary>
/// Bindable property for the <see cref="MetadataArtworkUrl"/> property.
/// Bindable property for the <see cref="MetadataArtworkSource"/> property.
/// </summary>
public static readonly BindableProperty MetadataArtworkUrlProperty = BindableProperty.Create(nameof(MetadataArtworkUrl), typeof(string), typeof(MediaElement), MediaElementDefaults.MetadataArtworkUrl);
public static readonly BindableProperty MetadataArtworkSourceProperty = BindableProperty.Create(nameof(MetadataArtworkSource), typeof(MediaSource), typeof(MediaElement), MediaElementDefaults.MetadataArtworkSource);

readonly WeakEventManager eventManager = new();
readonly SemaphoreSlim seekToSemaphoreSlim = new(1, 1);
Expand Down Expand Up @@ -333,14 +333,14 @@ public string MetadataArtist
}

/// <summary>
/// Gets or sets the <see cref="MetadataArtworkUrl"/> of the media.
/// Gets or sets the <see cref="MetadataArtworkSource"/> of the media.
/// </summary>
public string MetadataArtworkUrl
[TypeConverter(typeof(MediaSourceConverter))]
public MediaSource? MetadataArtworkSource
{
get => (string)GetValue(MetadataArtworkUrlProperty);
set => SetValue(MetadataArtworkUrlProperty, value);
get => (MediaSource?)GetValue(MetadataArtworkSourceProperty);
set => SetValue(MetadataArtworkSourceProperty, value);
}

/// <summary>
/// Gets or sets the <see cref="Volume"/> of the media.
/// </summary>
Expand Down Expand Up @@ -577,7 +577,6 @@ void ClearTimer()
timer.Stop();
timer = null;
}

void OnSourceChanged(object? sender, EventArgs eventArgs)
{
OnPropertyChanged(SourceProperty.PropertyName);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using CommunityToolkit.Maui.Views;

namespace CommunityToolkit.Maui.Core;

static class MediaElementDefaults
Expand Down Expand Up @@ -26,7 +28,7 @@ static class MediaElementDefaults

public const string MetadataArtist = "";

public const string MetadataArtworkUrl = "";
public static MediaSource? MetadataArtworkSource => null;

public const MediaElementState CurrentState = MediaElementState.None;

Expand Down
141 changes: 120 additions & 21 deletions src/CommunityToolkit.Maui.MediaElement/Primitives/Metadata.macios.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AVFoundation;
using CommunityToolkit.Maui.Views;
using CoreMedia;
using Foundation;
using MediaPlayer;
Expand Down Expand Up @@ -69,42 +70,33 @@ public Metadata(PlatformMediaElement player)
/// </summary>
/// <param name="playerItem"></param>
/// <param name="mediaElement"></param>
public void SetMetadata(AVPlayerItem? playerItem, IMediaElement? mediaElement)
/// <param name="cancellationToken"></param>
public async Task SetMetadata(AVPlayerItem? playerItem, IMediaElement? mediaElement, CancellationToken cancellationToken = default)
{
if (mediaElement is null)
{
Metadata.ClearNowPlaying();
return;
}
ClearNowPlaying();
var artwork = await MetadataArtworkMediaSource(mediaElement.MetadataArtworkSource, cancellationToken).ConfigureAwait(false);

var url = mediaElement.MetadataArtworkUrl;

if (artwork is UIImage image)
{
NowPlayingInfo.Artwork = new(boundsSize: new(320, 240), requestHandler: _ => image);
}
else
{
NowPlayingInfo.Artwork = new(boundsSize: new(0, 0), requestHandler: _ => defaultUIImage);
}
NowPlayingInfo.Title = mediaElement.MetadataTitle;
NowPlayingInfo.Artist = mediaElement.MetadataArtist;
NowPlayingInfo.PlaybackDuration = playerItem?.Duration.Seconds ?? 0;
NowPlayingInfo.IsLiveStream = false;
NowPlayingInfo.PlaybackRate = mediaElement.Speed;
NowPlayingInfo.ElapsedPlaybackTime = playerItem?.CurrentTime.Seconds ?? 0;
NowPlayingInfo.Artwork = new(boundsSize: new(320, 240), requestHandler: _ => GetImage(url));
MPNowPlayingInfoCenter.DefaultCenter.NowPlaying = NowPlayingInfo;
}

static UIImage GetImage(string imageUri)
{
try
{
if (imageUri.StartsWith(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))
{
return UIImage.LoadFromData(NSData.FromUrl(new NSUrl(imageUri))) ?? defaultUIImage;
}
return defaultUIImage;
}
catch
{
return defaultUIImage;
}
}

MPRemoteCommandHandlerStatus SeekCommand(MPRemoteCommandEvent? commandEvent)
{
if (commandEvent is not MPChangePlaybackPositionCommandEvent eventArgs)
Expand Down Expand Up @@ -181,4 +173,111 @@ MPRemoteCommandHandlerStatus ToggleCommand(MPRemoteCommandEvent? commandEvent)

return MPRemoteCommandHandlerStatus.Success;
}

public static async Task<UIImage?> MetadataArtworkMediaSource(MediaSource? artworkUrl, CancellationToken cancellationToken = default)
{
switch(artworkUrl)
{
case UriMediaSource uriMediaSource:
var uri = uriMediaSource.Uri;
return GetBitmapFromUrl(uri?.AbsoluteUri);
case FileMediaSource fileMediaSource:
var uriFile = fileMediaSource.Path;
return await GetBitmapFromFile(uriFile, cancellationToken).ConfigureAwait(false);
case ResourceMediaSource resourceMediaSource:
var path = resourceMediaSource.Path;
return await GetBitmapFromResource(path, cancellationToken).ConfigureAwait(false);
case null:
return null;
}
return null;
Comment on lines +177 to +193
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The switch statement on lines 178-192 has unreachable code. After each case returns a value, the final return null; on line 192 will never be reached because all possible cases (including null) return from within the switch. Consider removing line 192 or restructuring the switch to use a default case that returns null.

Copilot uses AI. Check for mistakes.
}

static async Task<UIImage?> GetBitmapFromFile(string? resource, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(resource))
{
System.Diagnostics.Trace.WriteLine("Metadata artwork file path is null or empty.");
return null;
}
if (!File.Exists(resource))
{
System.Diagnostics.Trace.WriteLine($"Metadata artwork file not found: '{resource}'.");
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using System.Diagnostics.Trace.WriteLine for debug logging violates the codebase's logging convention. According to the project's coding standards, Trace.WriteLine() should be used instead of Debug.WriteLine(), but more importantly, this file should use structured logging via the ILogger instance that exists in other platform managers. The Windows and Android implementations consistently use Logger.LogWarning() and Logger.LogInformation() for similar scenarios. This inconsistency makes debugging harder and prevents proper log filtering and categorization.

Copilot generated this review using guidance from repository custom instructions.
return null;
}
try
{
using var fileStream = File.OpenRead(resource);
using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0;
NSData temp = NSData.FromStream(memoryStream) ?? new NSData();
return UIImage.LoadFromData(temp);
}
catch (IOException ex)
{
System.Diagnostics.Trace.WriteLine($"Error reading metadata artwork file '{resource}': {ex}");
return null;
}
catch (UnauthorizedAccessException ex)
{
System.Diagnostics.Trace.WriteLine($"Access denied reading metadata artwork file '{resource}': {ex}");
return null;
}
}

static UIImage? GetBitmapFromUrl(string? resource)
{
if (string.IsNullOrEmpty(resource))
{
return null;
}

try
{
var nsUrl = new NSUrl(resource);
NSData? data = NSData.FromUrl(nsUrl);
if (data is null)
{
System.Diagnostics.Trace.WriteLine($"Failed to load metadata artwork from URL: '{resource}' - NSData.FromUrl returned null.");
return null;
}

UIImage? image = UIImage.LoadFromData(data);
if (image is null)
{
System.Diagnostics.Trace.WriteLine($"Failed to create UIImage from URL: '{resource}' - UIImage.LoadFromData returned null.");
}

return image;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine($"Error loading metadata artwork from URL '{resource}': {ex}");
return null;
}
}
static async Task<UIImage?> GetBitmapFromResource(string? resource, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(resource))
{
return null;
}
using var inputStream = await FileSystem.OpenAppPackageFileAsync(resource).ConfigureAwait(false);
using var memoryStream = new MemoryStream();
if (inputStream is null)
{
System.Diagnostics.Trace.WriteLine($"Failed to open app package file: '{resource}' - stream is null.");
return null;
}
await inputStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0;
NSData? nsdata = NSData.FromStream(memoryStream);
if (nsdata is null)
{
System.Diagnostics.Trace.TraceInformation($"NSData create from stream: {nsdata} is null.");
Comment on lines +270 to +278
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using System.Diagnostics.Trace.WriteLine for debug logging violates the codebase's logging convention. According to the project's coding standards, Trace.WriteLine() should be used instead of Debug.WriteLine(), but more importantly, this file should use structured logging via the ILogger instance that exists in other platform managers. The Windows and Android implementations consistently use Logger.LogWarning() and Logger.LogInformation() for similar scenarios. This inconsistency makes debugging harder and prevents proper log filtering and categorization.

Copilot generated this review using guidance from repository custom instructions.
return null;
}
return UIImage.LoadFromData(nsdata);
}
}
Loading
Loading