diff --git a/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs b/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs index 1dd763355a..d5f9e188f5 100644 --- a/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs +++ b/samples/CommunityToolkit.Maui.Sample/MauiProgram.cs @@ -72,6 +72,7 @@ public static MauiApp CreateMauiApp() .UseMauiCommunityToolkitMediaElement(static options => { options.SetDefaultAndroidViewType(AndroidViewType.TextureView); + options.SetDefaultAndroidForegroundService(true); }) .ConfigureMauiHandlers(static handlers => { diff --git a/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml b/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml index 8f95e12183..48265afc3f 100644 --- a/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml +++ b/samples/CommunityToolkit.Maui.Sample/Platforms/Android/AndroidManifest.xml @@ -4,12 +4,23 @@ android:supportsRtl="true"> + + + + + + + + + + diff --git a/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs b/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs index 3f7eb88638..e3d70f8d39 100644 --- a/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs @@ -25,7 +25,7 @@ public static MauiAppBuilder UseMauiCommunityToolkitMediaElement(this MauiAppBui { // Update the default MediaElementOptions for MediaElement if Action is not null options?.Invoke(new MediaElementOptions(builder)); - + // Perform Handler configuration builder.ConfigureMauiHandlers(h => { diff --git a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs index bd1e00e6e7..9a1ed2e361 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Handlers/MediaElementHandler.android.cs @@ -24,7 +24,7 @@ protected override MauiMediaElement CreatePlatformView() VirtualView, Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null")); - var (_, playerView) = MediaManager.CreatePlatformView(VirtualView.AndroidViewType); + var (_, playerView) = MediaManager.CreatePlatformView(VirtualView.AndroidViewType, VirtualView.IsAndroidForegroundServiceEnabled); return new(Context, playerView); } diff --git a/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs b/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs index 6d663135f2..d6fceec637 100644 --- a/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs @@ -225,6 +225,8 @@ internal event EventHandler StopRequested /// public AndroidViewType AndroidViewType { get; init; } = MediaElementOptions.DefaultAndroidViewType; + internal bool IsAndroidForegroundServiceEnabled { get; init; } = MediaElementOptions.IsAndroidForegroundServiceEnabled; + /// /// Gets or sets whether the media should start playing as soon as it's loaded. /// Default is . This is a bindable property. diff --git a/src/CommunityToolkit.Maui.MediaElement/MediaElementOptions.shared.cs b/src/CommunityToolkit.Maui.MediaElement/MediaElementOptions.shared.cs index 48ae9649c7..0acb383fab 100644 --- a/src/CommunityToolkit.Maui.MediaElement/MediaElementOptions.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/MediaElementOptions.shared.cs @@ -22,8 +22,19 @@ internal MediaElementOptions(in MauiAppBuilder builder) : this() /// internal static AndroidViewType DefaultAndroidViewType { get; private set; } = AndroidViewType.SurfaceView; + /// + /// Set Android Foreground Service for MediaElement on construction + /// + internal static bool IsAndroidForegroundServiceEnabled { get; private set; } = true; + /// /// Set Android View type for MediaElement as SurfaceView or TextureView on construction /// public void SetDefaultAndroidViewType(AndroidViewType androidViewType) => DefaultAndroidViewType = androidViewType; + + /// + /// Set Android Foreground Service for MediaElement on construction + /// + /// Specifies whether the Android Foreground Service should be enabled for the MediaElement. Set to true to enable, or false to disable. + public void SetDefaultAndroidForegroundService(bool androidForegroundServiceEnabled) => IsAndroidForegroundServiceEnabled = androidForegroundServiceEnabled; } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.MediaElement/Views/MauiMediaElement.android.cs b/src/CommunityToolkit.Maui.MediaElement/Views/MauiMediaElement.android.cs index b9799a3874..381346ff17 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Views/MauiMediaElement.android.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Views/MauiMediaElement.android.cs @@ -8,11 +8,6 @@ using AndroidX.Media3.UI; using CommunityToolkit.Maui.Views; -[assembly: UsesPermission(Android.Manifest.Permission.ForegroundServiceMediaPlayback)] -[assembly: UsesPermission(Android.Manifest.Permission.ForegroundService)] -[assembly: UsesPermission(Android.Manifest.Permission.MediaContentControl)] -[assembly: UsesPermission(Android.Manifest.Permission.PostNotifications)] - namespace CommunityToolkit.Maui.Core.Views; /// diff --git a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs index 7da0a27ea8..24ef7be27a 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs @@ -21,6 +21,7 @@ namespace CommunityToolkit.Maui.Core.Views; public partial class MediaManager : Java.Lang.Object, IPlayerListener { + bool androidForegroundServiceEnabled; const int bufferState = 2; const int readyState = 3; const int endedState = 4; @@ -130,11 +131,11 @@ 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(AndroidViewType androidViewType) + public (PlatformMediaElement platformView, PlayerView PlayerView) CreatePlatformView(AndroidViewType androidViewType, bool androidForegroundServiceEnabled) { Player = new ExoPlayerBuilder(MauiContext.Context).Build() ?? throw new InvalidOperationException("Player cannot be null"); Player.AddListener(this); - + this.androidForegroundServiceEnabled = androidForegroundServiceEnabled; if (androidViewType is AndroidViewType.SurfaceView) { PlayerView = new PlayerView(MauiContext.Context) @@ -353,7 +354,7 @@ protected virtual async partial ValueTask PlatformUpdateSource() return; } - if (connection is null) + if (connection is null && androidForegroundServiceEnabled) { StartService(); } diff --git a/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs b/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs index 27ef4de017..42bdd5cd30 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Extensions/AppBuilderExtensionsTests.cs @@ -166,5 +166,24 @@ public void UseMauiCommunityToolkitMediaElement_ShouldSetDefaultAndroidViewType( MediaElementOptions.DefaultAndroidViewType.Should().Be(AndroidViewType.TextureView); } + + [Fact] + public void UseMauiCommunityToolkitMediaElement_ShouldSetAndroidServiceByDefault() + { + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkitMediaElement(); + MediaElementOptions.IsAndroidForegroundServiceEnabled.Should().Be(true); + } + + [Fact] + public void UseMauiCommunityToolkitMediaElement_ServiceCanBeDisabled() + { + var builder = MauiApp.CreateBuilder(); + builder.UseMauiCommunityToolkitMediaElement(static options => + { + options.SetDefaultAndroidForegroundService(false); + }); + MediaElementOptions.IsAndroidForegroundServiceEnabled.Should().Be(false); + } } #pragma warning restore CA1416 \ No newline at end of file