Skip to content

Commit 0918c78

Browse files
committed
Fork media element project to this repo as library
1 parent 1fec28f commit 0918c78

File tree

49 files changed

+6538
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+6538
-2
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Runtime.Versioning;
3+
using CommunityToolkit.Maui.Core;
4+
using CommunityToolkit.Maui.Core.Handlers;
5+
using CommunityToolkit.Maui.Views;
6+
using Microsoft.Maui.Hosting;
7+
8+
namespace CommunityToolkit.Maui;
9+
10+
/// <summary>
11+
/// This class contains MediaElement's <see cref="MauiAppBuilder"/> extensions.
12+
/// </summary>
13+
[SupportedOSPlatform("iOS15.0")]
14+
[SupportedOSPlatform("Android26.0")]
15+
[SupportedOSPlatform("Windows10.0.17763")]
16+
public static class AppBuilderExtensions
17+
{
18+
/// <summary>
19+
/// Initializes the .NET MAUI Community Toolkit MediaElement Library
20+
/// </summary>
21+
/// <param name="builder"><see cref="MauiAppBuilder"/> generated by <see cref="MauiApp"/>.</param>
22+
/// <param name="options"></param>
23+
/// <returns><see cref="MauiAppBuilder"/> initialized for <see cref="MediaElement"/>.</returns>
24+
public static MauiAppBuilder UseMauiCommunityToolkitMediaElement(this MauiAppBuilder builder, Action<MediaElementOptions>? options = null)
25+
{
26+
// Update the default MediaElementOptions for MediaElement if Action is not null
27+
options?.Invoke(new MediaElementOptions(builder));
28+
29+
// Perform Handler configuration
30+
builder.ConfigureMauiHandlers(h =>
31+
{
32+
h.AddHandler<MediaElement, MediaElementHandler>();
33+
});
34+
35+
#if ANDROID
36+
builder.Services.AddSingleton<Media.Services.MediaControlsService>();
37+
#endif
38+
39+
return builder;
40+
}
41+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+

2+
[assembly: Microsoft.Maui.Controls.XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Converters))]
3+
[assembly: Microsoft.Maui.Controls.XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Core))]
4+
[assembly: Microsoft.Maui.Controls.XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Core.Handlers))]
5+
[assembly: Microsoft.Maui.Controls.XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Core.Views))]
6+
[assembly: Microsoft.Maui.Controls.XmlnsDefinition(Constants.XamlNamespace, Constants.CommunityToolkitNamespacePrefix + nameof(CommunityToolkit.Maui.Views))]
7+
8+
[assembly: Microsoft.Maui.Controls.XmlnsPrefix(Constants.XamlNamespace, "toolkit")]
9+
10+
class Constants
11+
{
12+
public const string XamlNamespace = "http://schemas.microsoft.com/dotnet/2022/maui/toolkit";
13+
14+
public const string CommunityToolkitNamespacePrefix = $"{nameof(CommunityToolkit)}.{nameof(CommunityToolkit.Maui)}.";
15+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net10.0;net10.0-android;net10.0-ios</TargetFrameworks>
5+
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
6+
<SingleProject>true</SingleProject>
7+
<UseMaui>true</UseMaui>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<Nullable>enable</Nullable>
10+
<GenerateDocumentationFile>false</GenerateDocumentationFile>
11+
<IsAotCompatible>true</IsAotCompatible>
12+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
13+
<AllowMissingPrunePackageData>true</AllowMissingPrunePackageData>
14+
15+
16+
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
17+
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">26.0</SupportedOSPlatformVersion>
18+
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
19+
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
20+
<RootNamespace>CommunityToolkit.Maui</RootNamespace>
21+
<AndroidGenerateResourceDesigner>false</AndroidGenerateResourceDesigner>
22+
</PropertyGroup>
23+
24+
<PropertyGroup>
25+
<GitInfoReportImportance>high</GitInfoReportImportance>
26+
<PackageId>CommunityToolkit.Maui.MediaElement</PackageId>
27+
<Summary>MediaElement is a view for playing video and audio in your .NET MAUI app.</Summary>
28+
<Authors>Microsoft</Authors>
29+
<Owners>Microsoft</Owners>
30+
<NeutralLanguage>en</NeutralLanguage>
31+
<Product>CommunityToolkit.Maui (net10.0)</Product>
32+
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
33+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
34+
<PackageProjectUrl>https://github.com/CommunityToolkit/Maui</PackageProjectUrl>
35+
<RepositoryUrl>https://github.com/CommunityToolkit/Maui</RepositoryUrl>
36+
<PackageReleaseNotes>See https://github.com/CommunityToolkit/Maui/releases</PackageReleaseNotes>
37+
<DefineConstants>$(DefineConstants);</DefineConstants>
38+
<UseFullSemVerForNuGet>false</UseFullSemVerForNuGet>
39+
<Title>.NET MAUI MediaElement</Title>
40+
<Description>MediaElement is a view for playing video and audio in your .NET MAUI app.</Description>
41+
<PackageIcon>icon.png</PackageIcon>
42+
<Product>$(AssemblyName) ($(TargetFramework))</Product>
43+
<Version>1.0.0-pre1</Version>
44+
<PackageVersion>$(Version)$(VersionSuffix)</PackageVersion>
45+
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
46+
<PackageTags>dotnet,maui,toolkit,kit,communitytoolkit,dotnetcommunitytoolkit,mediaelement,mediaplayer,audio,video</PackageTags>
47+
<Configurations>Debug;Release</Configurations>
48+
<NoWarn>$(NoWarn);CS1591;CA1422</NoWarn>
49+
</PropertyGroup>
50+
51+
<!-- Removed analyzer references - not needed for local build -->
52+
53+
<ItemGroup Condition="$(TargetFramework.Contains('-android'))">
54+
<PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer" Version="1.8.0" />
55+
<PackageReference Include="Xamarin.AndroidX.Media3.Common" Version="1.8.0" />
56+
<PackageReference Include="Xamarin.AndroidX.Media3.UI" Version="1.8.0" />
57+
<PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer.Rtsp" Version="1.8.0" />
58+
<PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer.Hls" Version="1.8.0" />
59+
<PackageReference Include="Xamarin.AndroidX.Media3.ExoPlayer.Dash" Version="1.8.0" />
60+
<PackageReference Include="Xamarin.AndroidX.Media3.Session" Version="1.8.0" />
61+
62+
<!--include the textureview.xml needed for creating textureview in android-->
63+
<Folder Include="Platforms\Android\Resources\layout\" />
64+
<AndroidResource Include="Platforms\Android\Resources\layout\textureview.xml" />
65+
</ItemGroup>
66+
67+
<ItemGroup>
68+
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.11" />
69+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" Condition=" '$(Configuration)'=='Release' " PrivateAssets="All" />
70+
</ItemGroup>
71+
72+
<ItemGroup>
73+
<EmbeddedResource Include="ResourceDictionary.windows.xaml">
74+
<LogicalName>ResourceDictionary.windows.xaml</LogicalName>
75+
</EmbeddedResource>
76+
</ItemGroup>
77+
78+
<!-- Exclude platform-specific files for unsupported platforms -->
79+
<ItemGroup>
80+
<!-- Exclude macOS files when not building for macOS -->
81+
<Compile Remove="**\*.macios.cs" Condition="!$(TargetFramework.Contains('-maccatalyst'))" />
82+
<!-- Exclude Tizen files -->
83+
<Compile Remove="**\*.tizen.cs" />
84+
<!-- Exclude Windows files when not building for Windows -->
85+
<Compile Remove="**\*.windows.cs" Condition="!$(TargetFramework.Contains('-windows'))" />
86+
<!-- Exclude iOS files when not building for iOS -->
87+
<Compile Remove="**\*.ios.cs" Condition="!$(TargetFramework.Contains('-ios'))" />
88+
<!-- Exclude Android files when not building for Android -->
89+
<Compile Remove="**\*.android.cs" Condition="!$(TargetFramework.Contains('-android'))" />
90+
<!-- Exclude net.cs file when building for platform-specific targets (android, ios, windows) -->
91+
<Compile Remove="**\*.net.cs" Condition="$(TargetFramework.Contains('-android')) OR $(TargetFramework.Contains('-ios')) OR $(TargetFramework.Contains('-windows'))" />
92+
</ItemGroup>
93+
94+
</Project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.ComponentModel;
2+
using System.Globalization;
3+
using CommunityToolkit.Maui.Views;
4+
5+
namespace CommunityToolkit.Maui.Converters;
6+
7+
/// <summary>
8+
/// A <see cref="TypeConverter"/> specific to converting a string value to a <see cref="FileMediaSource"/>.
9+
/// </summary>
10+
[TypeConverter(typeof(FileMediaSource))]
11+
public sealed class FileMediaSourceConverter : TypeConverter
12+
{
13+
/// <inheritdoc/>
14+
/// <exception cref="InvalidOperationException">Thrown when <paramref name="value"/> is <see langword="null"/> or empty.</exception>
15+
public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
16+
{
17+
var filePath = value.ToString() ?? string.Empty;
18+
19+
return string.IsNullOrWhiteSpace(filePath)
20+
? (FileMediaSource)MediaSource.FromFile(filePath)
21+
: throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(FileMediaSource)}");
22+
}
23+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.ComponentModel;
2+
using System.Globalization;
3+
using CommunityToolkit.Maui.Views;
4+
5+
namespace CommunityToolkit.Maui.Converters;
6+
7+
/// <summary>
8+
/// A <see cref="TypeConverter"/> specific to converting a string value to a <see cref="MediaSource"/>.
9+
/// </summary>
10+
public sealed class MediaSourceConverter : TypeConverter
11+
{
12+
const string embeddedResourcePrefix = "embed://";
13+
const string fileSystemPrefix = "filesystem://";
14+
15+
/// <inheritdoc/>
16+
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
17+
=> sourceType == typeof(string);
18+
19+
/// <inheritdoc/>
20+
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
21+
=> destinationType == typeof(string);
22+
23+
/// <inheritdoc/>
24+
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value)
25+
{
26+
var valueAsString = value?.ToString() ?? string.Empty;
27+
28+
if (string.IsNullOrWhiteSpace(valueAsString))
29+
{
30+
return null;
31+
}
32+
33+
var valueAsStringLowercase = valueAsString.ToLowerInvariant();
34+
35+
if (valueAsStringLowercase.StartsWith(embeddedResourcePrefix))
36+
{
37+
return MediaSource.FromResource(
38+
valueAsString[embeddedResourcePrefix.Length..]);
39+
}
40+
41+
if (valueAsStringLowercase.StartsWith(fileSystemPrefix))
42+
{
43+
return MediaSource.FromFile(valueAsString[fileSystemPrefix.Length..]);
44+
}
45+
46+
return Uri.TryCreate(valueAsString, UriKind.Absolute, out var uri) && uri.Scheme != "file"
47+
? MediaSource.FromUri(uri)
48+
: MediaSource.FromFile(valueAsString);
49+
}
50+
51+
/// <inheritdoc/>
52+
public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) => value switch
53+
{
54+
UriMediaSource uriMediaSource => uriMediaSource.ToString(),
55+
FileMediaSource fileMediaSource => fileMediaSource.ToString(),
56+
ResourceMediaSource resourceMediaSource => resourceMediaSource.ToString(),
57+
MediaSource => string.Empty,
58+
_ => throw new ArgumentException($"Invalid Media Source", nameof(value))
59+
};
60+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using Microsoft.Maui.Controls;
3+
namespace CommunityToolkit.Maui.Extensions;
4+
5+
static class ElementExtensions
6+
{
7+
public static bool TryFindParent<T>(this Element? child, [NotNullWhen(true)] out T? parent) where T : VisualElement
8+
{
9+
while (child is not null)
10+
{
11+
if (child.Parent is T element)
12+
{
13+
parent = element;
14+
return true;
15+
}
16+
17+
child = child.Parent;
18+
}
19+
20+
parent = null;
21+
return false;
22+
}
23+
24+
public static bool TryFindParentPlatformView<T>(this Element? child, [NotNullWhen(true)] out T? parentPlatformView)
25+
{
26+
while (child is not null)
27+
{
28+
if (child.Parent?.Handler?.PlatformView is T element)
29+
{
30+
parentPlatformView = element;
31+
return true;
32+
}
33+
34+
child = child.Parent;
35+
}
36+
37+
parentPlatformView = default;
38+
return false;
39+
}
40+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Microsoft.Maui.Controls;
2+
namespace CommunityToolkit.Maui.Extensions;
3+
4+
// Since MediaElement can't access .NET MAUI internals we have to copy this code here
5+
// https://github.com/dotnet/maui/blob/main/src/Controls/src/Core/Platform/PageExtensions.cs
6+
static class PageExtensions
7+
{
8+
internal static Page GetCurrentPage(this Page currentPage)
9+
{
10+
if (currentPage.NavigationProxy.ModalStack.LastOrDefault() is Page modal)
11+
{
12+
return modal;
13+
}
14+
else if (currentPage is FlyoutPage fp)
15+
{
16+
return GetCurrentPage(fp.Detail);
17+
}
18+
else if (currentPage is Shell shell && shell.CurrentItem?.CurrentItem is IShellSectionController ssc)
19+
{
20+
return ssc.PresentedPage;
21+
}
22+
else if (currentPage is IPageContainer<Page> pc)
23+
{
24+
return GetCurrentPage(pc.CurrentPage);
25+
}
26+
else
27+
{
28+
return currentPage;
29+
}
30+
}
31+
32+
internal record struct ParentWindow
33+
{
34+
static Page CurrentPage => GetCurrentPage(Application.Current?.Windows[^1].Page ?? throw new InvalidOperationException($"{nameof(Page)} cannot be null."));
35+
/// <summary>
36+
/// Checks if the parent window is null.
37+
/// </summary>
38+
public static bool Exists
39+
{
40+
get
41+
{
42+
if (CurrentPage.GetParentWindow() is null)
43+
{
44+
return false;
45+
}
46+
if (CurrentPage.GetParentWindow().Handler is null)
47+
{
48+
return false;
49+
}
50+
51+
return CurrentPage.GetParentWindow().Handler?.PlatformView is not null;
52+
}
53+
}
54+
}
55+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using CommunityToolkit.Maui.Core.Views;
2+
using CommunityToolkit.Maui.Views;
3+
using Microsoft.Maui.Handlers;
4+
5+
namespace CommunityToolkit.Maui.Core.Handlers;
6+
7+
public partial class MediaElementHandler : ViewHandler<MediaElement, MauiMediaElement>, IDisposable
8+
{
9+
/// <summary>
10+
/// Maps the <see cref="IMediaElement.ShouldLoopPlayback"/> property between the abstract
11+
/// <see cref="MediaElement"/> and platform counterpart.
12+
/// </summary>
13+
/// <param name="handler">The associated handler.</param>
14+
/// <param name="mediaElement">The associated <see cref="MediaElement"/> instance.</param>
15+
public static void ShouldLoopPlayback(MediaElementHandler handler, MediaElement mediaElement)
16+
{
17+
handler.MediaManager?.UpdateShouldLoopPlayback();
18+
}
19+
20+
21+
protected override MauiMediaElement CreatePlatformView()
22+
{
23+
MediaManager ??= new(MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null"),
24+
VirtualView,
25+
Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null"));
26+
27+
var (_, playerView) = MediaManager.CreatePlatformView(VirtualView.AndroidViewType);
28+
return new(Context, playerView);
29+
}
30+
31+
protected override void DisconnectHandler(MauiMediaElement platformView)
32+
{
33+
platformView.Dispose();
34+
Dispose();
35+
base.DisconnectHandler(platformView);
36+
}
37+
}

0 commit comments

Comments
 (0)