Skip to content

Commit d63101f

Browse files
authored
Merge branch 'main' into FullScreenEvents
2 parents 93a534e + 0d4d476 commit d63101f

File tree

4 files changed

+127
-18
lines changed

4 files changed

+127
-18
lines changed

Directory.Build.props

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
<SkipValidateMauiImplicitPackageReferences>true</SkipValidateMauiImplicitPackageReferences>
2323
<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>
2424

25+
<!-- WindowsAppSDK
26+
We should keep this in sync with .NET MAUI https://github.com/dotnet/maui/blob/8ee00e00f603cd8a420a3467daddaf3935c8c587/Directory.Build.props#L142
27+
This will prevent transitive dependencies from causing conflicts when they're not aligned.
28+
-->
29+
<WindowsSdkPackageVersion>10.0.19041.44</WindowsSdkPackageVersion>
30+
2531
<!-- https://learn.microsoft.com/dotnet/core/deploying/native-aot/?tabs=net8plus%2Cwindows -->
2632
<StripSymbols>false</StripSymbols>
2733
<TrimmerSingleWarn>false</TrimmerSingleWarn>

samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ namespace CommunityToolkit.Maui.Sample.Pages.Views;
1010

1111
public partial class MediaElementPage : BasePage<MediaElementViewModel>
1212
{
13-
readonly ILogger logger;
14-
1513
const string loadOnlineMp4 = "Load Online MP4";
1614
const string loadHls = "Load HTTP Live Stream (HLS)";
1715
const string loadLocalResource = "Load Local Resource";
@@ -22,11 +20,18 @@ public partial class MediaElementPage : BasePage<MediaElementViewModel>
2220
const string hlsStreamTestUrl = "https://mtoczko.github.io/hls-test-streams/test-gap/playlist.m3u8";
2321
const string hal9000AudioUrl = "https://github.com/prof3ssorSt3v3/media-sample-files/raw/master/hal-9000.mp3";
2422

25-
public MediaElementPage(MediaElementViewModel viewModel, ILogger<MediaElementPage> logger) : base(viewModel)
23+
24+
readonly ILogger logger;
25+
readonly IDeviceInfo deviceInfo;
26+
readonly IFileSystem fileSystem;
27+
28+
public MediaElementPage(MediaElementViewModel viewModel, IFileSystem fileSystem, IDeviceInfo deviceInfo, ILogger<MediaElementPage> logger) : base(viewModel)
2629
{
2730
InitializeComponent();
2831

2932
this.logger = logger;
33+
this.deviceInfo = deviceInfo;
34+
this.fileSystem = fileSystem;
3035
MediaElement.PropertyChanged += MediaElement_PropertyChanged;
3136
}
3237

@@ -165,6 +170,9 @@ async void ChangeSourceClicked(Object sender, EventArgs e)
165170
var result = await DisplayActionSheet("Choose a source", "Cancel", null,
166171
loadOnlineMp4, loadHls, loadLocalResource, resetSource, loadMusic);
167172

173+
MediaElement.Stop();
174+
MediaElement.Source = null;
175+
168176
switch (result)
169177
{
170178
case loadOnlineMp4:
@@ -248,15 +256,36 @@ async void ChangeAspectClicked(object? sender, EventArgs e)
248256
async void DisplayPopup(object sender, EventArgs e)
249257
{
250258
MediaElement.Pause();
259+
260+
MediaSource source;
261+
262+
if (deviceInfo.Platform == DevicePlatform.Android)
263+
{
264+
source = MediaSource.FromResource("AndroidVideo.mp4");
265+
}
266+
else if (deviceInfo.Platform == DevicePlatform.MacCatalyst
267+
|| deviceInfo.Platform == DevicePlatform.iOS
268+
|| deviceInfo.Platform == DevicePlatform.macOS)
269+
{
270+
source = MediaSource.FromResource("AppleVideo.mp4");
271+
}
272+
else
273+
{
274+
source = MediaSource.FromResource("WindowsVideo.mp4");
275+
}
276+
251277
var popupMediaElement = new MediaElement
252278
{
253279
AndroidViewType = AndroidViewType.SurfaceView,
254-
Source = MediaSource.FromResource("AppleVideo.mp4"),
280+
Source = source,
281+
MetadataArtworkUrl = "dotnet_bot.png",
255282
ShouldAutoPlay = true,
256283
ShouldShowPlaybackControls = true,
257284
};
258285

259286
await this.ShowPopupAsync(popupMediaElement);
287+
260288
popupMediaElement.Stop();
289+
popupMediaElement.Source = null;
261290
}
262291
}
58.4 KB
Loading

src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Diagnostics.CodeAnalysis;
1+
using System.Diagnostics;
2+
using System.Diagnostics.CodeAnalysis;
23
using Android.Content;
34
using Android.Views;
45
using Android.Widget;
@@ -536,27 +537,97 @@ protected override void Dispose(bool disposing)
536537
}
537538
}
538539

