Skip to content

Add support to make AndroidForegroundService optional to MediaElement #2658

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

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<toolkit:MediaElement
x:Name="MediaElement"
ShouldAutoPlay="True"
IsAndroidForegroundServiceEnabled="True"
Source="{x:Static constants:StreamingVideoUrls.BuckBunny}"
MetadataArtworkUrl="https://lh3.googleusercontent.com/pw/AP1GczNRrebWCJvfdIau1EbsyyYiwAfwHS0JXjbioXvHqEwYIIdCzuLodQCZmA57GADIo5iB3yMMx3t_vsefbfoHwSg0jfUjIXaI83xpiih6d-oT7qD_slR0VgNtfAwJhDBU09kS5V2T5ZML-WWZn8IrjD4J-g=w1792-h1024-s-no-gm"
MetadataTitle="Big Buck Bunny"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ void DisplayPopup(object sender, EventArgs e)
{
AndroidViewType = AndroidViewType.SurfaceView,
Source = MediaSource.FromResource("AppleVideo.mp4"),
MetadataArtworkUrl = botImageUrl,
IsAndroidForegroundServiceEnabled = false,
HeightRequest = 600,
WidthRequest = 600,
ShouldAutoPlay = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,23 @@
android:supportsRtl="true">

<meta-data android:name="com.google.android.geo.API_KEY" android:value="PASTE-YOUR-API-KEY-HERE" />

<service android:name="communityToolkit.maui.media.services" android:stopWithTask="true" android:exported="false" android:enabled="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
</application>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
<!-- Samsung -->
<uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
{
// Update the default MediaElementOptions for MediaElement if Action is not null
options?.Invoke(new MediaElementOptions(builder));

// Perform Handler configuration
builder.ConfigureMauiHandlers(h =>
{
Expand All @@ -33,7 +33,7 @@
});

#if ANDROID
builder.Services.AddSingleton<Media.Services.MediaControlsService>();

Check warning on line 36 in src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

This call site is reachable on: 'Android' 26.0 and later, 'iOS' 15.0 and later, 'maccatalyst' 15.0 and later, 'Tizen' 6.5 and later, 'Windows' 10.0.17763 and later. 'MediaControlsService' is only supported on: 'Android' 26.0 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 36 in src/CommunityToolkit.Maui.MediaElement/AppBuilderExtensions.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

This call site is reachable on: 'Android' 26.0 and later, 'iOS' 15.0 and later, 'maccatalyst' 15.0 and later, 'Tizen' 6.5 and later, 'Windows' 10.0.17763 and later. 'MediaControlsService' is only supported on: 'Android' 26.0 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)
#endif

return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
5 changes: 5 additions & 0 deletions src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ internal event EventHandler StopRequested
/// </summary>
public AndroidViewType AndroidViewType { get; init; } = MediaElementOptions.DefaultAndroidViewType;

/// <summary>
/// Gets or sets whether the Android Foreground Service is enabled.
/// </summary>
public bool IsAndroidForegroundServiceEnabled { get; init; } = MediaElementOptions.IsAndroidForegroundServiceEnabled;

/// <summary>
/// Gets or sets whether the media should start playing as soon as it's loaded.
/// Default is <see langword="false"/>. This is a bindable property.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,19 @@ internal MediaElementOptions(in MauiAppBuilder builder) : this()
/// </summary>
internal static AndroidViewType DefaultAndroidViewType { get; private set; } = AndroidViewType.SurfaceView;

/// <summary>
/// Set Android Foreground Service for MediaElement on construction
/// </summary>
internal static bool IsAndroidForegroundServiceEnabled { get; private set; } = true;

/// <summary>
/// Set Android View type for MediaElement as SurfaceView or TextureView on construction
/// </summary>
public void SetDefaultAndroidViewType(AndroidViewType androidViewType) => DefaultAndroidViewType = androidViewType;

/// <summary>
/// Set Android Foreground Service for MediaElement on construction
/// </summary>
/// <param name="androidForegroundServiceEnabled"></param>
public void SetDefaultAndroidForegroundService(bool androidForegroundServiceEnabled) => IsAndroidForegroundServiceEnabled = androidForegroundServiceEnabled;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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;
Expand Down Expand Up @@ -129,11 +130,11 @@ or PlaybackState.StateSkippingToQueueItem
/// <returns>The platform native counterpart of <see cref="MediaElement"/>.</returns>
/// <exception cref="NullReferenceException">Thrown when <see cref="Context"/> is <see langword="null"/> or when the platform view could not be created.</exception>
[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)
Expand Down Expand Up @@ -352,7 +353,7 @@ protected virtual async partial ValueTask PlatformUpdateSource()
return;
}

if (connection is null)
if (connection is null && androidForegroundServiceEnabled)
{
StartService();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,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
Loading