diff --git a/samples/CommunityToolkit.Maui.Sample/Constants/StreamingVideoUrls.cs b/samples/CommunityToolkit.Maui.Sample/Constants/StreamingVideoUrls.cs new file mode 100644 index 0000000000..f0e1847253 --- /dev/null +++ b/samples/CommunityToolkit.Maui.Sample/Constants/StreamingVideoUrls.cs @@ -0,0 +1,8 @@ +namespace CommunityToolkit.Maui.Sample.Constants; + +static class StreamingVideoUrls +{ + public const string BuckBunny = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; + public const string ElephantsDream = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"; + public const string Sintel = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"; +} \ No newline at end of file diff --git a/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs b/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs index c7268cc8be..99c03b8463 100644 --- a/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs +++ b/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using CommunityToolkit.Maui.ApplicationModel; +using CommunityToolkit.Maui.Core; using CommunityToolkit.Maui.Markup; using CommunityToolkit.Maui.Media; using CommunityToolkit.Maui.Sample.Models; @@ -26,6 +27,7 @@ using CommunityToolkit.Maui.Sample.ViewModels.Views; using CommunityToolkit.Maui.Sample.Views.Popups; using CommunityToolkit.Maui.Storage; +using CommunityToolkit.Maui.Views; using Microsoft.Extensions.Http.Resilience; using Microsoft.Extensions.Logging; using Microsoft.Maui.LifecycleEvents; @@ -66,9 +68,11 @@ public static MauiApp CreateMauiApp() #endif .UseMauiCommunityToolkitMarkup() .UseMauiCommunityToolkitCamera() - .UseMauiCommunityToolkitMediaElement() - - .ConfigureMauiHandlers(handlers => + .UseMauiCommunityToolkitMediaElement(static options => + { + options.SetDefaultAndroidViewType(AndroidViewType.TextureView); + }) + .ConfigureMauiHandlers(static handlers => { #if IOS || MACCATALYST handlers.AddHandler(); diff --git a/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementCarouselViewPage.xaml b/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementCarouselViewPage.xaml index 2ba3802d16..552ced6003 100644 --- a/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementCarouselViewPage.xaml +++ b/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementCarouselViewPage.xaml @@ -37,6 +37,7 @@ { - const string buckBunnyMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; #if WINDOWS || MACCATALYST - const string elephantsDreamMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"; readonly Window secondWindow; #endif @@ -22,14 +22,16 @@ public MediaElementMultipleWindowsPage(MediaElementMultipleWindowsViewModel view { Content = new MediaElement { - Source = elephantsDreamMp4Url, + AndroidViewType= AndroidViewType.SurfaceView, + Source = StreamingVideoUrls.ElephantsDream, ShouldAutoPlay = true } }); Content = new MediaElement { - Source = buckBunnyMp4Url, + AndroidViewType= AndroidViewType.SurfaceView, + Source = StreamingVideoUrls.BuckBunny, ShouldAutoPlay = true }; #else diff --git a/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml b/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml index 609ce11a3e..f017af2ede 100644 --- a/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml +++ b/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages" xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" + xmlns:constants="clr-namespace:CommunityToolkit.Maui.Sample.Constants" xmlns:viewModels="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views" xmlns:converters="clr-namespace:CommunityToolkit.Maui.Sample.Converters" x:TypeArguments="viewModels:MediaElementViewModel" @@ -29,7 +30,7 @@ const string resetSource = "Reset Source to null"; const string loadMusic = "Load Music"; - const string buckBunnyMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; 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"; @@ -170,7 +170,7 @@ async void ChangeSourceClicked(Object sender, EventArgs e) MediaElement.MetadataArtworkUrl = botImageUrl; MediaElement.MetadataArtist = "Big Buck Bunny Album"; MediaElement.Source = - MediaSource.FromUri(buckBunnyMp4Url); + MediaSource.FromUri(StreamingVideoUrls.BuckBunny); return; case loadHls: @@ -248,6 +248,7 @@ void DisplayPopup(object sender, EventArgs e) MediaElement.Pause(); var popupMediaElement = new MediaElement { + AndroidViewType = AndroidViewType.SurfaceView, Source = MediaSource.FromResource("AppleVideo.mp4"), HeightRequest = 600, WidthRequest = 600, diff --git a/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml b/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml index 6feec1bfdb..8f95e12183 100644 --- a/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml +++ b/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml @@ -1,6 +1,6 @@  - diff --git a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCarouselViewViewModel.cs b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCarouselViewViewModel.cs index a9715ebffd..9211de8a16 100644 --- a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCarouselViewViewModel.cs +++ b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCarouselViewViewModel.cs @@ -1,17 +1,15 @@ using System.Collections.ObjectModel; +using CommunityToolkit.Maui.Sample.Constants; + namespace CommunityToolkit.Maui.Sample.ViewModels.Views; public partial class MediaElementCarouselViewViewModel : BaseViewModel { - const string buckBunnyMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; - const string elephantsDreamMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"; - const string sintelMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"; - public ObservableCollection ItemSource { get; } = [ - new(new Uri(buckBunnyMp4Url), "Buck Bunny", 720, 1280), - new(new Uri(elephantsDreamMp4Url), "Elephants Dream", 720, 1280), - new(new Uri(sintelMp4Url), "Sintel", 546, 1280) + new(new Uri(StreamingVideoUrls.BuckBunny), "Buck Bunny", 720, 1280), + new(new Uri(StreamingVideoUrls.ElephantsDream), "Elephants Dream", 720, 1280), + new(new Uri(StreamingVideoUrls.Sintel), "Sintel", 546, 1280) ]; } diff --git a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCollectionViewViewModel.cs b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCollectionViewViewModel.cs index 8df3f64fb4..6a8288a560 100644 --- a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCollectionViewViewModel.cs +++ b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementCollectionViewViewModel.cs @@ -1,16 +1,14 @@ using System.Collections.ObjectModel; +using CommunityToolkit.Maui.Sample.Constants; + namespace CommunityToolkit.Maui.Sample.ViewModels.Views; public partial class MediaElementCollectionViewViewModel : BaseViewModel { - const string buckBunnyMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; - const string elephantsDreamMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"; - const string sintelMp4Url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"; - public ObservableCollection ItemSource { get; } = [ - new(new Uri(buckBunnyMp4Url), "Buck Bunny", 720, 1280), - new(new Uri(elephantsDreamMp4Url), "Elephants Dream", 720, 1280), - new(new Uri(sintelMp4Url), "Sintel", 546, 1280) + new(new Uri(StreamingVideoUrls.BuckBunny), "Buck Bunny", 720, 1280), + new(new Uri(StreamingVideoUrls.ElephantsDream), "Elephants Dream", 720, 1280), + new(new Uri(StreamingVideoUrls.Sintel), "Sintel", 546, 1280) ]; } \ No newline at end of file diff --git a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementViewModel.cs b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementViewModel.cs index d119bfbb6b..60543f7927 100644 --- a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementViewModel.cs +++ b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/MediaElement/MediaElementViewModel.cs @@ -2,8 +2,4 @@ public partial class MediaElementViewModel : BaseViewModel { - public MediaElementViewModel() - { - - } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs b/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs index f2ee5d594b..3f7eb88638 100644 --- a/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs @@ -1,4 +1,5 @@ using System.Runtime.Versioning; +using CommunityToolkit.Maui.Core; using CommunityToolkit.Maui.Core.Handlers; using CommunityToolkit.Maui.Views; @@ -18,9 +19,14 @@ public static class AppBuilderExtensions /// Initializes the .NET MAUI Community Toolkit MediaElement Library /// /// generated by . + /// /// initialized for . - public static MauiAppBuilder UseMauiCommunityToolkitMediaElement(this MauiAppBuilder builder) + public static MauiAppBuilder UseMauiCommunityToolkitMediaElement(this MauiAppBuilder builder, Action? options = null) { + // Update the default MediaElementOptions for MediaElement if Action is not null + options?.Invoke(new MediaElementOptions(builder)); + + // Perform Handler configuration builder.ConfigureMauiHandlers(h => { h.AddHandler(); @@ -29,6 +35,7 @@ public static MauiAppBuilder UseMauiCommunityToolkitMediaElement(this MauiAppBui #if ANDROID builder.Services.AddSingleton(); #endif + return builder; } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/AssemblyInfo.shared.cs b/src/CommunityToolkit.Maui.MediaElement/AssemblyInfo.shared.cs index 58845013be..5e4f0ffdb0 100644 --- a/src/CommunityToolkit.Maui.MediaElement/AssemblyInfo.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/AssemblyInfo.shared.cs @@ -1,6 +1,5 @@ [assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Converters))] [assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Core))] -[assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Core.Primitives))] [assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Core.Handlers))] [assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Core.Views))] [assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Views))] diff --git a/src/CommunityToolkit.Maui.MediaElement/CommunityToolkit.Maui.MediaElement.csproj b/src/CommunityToolkit.Maui.MediaElement/CommunityToolkit.Maui.MediaElement.csproj index 2fc45346ac..2eebfc1f8c 100644 --- a/src/CommunityToolkit.Maui.MediaElement/CommunityToolkit.Maui.MediaElement.csproj +++ b/src/CommunityToolkit.Maui.MediaElement/CommunityToolkit.Maui.MediaElement.csproj @@ -63,11 +63,15 @@ - + + + + + - + @@ -76,4 +80,5 @@ ResourceDictionary.windows.xaml + \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/Converters/FileMediaSourceConverter.cs b/src/CommunityToolkit.Maui.MediaElement/Converters/FileMediaSourceConverter.shared.cs similarity index 100% rename from src/CommunityToolkit.Maui.MediaElement/Converters/FileMediaSourceConverter.cs rename to src/CommunityToolkit.Maui.MediaElement/Converters/FileMediaSourceConverter.shared.cs diff --git a/src/CommunityToolkit.Maui.MediaElement/Converters/MediaSourceConverter.cs b/src/CommunityToolkit.Maui.MediaElement/Converters/MediaSourceConverter.shared.cs similarity index 100% rename from src/CommunityToolkit.Maui.MediaElement/Converters/MediaSourceConverter.cs rename to src/CommunityToolkit.Maui.MediaElement/Converters/MediaSourceConverter.shared.cs diff --git a/src/CommunityToolkit.Maui.MediaElement/Extensions/PageExtensions.cs b/src/CommunityToolkit.Maui.MediaElement/Extensions/PageExtensions.shared.cs similarity index 100% rename from src/CommunityToolkit.Maui.MediaElement/Extensions/PageExtensions.cs rename to src/CommunityToolkit.Maui.MediaElement/Extensions/PageExtensions.shared.cs diff --git a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs index c204ef847b..bd1e00e6e7 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs @@ -14,17 +14,17 @@ public partial class MediaElementHandler : ViewHandlerThe associated instance. public static void ShouldLoopPlayback(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateShouldLoopPlayback(); + handler.MediaManager?.UpdateShouldLoopPlayback(); } protected override MauiMediaElement CreatePlatformView() { - mediaManager ??= new(MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null"), + MediaManager ??= new(MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null"), VirtualView, Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null")); - var (_, playerView) = mediaManager.CreatePlatformView(); + var (_, playerView) = MediaManager.CreatePlatformView(VirtualView.AndroidViewType); return new(Context, playerView); } diff --git a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.macios.cs b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.macios.cs index f2624479cf..56ed0612d4 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.macios.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.macios.cs @@ -18,11 +18,12 @@ protected override MauiMediaElement CreatePlatformView() throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null"); } - mediaManager ??= new(MauiContext, + MediaManager ??= new(MauiContext, VirtualView, Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null")); - (_, playerViewController) = mediaManager.CreatePlatformView(); + + (_, playerViewController) = MediaManager.CreatePlatformView(); return new(playerViewController, VirtualView); } diff --git a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.shared.cs b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.shared.cs index 44b91acc4b..62a9d8e8a4 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.shared.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Maui.Core.Primitives; -using CommunityToolkit.Maui.Core.Views; +using CommunityToolkit.Maui.Core.Views; using CommunityToolkit.Maui.Views; namespace CommunityToolkit.Maui.Core.Handlers; @@ -9,14 +8,6 @@ namespace CommunityToolkit.Maui.Core.Handlers; /// public partial class MediaElementHandler { -#if ANDROID || IOS || MACCATALYST || WINDOWS || TIZEN - /// - /// The that is managing the instance. - /// - - protected MediaManager? mediaManager; -#endif - /// /// The default property mapper for this handler. /// @@ -66,6 +57,12 @@ public MediaElementHandler(IPropertyMapper? mapper, CommandMapper? commandMapper } #if ANDROID || IOS || MACCATALYST || WINDOWS || TIZEN + /// + /// The that is managing the instance. + /// + + protected MediaManager? MediaManager { get; set; } + /// /// Maps the property between the abstract /// and platform counterpart. @@ -74,7 +71,7 @@ public MediaElementHandler(IPropertyMapper? mapper, CommandMapper? commandMapper /// The associated instance. public static void MapAspect(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateAspect(); + handler.MediaManager?.UpdateAspect(); } /// @@ -85,7 +82,7 @@ public static void MapAspect(MediaElementHandler handler, MediaElement mediaElem /// The associated instance. public static void MapShouldShowPlaybackControls(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateShouldShowPlaybackControls(); + handler.MediaManager?.UpdateShouldShowPlaybackControls(); } /// @@ -96,7 +93,7 @@ public static void MapShouldShowPlaybackControls(MediaElementHandler handler, Me /// The associated instance. public static void MapSource(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateSource(); + handler.MediaManager?.UpdateSource(); } /// @@ -107,7 +104,7 @@ public static void MapSource(MediaElementHandler handler, MediaElement mediaElem /// The associated instance. public static void MapSpeed(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateSpeed(); + handler.MediaManager?.UpdateSpeed(); } /// @@ -119,7 +116,7 @@ public static void MapSpeed(MediaElementHandler handler, MediaElement mediaEleme /// is not used. public static void MapStatusUpdated(MediaElementHandler handler, MediaElement mediaElement, object? args) { - handler.mediaManager?.UpdateStatus(); + handler.MediaManager?.UpdateStatus(); } /// @@ -130,7 +127,7 @@ public static void MapStatusUpdated(MediaElementHandler handler, MediaElement me /// The associated instance. public static void MapVolume(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateVolume(); + handler.MediaManager?.UpdateVolume(); } /// @@ -141,7 +138,7 @@ public static void MapVolume(MediaElementHandler handler, MediaElement mediaElem /// The associated instance. public static void MapShouldKeepScreenOn(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateShouldKeepScreenOn(); + handler.MediaManager?.UpdateShouldKeepScreenOn(); } /// @@ -152,7 +149,7 @@ public static void MapShouldKeepScreenOn(MediaElementHandler handler, MediaEleme /// The associated instance. public static void MapShouldMute(MediaElementHandler handler, MediaElement mediaElement) { - handler.mediaManager?.UpdateShouldMute(); + handler.MediaManager?.UpdateShouldMute(); } /// @@ -164,7 +161,7 @@ public static void MapShouldMute(MediaElementHandler handler, MediaElement media /// is not used. public static void MapPlayRequested(MediaElementHandler handler, MediaElement mediaElement, object? args) { - handler.mediaManager?.Play(); + handler.MediaManager?.Play(); } /// @@ -176,7 +173,7 @@ public static void MapPlayRequested(MediaElementHandler handler, MediaElement me /// is not used. public static void MapPauseRequested(MediaElementHandler handler, MediaElement mediaElement, object? args) { - handler.mediaManager?.Pause(); + handler.MediaManager?.Pause(); } /// @@ -191,7 +188,7 @@ public static async void MapSeekRequested(MediaElementHandler handler, MediaElem ArgumentNullException.ThrowIfNull(args); var positionArgs = (MediaSeekRequestedEventArgs)args; - await (handler.mediaManager?.Seek(positionArgs.RequestedPosition, CancellationToken.None) ?? Task.CompletedTask); + await (handler.MediaManager?.Seek(positionArgs.RequestedPosition, CancellationToken.None) ?? Task.CompletedTask); ((IMediaElement)mediaElement).SeekCompletedTCS.TrySetResult(); } @@ -205,7 +202,7 @@ public static async void MapSeekRequested(MediaElementHandler handler, MediaElem /// is not used. public static void MapStopRequested(MediaElementHandler handler, MediaElement mediaElement, object? args) { - handler.mediaManager?.Stop(); + handler.MediaManager?.Stop(); } /// @@ -225,8 +222,8 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - mediaManager?.Dispose(); - mediaManager = null; + MediaManager?.Dispose(); + MediaManager = null; PlatformDispose(); } } diff --git a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.tizen.cs b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.tizen.cs index 33ae9086b1..8bf0f99061 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.tizen.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.tizen.cs @@ -10,11 +10,11 @@ public partial class MediaElementHandler : ViewHandlerThrown if is . protected override MauiMediaElement CreatePlatformView() { - mediaManager ??= new(MauiContext ?? throw new NullReferenceException(), + MediaManager ??= new(MauiContext ?? throw new NullReferenceException(), VirtualView, Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null")); - var playerView = mediaManager.CreatePlatformView(); + var playerView = MediaManager.CreatePlatformView(); return new (playerView); } @@ -28,6 +28,6 @@ protected override void DisconnectHandler(MauiMediaElement platformView) /// public static void ShouldLoopPlayback(MediaElementHandler handler, MediaElement MediaElement) { - handler.mediaManager?.UpdateShouldLoopPlayback(); + handler.MediaManager?.UpdateShouldLoopPlayback(); } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.windows.cs b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.windows.cs index 63a25e5f1a..bbc29f0064 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.windows.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.windows.cs @@ -14,17 +14,17 @@ public partial class MediaElementHandler : ViewHandlerThe associated instance. public static void ShouldLoopPlayback(MediaElementHandler handler, MediaElement MediaElement) { - handler.mediaManager?.UpdateShouldLoopPlayback(); + handler?.MediaManager?.UpdateShouldLoopPlayback(); } /// protected override MauiMediaElement CreatePlatformView() { - mediaManager ??= new(MauiContext ?? throw new NullReferenceException(), + MediaManager ??= new(MauiContext ?? throw new NullReferenceException(), VirtualView, Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null")); - var mediaPlatform = mediaManager.CreatePlatformView(); + var mediaPlatform = MediaManager.CreatePlatformView(); return new(mediaPlatform); } diff --git a/src/CommunityToolkit.Maui.MediaElement/Interfaces/IAsynchronousMediaElementHandler.cs b/src/CommunityToolkit.Maui.MediaElement/Interfaces/IAsynchronousMediaElementHandler.shared.cs similarity index 100% rename from src/CommunityToolkit.Maui.MediaElement/Interfaces/IAsynchronousMediaElementHandler.cs rename to src/CommunityToolkit.Maui.MediaElement/Interfaces/IAsynchronousMediaElementHandler.shared.cs diff --git a/src/CommunityToolkit.Maui.MediaElement/Interfaces/IMediaElement.cs b/src/CommunityToolkit.Maui.MediaElement/Interfaces/IMediaElement.shared.cs similarity index 98% rename from src/CommunityToolkit.Maui.MediaElement/Interfaces/IMediaElement.cs rename to src/CommunityToolkit.Maui.MediaElement/Interfaces/IMediaElement.shared.cs index 05e3ce7f51..a8f7ea3ebb 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Interfaces/IMediaElement.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Interfaces/IMediaElement.shared.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Maui.Core.Primitives; -using CommunityToolkit.Maui.Views; +using CommunityToolkit.Maui.Views; namespace CommunityToolkit.Maui.Core; diff --git a/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs b/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs index b23d291711..6d663135f2 100644 --- a/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs @@ -1,7 +1,6 @@ using System.ComponentModel; using CommunityToolkit.Maui.Converters; using CommunityToolkit.Maui.Core; -using CommunityToolkit.Maui.Core.Primitives; namespace CommunityToolkit.Maui.Views; @@ -127,6 +126,11 @@ public partial class MediaElement : View, IMediaElement, IDisposable IDispatcherTimer? timer; TaskCompletionSource seekCompletedTaskCompletionSource = new(); + /// + /// Finalizer + /// + ~MediaElement() => Dispose(false); + /// public event EventHandler MediaEnded { @@ -205,11 +209,6 @@ internal event EventHandler StopRequested remove => eventManager.RemoveEventHandler(value); } - /// - /// Finalizer - /// - ~MediaElement() => Dispose(false); - /// /// The current position of the playing media. This is a bindable property. /// @@ -221,6 +220,11 @@ internal event EventHandler StopRequested /// Might not be available for some types, like live streams. public TimeSpan Duration => (TimeSpan)GetValue(DurationProperty); + /// + /// Read the MediaElementOptions set in on construction, cannot be changed after construction + /// + public AndroidViewType AndroidViewType { get; init; } = MediaElementOptions.DefaultAndroidViewType; + /// /// Gets or sets whether the media should start playing as soon as it's loaded. /// Default is . This is a bindable property. @@ -311,7 +315,6 @@ public double Volume SetValue(VolumeProperty, value); break; } - } } diff --git a/src/CommunityToolkit.Maui.MediaElement/MediaElementOptions.shared.cs b/src/CommunityToolkit.Maui.MediaElement/MediaElementOptions.shared.cs new file mode 100644 index 0000000000..35c839d2a4 --- /dev/null +++ b/src/CommunityToolkit.Maui.MediaElement/MediaElementOptions.shared.cs @@ -0,0 +1,24 @@ +namespace CommunityToolkit.Maui.Core; + +/// +/// Construction options for MediaElement, for example, to create an Android SurfaceView or TextureView +/// +public class MediaElementOptions() +{ + readonly MauiAppBuilder? builder; + + internal MediaElementOptions(in MauiAppBuilder builder) : this() + { + this.builder = builder; + } + + /// + /// Set Android View type for MediaElement as SurfaceView or TextureView on construction + /// + internal static AndroidViewType DefaultAndroidViewType { get; private set; } = AndroidViewType.SurfaceView; + + /// + /// Set Android View type for MediaElement as SurfaceView or TextureView on construction + /// + public void SetDefaultAndroidViewType(AndroidViewType androidViewType) => DefaultAndroidViewType = androidViewType; +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/Platforms/Android/Resources/layout/textureview.xml b/src/CommunityToolkit.Maui.MediaElement/Platforms/Android/Resources/layout/textureview.xml new file mode 100644 index 0000000000..58c65010ec --- /dev/null +++ b/src/CommunityToolkit.Maui.MediaElement/Platforms/Android/Resources/layout/textureview.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/AndroidViewType.shared.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/AndroidViewType.shared.cs new file mode 100644 index 0000000000..f6b58f9468 --- /dev/null +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/AndroidViewType.shared.cs @@ -0,0 +1,19 @@ +using CommunityToolkit.Maui.Views; + +namespace CommunityToolkit.Maui.Core; + +/// +/// Enum for view type on Android +/// +public enum AndroidViewType +{ + /// + /// Create MediaElement on Android using SurfaceView + /// + SurfaceView, + + /// + /// Create MediaElement on Android using TextureView + /// + TextureView +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementState.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementState.shared.cs similarity index 92% rename from src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementState.cs rename to src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementState.shared.cs index 682a868879..07c470e34a 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementState.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementState.shared.cs @@ -1,4 +1,4 @@ -namespace CommunityToolkit.Maui.Core.Primitives; +namespace CommunityToolkit.Maui.Core; /// /// Represents the different states media can go through. diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaFailedEventArgs.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaFailedEventArgs.shared.cs similarity index 91% rename from src/CommunityToolkit.Maui.MediaElement/Primitives/MediaFailedEventArgs.cs rename to src/CommunityToolkit.Maui.MediaElement/Primitives/MediaFailedEventArgs.shared.cs index afd6c9623d..fecd2dbf61 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaFailedEventArgs.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaFailedEventArgs.shared.cs @@ -1,4 +1,4 @@ -namespace CommunityToolkit.Maui.Core.Primitives; +namespace CommunityToolkit.Maui.Core; /// /// Represents event data for when media has failed loading or playing. diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaPositionChangedEventArgs.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaPositionChangedEventArgs.shared.cs similarity index 90% rename from src/CommunityToolkit.Maui.MediaElement/Primitives/MediaPositionChangedEventArgs.cs rename to src/CommunityToolkit.Maui.MediaElement/Primitives/MediaPositionChangedEventArgs.shared.cs index 272edd2a3d..6d8bea3ff1 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaPositionChangedEventArgs.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaPositionChangedEventArgs.shared.cs @@ -1,4 +1,4 @@ -namespace CommunityToolkit.Maui.Core.Primitives; +namespace CommunityToolkit.Maui.Core; /// /// Represents event data for when media position has changed. diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaStateChangedEventArgs.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaStateChangedEventArgs.shared.cs similarity index 94% rename from src/CommunityToolkit.Maui.MediaElement/Primitives/MediaStateChangedEventArgs.cs rename to src/CommunityToolkit.Maui.MediaElement/Primitives/MediaStateChangedEventArgs.shared.cs index f83d709f06..a12b70bcdd 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaStateChangedEventArgs.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaStateChangedEventArgs.shared.cs @@ -1,4 +1,4 @@ -namespace CommunityToolkit.Maui.Core.Primitives; +namespace CommunityToolkit.Maui.Core; /// /// Represents event data for when media state has changed. diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/Metadata.macios.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/Metadata.macios.cs index d0dc3ec0d6..3ce3c627a1 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/Metadata.macios.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/Metadata.macios.cs @@ -4,7 +4,7 @@ using MediaPlayer; using UIKit; -namespace CommunityToolkit.Maui.Core.Primitives; +namespace CommunityToolkit.Maui.Core; sealed class Metadata { diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/PlayerViewChangedEventArgs.android.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/PlayerViewChangedEventArgs.android.cs index ab653e2ac2..90379eee35 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/PlayerViewChangedEventArgs.android.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/PlayerViewChangedEventArgs.android.cs @@ -1,6 +1,6 @@ using AndroidX.Media3.UI; -namespace CommunityToolkit.Maui.Primitives; +namespace CommunityToolkit.Maui.Core; public sealed class PlayerViewChangedEventArgs(PlayerView playerView) : EventArgs { diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/SeekRequestedEventArgs.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/SeekRequestedEventArgs.shared.cs similarity index 90% rename from src/CommunityToolkit.Maui.MediaElement/Primitives/SeekRequestedEventArgs.cs rename to src/CommunityToolkit.Maui.MediaElement/Primitives/SeekRequestedEventArgs.shared.cs index 7677b9aa29..6d074ec87c 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/SeekRequestedEventArgs.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/SeekRequestedEventArgs.shared.cs @@ -1,4 +1,4 @@ -namespace CommunityToolkit.Maui.Core.Primitives; +namespace CommunityToolkit.Maui.Core; /// /// Represents event data for when a seek operation is requested on media. diff --git a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs index 24bb970536..180f826109 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs @@ -8,7 +8,6 @@ using AndroidX.Media3.ExoPlayer; using AndroidX.Media3.Session; using AndroidX.Media3.UI; -using CommunityToolkit.Maui.Core.Primitives; using CommunityToolkit.Maui.Media.Services; using CommunityToolkit.Maui.Services; using CommunityToolkit.Maui.Views; @@ -130,21 +129,51 @@ or PlaybackState.StateSkippingToQueueItem /// The platform native counterpart of . /// Thrown when is or when the platform view could not be created. [MemberNotNull(nameof(Player), nameof(PlayerView), nameof(session))] - public (PlatformMediaElement platformView, PlayerView PlayerView) CreatePlatformView() + public (PlatformMediaElement platformView, PlayerView PlayerView) CreatePlatformView(AndroidViewType androidViewType) { Player = new ExoPlayerBuilder(MauiContext.Context).Build() ?? throw new InvalidOperationException("Player cannot be null"); Player.AddListener(this); - PlayerView = new PlayerView(MauiContext.Context) + + if (androidViewType is AndroidViewType.SurfaceView) { - Player = Player, - UseController = false, - ControllerAutoShow = false, - LayoutParameters = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent) - }; - string randomId = Convert.ToBase64String(Guid.NewGuid().ToByteArray())[..8]; - var mediaSessionWRandomId = new MediaSession.Builder(Platform.AppContext, Player); - mediaSessionWRandomId.SetId(randomId); - session ??= mediaSessionWRandomId.Build() ?? throw new InvalidOperationException("Session cannot be null"); + PlayerView = new PlayerView(MauiContext.Context) + { + Player = Player, + UseController = false, + ControllerAutoShow = false, + LayoutParameters = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent) + }; + } + else if (androidViewType is AndroidViewType.TextureView) + { + if (MauiContext.Context?.Resources is null) + { + throw new InvalidOperationException("Unable to retrieve Android Resources"); + } + + var resources = MauiContext.Context.Resources; + var xmlResource = resources.GetXml(Microsoft.Maui.Resource.Layout.textureview); + xmlResource.Read(); + + var attributes = Android.Util.Xml.AsAttributeSet(xmlResource)!; + + PlayerView = new PlayerView(MauiContext.Context, attributes) + { + Player = Player, + UseController = false, + ControllerAutoShow = false, + LayoutParameters = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent) + }; + } + else + { + throw new NotSupportedException($"{androidViewType} is not yet supported"); + } + + var mediaSession = new MediaSession.Builder(Platform.AppContext, Player); + mediaSession.SetId(Convert.ToBase64String(Guid.NewGuid().ToByteArray())[..8]); + + session ??= mediaSession.Build() ?? throw new InvalidOperationException("Session cannot be null"); ArgumentNullException.ThrowIfNull(session.Id); return (Player, PlayerView); @@ -460,7 +489,7 @@ protected virtual partial void PlatformUpdateShouldMute() return; } - // We're going to mute state. Capture current volume first so we can restore later. + // We're going to mute state. Capture the current volume first so we can restore later. if (MediaElement.ShouldMute) { volumeBeforeMute = Player.Volume; @@ -662,4 +691,6 @@ static class PlaybackState public const int StateStopped = 1; public const int StateError = 7; } + + } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.macios.cs b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.macios.cs index 3354e500b1..352e8fdee0 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.macios.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.macios.cs @@ -1,6 +1,5 @@ using AVFoundation; using AVKit; -using CommunityToolkit.Maui.Core.Primitives; using CommunityToolkit.Maui.Views; using CoreFoundation; using CoreGraphics; @@ -20,11 +19,57 @@ public partial class MediaManager : IDisposable // This field was added to overcome that. bool isInitialSpeedSet; + /// + /// Creates the corresponding platform view of on iOS and macOS. + /// + /// The platform native counterpart of . + public (PlatformMediaElement Player, AVPlayerViewController PlayerViewController) CreatePlatformView() + { + Player = new(); + PlayerViewController = new() + { + Player = Player + }; + + // Pre-initialize Volume and Muted properties to the player object + Player.Muted = MediaElement.ShouldMute; + var volumeDiff = Math.Abs(Player.Volume - MediaElement.Volume); + if (volumeDiff > 0.01) + { + Player.Volume = (float)MediaElement.Volume; + } + + UIApplication.SharedApplication.BeginReceivingRemoteControlEvents(); + +#if IOS + PlayerViewController.UpdatesNowPlayingInfoCenter = false; +#else + PlayerViewController.UpdatesNowPlayingInfoCenter = true; +#endif + var avSession = AVAudioSession.SharedInstance(); + avSession.SetCategory(AVAudioSessionCategory.Playback); + avSession.SetActive(true); + + AddStatusObservers(); + AddPlayedToEndObserver(); + AddErrorObservers(); + + return (Player, PlayerViewController); + } + + /// + /// Releases the managed and unmanaged resources used by the . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + /// /// The default flags used in the iOS and macOS observers. /// - protected const NSKeyValueObservingOptions valueObserverOptions = - NSKeyValueObservingOptions.Initial | NSKeyValueObservingOptions.New; + protected NSKeyValueObservingOptions ValueObserverOptions => NSKeyValueObservingOptions.Initial | NSKeyValueObservingOptions.New; /// /// Observer that tracks when an error has occurred in the playback of the current item. @@ -86,52 +131,6 @@ public partial class MediaManager : IDisposable /// protected IDisposable? MutedObserver { get; set; } - /// - /// Creates the corresponding platform view of on iOS and macOS. - /// - /// The platform native counterpart of . - public (PlatformMediaElement Player, AVPlayerViewController PlayerViewController) CreatePlatformView() - { - Player = new(); - PlayerViewController = new() - { - Player = Player - }; - // Pre-initialize Volume and Muted properties to the player object - Player.Muted = MediaElement.ShouldMute; - var volumeDiff = Math.Abs(Player.Volume - MediaElement.Volume); - if (volumeDiff > 0.01) - { - Player.Volume = (float)MediaElement.Volume; - } - - UIApplication.SharedApplication.BeginReceivingRemoteControlEvents(); - -#if IOS - PlayerViewController.UpdatesNowPlayingInfoCenter = false; -#else - PlayerViewController.UpdatesNowPlayingInfoCenter = true; -#endif - var avSession = AVAudioSession.SharedInstance(); - avSession.SetCategory(AVAudioSessionCategory.Playback); - avSession.SetActive(true); - - AddStatusObservers(); - AddPlayedToEndObserver(); - AddErrorObservers(); - - return (Player, PlayerViewController); - } - - /// - /// Releases the managed and unmanaged resources used by the . - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - protected virtual partial void PlatformPlay() { if (Player?.CurrentTime == PlayerItem?.Duration) @@ -272,7 +271,7 @@ protected virtual partial ValueTask PlatformUpdateSource() Player.ReplaceCurrentItemWithPlayerItem(PlayerItem); CurrentItemErrorObserver = PlayerItem?.AddObserver("error", - valueObserverOptions, (NSObservedChange change) => + ValueObserverOptions, (NSObservedChange change) => { if (Player.CurrentItem?.Error is null) { @@ -280,7 +279,7 @@ protected virtual partial ValueTask PlatformUpdateSource() } var message = $"{Player.CurrentItem?.Error?.LocalizedDescription} - " + - $"{Player.CurrentItem?.Error?.LocalizedFailureReason}"; + $"{Player.CurrentItem?.Error?.LocalizedFailureReason}"; MediaElement.MediaFailed( new MediaFailedEventArgs(message)); @@ -298,6 +297,7 @@ protected virtual partial ValueTask PlatformUpdateSource() { Player.Play(); } + SetPoster(); } else if (PlayerItem is null) @@ -316,11 +316,13 @@ void SetPoster() { return; } + var videoTrack = PlayerItem.Asset.TracksWithMediaType(AVMediaTypes.Video.GetConstant()).FirstOrDefault(); if (videoTrack is not null) { return; } + if (PlayerItem.Asset.Tracks.Length == 0) { // No video track found and no tracks found. This is likely an audio file. So we can't set a poster. @@ -433,6 +435,7 @@ protected virtual partial void PlatformUpdateShouldKeepScreenOn() { return; } + UIApplication.SharedApplication.IdleTimerDisabled = MediaElement.ShouldKeepScreenOn; } @@ -456,7 +459,6 @@ protected virtual partial void PlatformUpdateShouldLoopPlayback() /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// to release both managed and unmanaged resources; to release only unmanaged resources. - protected virtual void Dispose(bool disposing) { if (disposing) @@ -464,10 +466,7 @@ protected virtual void Dispose(bool disposing) if (Player is not null) { Player.Pause(); - Player.InvokeOnMainThread(() => - { - UIApplication.SharedApplication.EndReceivingRemoteControlEvents(); - }); + Player.InvokeOnMainThread(() => { UIApplication.SharedApplication.EndReceivingRemoteControlEvents(); }); // disable the idle timer so screen turns off when media is not playing UIApplication.SharedApplication.IdleTimerDisabled = false; var audioSession = AVAudioSession.SharedInstance(); @@ -549,10 +548,10 @@ void AddStatusObservers() return; } - MutedObserver = Player.AddObserver("muted", valueObserverOptions, MutedChanged); - VolumeObserver = Player.AddObserver("volume", valueObserverOptions, VolumeChanged); - StatusObserver = Player.AddObserver("status", valueObserverOptions, StatusChanged); - TimeControlStatusObserver = Player.AddObserver("timeControlStatus", valueObserverOptions, TimeControlStatusChanged); + MutedObserver = Player.AddObserver("muted", ValueObserverOptions, MutedChanged); + VolumeObserver = Player.AddObserver("volume", ValueObserverOptions, VolumeChanged); + StatusObserver = Player.AddObserver("status", ValueObserverOptions, StatusChanged); + TimeControlStatusObserver = Player.AddObserver("timeControlStatus", ValueObserverOptions, TimeControlStatusChanged); RateObserver = AVPlayer.Notifications.ObserveRateDidChange(RateChanged); } @@ -633,7 +632,7 @@ void StatusChanged(NSObservedChange obj) void TimeControlStatusChanged(NSObservedChange obj) { if (Player is null || Player.Status is AVPlayerStatus.Unknown - || Player.CurrentItem?.Error is not null) + || Player.CurrentItem?.Error is not null) { return; } @@ -668,7 +667,7 @@ void ErrorOccurred(object? sender, NSNotificationEventArgs args) { // Non-fatal error, just log message = args.Notification?.ToString() ?? - "Media playback failed for an unknown reason."; + "Media playback failed for an unknown reason."; Logger?.LogWarning("{LogMessage}", message); } diff --git a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.tizen.cs b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.tizen.cs index 319e364e62..f0f95fbdb7 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.tizen.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.tizen.cs @@ -1,5 +1,4 @@ using System; -using CommunityToolkit.Maui.Core.Primitives; using CommunityToolkit.Maui.Core.Views; using CommunityToolkit.Maui.Views; using Tizen.Multimedia; @@ -29,7 +28,7 @@ public partial class MediaManager : IDisposable /// Indicates whether the device's screen is locked. /// protected bool IsScreenLocked { get; set; } - + /// /// Releases the managed and unmanaged resources used by the . /// diff --git a/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs b/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs index 535918ea92..2a2d44b22a 100644 --- a/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs +++ b/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs @@ -1,4 +1,5 @@ using System.Globalization; +using CommunityToolkit.Maui.Core; using CommunityToolkit.Maui.UnitTests.Mocks; using Xunit; @@ -64,6 +65,10 @@ protected virtual void Dispose(bool isDisposing) options.SetShouldSuppressExceptionsInAnimations(false); options.SetShouldSuppressExceptionsInBehaviors(false); options.SetShouldSuppressExceptionsInConverters(false); + + // Restore default MediaElementOptions + var mediaElementOptions = new MediaElementOptions(); + mediaElementOptions.SetDefaultAndroidViewType(AndroidViewType.SurfaceView); isDisposed = true; } diff --git a/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs b/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs index 4c5a97eeba..5f25d24976 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs @@ -1,4 +1,5 @@ using CommunityToolkit.Maui.Core; +using FluentAssertions; using Xunit; namespace CommunityToolkit.Maui.UnitTests.Extensions; @@ -115,5 +116,28 @@ void HandleShouldUseStatusBarBehaviorOnAndroidModalPageOptionCompleted(object? s isAndroidDialogFragmentServiceInitialized = true; } } + + [Fact] + public void UseMauiCommunityToolkitMediaElement_ShouldUseSurfaceViewByDefault() + { + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkitMediaElement(); + + MediaElementOptions.DefaultAndroidViewType.Should().Be(AndroidViewType.SurfaceView); + } + + [Fact] + public void UseMauiCommunityToolkitMediaElement_ShouldSetDefaultAndroidViewType() + { + MediaElementOptions.DefaultAndroidViewType.Should().Be(AndroidViewType.SurfaceView); + + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkitMediaElement(static options => + { + options.SetDefaultAndroidViewType(AndroidViewType.TextureView); + }); + + MediaElementOptions.DefaultAndroidViewType.Should().Be(AndroidViewType.TextureView); + } } #pragma warning restore CA1416 \ No newline at end of file