539-
static async Task<byte[]> GetBytesFromMetadataArtworkUrl(string? url, CancellationToken cancellationToken = default)
540+
static async Task<byte[]> GetBytesFromMetadataArtworkUrl(string url, CancellationToken cancellationToken = default)
540541
{
541-
byte[] artworkData = [];
542+
if (string.IsNullOrWhiteSpace(url))
543+
{
544+
return [];
545+
}
546+
547+
Stream? stream = null;
548+
Uri.TryCreate(url, UriKind.Absolute, out var uri);
549+
542550
try
543551
{
544-
var response = await client.GetAsync(url, cancellationToken).ConfigureAwait(false);
545-
var stream = response.IsSuccessStatusCode ? await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false) : null;
552+
byte[] artworkData = [];
553+
long? contentLength = null;
554+
555+
// HTTP or HTTPS URL
556+
if (uri is not null &&
557+
(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
558+
{
559+
var request = new HttpRequestMessage(HttpMethod.Head, url);
560+
var contentLengthResponse = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
561+
contentLength = contentLengthResponse.Content.Headers.ContentLength ?? 0;
546562

547-
if (stream is null)
563+
var response = await client.GetAsync(url, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false);
564+
stream = response.IsSuccessStatusCode ? await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false) : null;
565+
}
566+
// Absolute File Path
567+
else if (uri is not null && uri.Scheme == Uri.UriSchemeFile)
548568
{
549-
return artworkData;
569+
var normalizedFilePath = NormalizeFilePath(url);
570+
571+
stream = File.Open(normalizedFilePath, FileMode.Create);
572+
contentLength = await GetByteCountFromStream(stream, cancellationToken);
550573
}
574+
// Relative File Path
575+
else if (Uri.TryCreate(url, UriKind.Relative, out _))
576+
{
577+
var normalizedFilePath = NormalizeFilePath(url);
551578

552-
using var memoryStream = new MemoryStream();
553-
await stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
554-
var bytes = memoryStream.ToArray();
555-
return bytes;
579+
stream = Platform.AppContext.Assets?.Open(normalizedFilePath) ?? throw new InvalidOperationException("Assets cannot be null");
580+
contentLength = await GetByteCountFromStream(stream, cancellationToken);
581+
}
582+
583+
if (stream is not null)
584+
{
585+
if (!contentLength.HasValue)
586+
{
587+
throw new InvalidOperationException($"{nameof(contentLength)} must be set when {nameof(stream)} is not null");
588+
}
589+
590+
artworkData = new byte[contentLength.Value];
591+
using var memoryStream = new MemoryStream(artworkData);
592+
await stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
593+
}
594+
595+
return artworkData;
556596
}
557-
catch
597+
catch (Exception e)
558598
{
559-
return artworkData;
599+
Trace.WriteLine($"Unable to retrieve {nameof(MediaElement.MetadataArtworkUrl)} for {url}.{e}\n");
600+
return [];
601+
}
602+
finally
603+
{
604+
if (stream is not null)
605+
{
606+
stream.Close();
607+
await stream.DisposeAsync();
608+
}
609+
}
610+
611+
static string NormalizeFilePath(string filePath) => filePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);
612+
613+
static async ValueTask<long> GetByteCountFromStream(Stream stream, CancellationToken token)
614+
{
615+
if (stream.CanSeek)
616+
{
617+
return stream.Length;
618+
}
619+
620+
long countedStreamBytes = 0;
621+
622+
var buffer = new byte[8192];
623+
int bytesRead;
624+
625+
while ((bytesRead = await stream.ReadAsync(buffer, token)) > 0)
626+
{
627+
countedStreamBytes += bytesRead;
628+
}
629+
630+
return countedStreamBytes;
560631
}
561632
}
562633

@@ -636,7 +707,10 @@ void StopService(in BoundServiceConnection boundServiceConnection)
636707
mediaMetaData.SetArtist(MediaElement.MetadataArtist);
637708
mediaMetaData.SetTitle(MediaElement.MetadataTitle);
638709
var data = await GetBytesFromMetadataArtworkUrl(MediaElement.MetadataArtworkUrl, cancellationToken).ConfigureAwait(true);
639-
mediaMetaData.SetArtworkData(data, (Java.Lang.Integer)MediaMetadata.PictureTypeFrontCover);
710+
if (data is not null && data.Length > 0)
711+
{
712+
mediaMetaData.SetArtworkData(data, (Java.Lang.Integer)MediaMetadata.PictureTypeFrontCover);
713+
}
640714

641715
mediaItem = new MediaItem.Builder();
642716
mediaItem.SetUri(url);

0 commit comments

Comments
 (0)