diff --git a/samples/LibVLCSharp.Android.Sample/Resources/Resource.Designer.cs b/samples/LibVLCSharp.Android.Sample/Resources/Resource.Designer.cs
index a1dca9f4f..26deb00a7 100644
--- a/samples/LibVLCSharp.Android.Sample/Resources/Resource.Designer.cs
+++ b/samples/LibVLCSharp.Android.Sample/Resources/Resource.Designer.cs
@@ -14,7 +14,7 @@ namespace LibVLCSharp.Android.Sample
{
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.2.0.155")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.2.2.120")]
public partial class Resource
{
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/App.xaml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/App.xaml
new file mode 100644
index 000000000..50ca86b67
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/App.xaml.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/App.xaml.cs
new file mode 100644
index 000000000..88286d909
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/App.xaml.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Maui.Controls;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ ///
+ /// Represents the main App.
+ ///
+ public partial class App : Application
+ {
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public App()
+ {
+ InitializeComponent();
+ ConfigureUnhandledExceptionHandling();
+
+ MainPage = new MainPage();
+ }
+
+ private static void ConfigureUnhandledExceptionHandling()
+ {
+ AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
+ {
+ if (e.ExceptionObject is Exception ex)
+ {
+ HandleException(ex);
+ }
+ };
+
+ TaskScheduler.UnobservedTaskException += (sender, e) =>
+ {
+ if (e.Exception is Exception ex)
+ {
+ HandleException(ex);
+ }
+ e.SetObserved(); // Prevents the process from terminating.
+ };
+ }
+
+ private static void HandleException(Exception ex)
+ {
+ if (ex != null)
+ {
+ Console.WriteLine(ex.Message);
+ }
+ }
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/AppShell.xaml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/AppShell.xaml
new file mode 100644
index 000000000..6885a1c3a
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/AppShell.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/AppShell.xaml.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/AppShell.xaml.cs
new file mode 100644
index 000000000..7d3493c6f
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/AppShell.xaml.cs
@@ -0,0 +1,16 @@
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ ///
+ /// Represents the main shell of the application.
+ ///
+ public partial class AppShell : Shell
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/LibVLCSharp.MAUI.Sample.MediaElement.csproj b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/LibVLCSharp.MAUI.Sample.MediaElement.csproj
new file mode 100644
index 000000000..b4b416ddf
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/LibVLCSharp.MAUI.Sample.MediaElement.csproj
@@ -0,0 +1,61 @@
+
+
+ net8.0-android;net8.0-ios
+
+
+ Exe
+ LibVLCSharp.MAUI.Sample.MediaElement
+ true
+ true
+ enable
+ latest
+
+ LibVLCSharp.MAUI.Sample.MediaElement
+
+ com.companyname.libvlcsharp.maui.sample.mediaelement
+ 3e92aff6-59b5-48cd-a2a0-3ecb5d63d8fb
+
+ 1.0
+ 1
+ 11.0
+ 13.1
+ 21.0
+ 10.0.17763.0
+ 6.5
+ True
+ 10.0.19041.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainPage.xaml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainPage.xaml
new file mode 100644
index 000000000..8477da926
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainPage.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainPage.xaml.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainPage.xaml.cs
new file mode 100644
index 000000000..bccf31a82
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainPage.xaml.cs
@@ -0,0 +1,30 @@
+using Microsoft.Maui.Controls;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ ///
+ /// Represnets the Main Page.
+ ///
+ public partial class MainPage : ContentPage
+ {
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+
+ void OnAppearing(object sender, System.EventArgs e)
+ {
+ base.OnAppearing();
+ ((MainViewModel)BindingContext).OnAppearing();
+ }
+
+ void OnDisappearing(object sender, System.EventArgs e)
+ {
+ base.OnDisappearing();
+ ((MainViewModel)BindingContext).OnDisappearing();
+ }
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainViewModel.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainViewModel.cs
new file mode 100644
index 000000000..6d8304e67
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MainViewModel.cs
@@ -0,0 +1,92 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using LibVLCSharp.Shared;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ ///
+ /// Represents the main viewmodel.
+ ///
+ public class MainViewModel : INotifyPropertyChanged
+ {
+ ///
+ /// Property changed event
+ ///
+ public event PropertyChangedEventHandler PropertyChanged = null!;
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public MainViewModel()
+ {
+ }
+
+ private LibVLC _libVLC;
+
+ ///
+ /// Gets the instance.
+ ///
+ public LibVLC LibVLC
+ {
+ get => _libVLC;
+ private set => SetProperty(ref _libVLC, value);
+ }
+
+ private LibVLCSharp.Shared.MediaPlayer _mediaPlayer;
+ ///
+ /// Gets the instance.
+ ///
+ public LibVLCSharp.Shared.MediaPlayer MediaPlayer
+ {
+ get => _mediaPlayer;
+ private set => SetProperty(ref _mediaPlayer, value);
+ }
+
+ ///
+ /// Initialize LibVLC and playback when page appears
+ ///
+ public void OnAppearing()
+ {
+ if (LibVLC == null)
+ {
+ LibVLC = new LibVLC(enableDebugLogs: true);
+ }
+
+ if (MediaPlayer == null)
+ {
+ var media = new Media(LibVLC, new Uri("http://streams.videolan.org/streams/mkv/multiple_tracks.mkv"));
+ MediaPlayer = new LibVLCSharp.Shared.MediaPlayer(media)
+ {
+ EnableHardwareDecoding = true
+ };
+ media.Dispose();
+ MediaPlayer.Play();
+ }
+ }
+
+ ///
+ /// Dispose MediaPlayer and LibVLC when page disappears
+ ///
+ public void OnDisappearing()
+ {
+ MediaPlayer?.Dispose();
+ MediaPlayer = null;
+
+ LibVLC?.Dispose();
+ LibVLC = null;
+ }
+
+ ///
+ /// Set property and notify UI
+ ///
+ private void SetProperty(ref T field, T value, [CallerMemberName] string propertyName = "")
+ {
+ if (!Equals(field, value))
+ {
+ field = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MauiProgram.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MauiProgram.cs
new file mode 100644
index 000000000..a259a4e0f
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/MauiProgram.cs
@@ -0,0 +1,33 @@
+using Microsoft.Extensions.Logging;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ ///
+ /// The MauiProgram class is responsible for creating and configuring the MAUI application.
+ ///
+ public static class MauiProgram
+ {
+ ///
+ /// Creates and configures the MAUI application.
+ ///
+ /// The configured MAUI application.
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ })
+ .UseLibVLCSharp(); // Add your custom fonts and handler from your library
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ return builder.Build();
+ }
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/AndroidManifest.xml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 000000000..e9937ad77
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/MainActivity.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/MainActivity.cs
new file mode 100644
index 000000000..83cdd2401
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/MainActivity.cs
@@ -0,0 +1,12 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using Microsoft.Maui;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+ public class MainActivity : MauiAppCompatActivity
+ {
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/MainApplication.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/MainApplication.cs
new file mode 100644
index 000000000..c28054c29
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/MainApplication.cs
@@ -0,0 +1,16 @@
+using Android.App;
+using Android.Runtime;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ [Application]
+ public class MainApplication : MauiApplication
+ {
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/Resources/values/colors.xml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 000000000..c04d7492a
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/AppDelegate.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 000000000..f0e33d31d
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/Info.plist b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 000000000..c96dd0a22
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/Program.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 000000000..30090b8eb
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/App.xaml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/App.xaml
new file mode 100644
index 000000000..8daf553d0
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/App.xaml.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/App.xaml.cs
new file mode 100644
index 000000000..e5405ac9b
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,25 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement.WinUI
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public partial class App : MauiWinUIApplication
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/Package.appxmanifest b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 000000000..fcdf34f18
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/app.manifest b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/app.manifest
new file mode 100644
index 000000000..a62726014
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/AppDelegate.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 000000000..6e7ff1fe9
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,18 @@
+using Foundation;
+using Microsoft.Maui;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ ///
+ /// The AppDelegate class is responsible for handling application-level events.
+ ///
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ ///
+ /// This method is used to create and configure the Maui application instance.
+ ///
+ /// Returns the configured application instance.
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/Info.plist b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/Info.plist
new file mode 100644
index 000000000..0004a4fde
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/Program.cs b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/Program.cs
new file mode 100644
index 000000000..30641945e
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Platforms/iOS/Program.cs
@@ -0,0 +1,22 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace LibVLCSharp.MAUI.Sample.MediaElement
+{
+ ///
+ /// The Program class contains the main entry point of the application.
+ ///
+ public class Program
+ {
+ ///
+ /// The main entry point of the application.
+ ///
+ /// An array of command-line arguments.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Properties/launchSettings.json b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Properties/launchSettings.json
new file mode 100644
index 000000000..edf8aadcc
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/AppIcon/appicon.svg b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/AppIcon/appicon.svg
new file mode 100644
index 000000000..9d63b6513
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/AppIcon/appiconfg.svg b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 000000000..21dfb25f1
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Fonts/OpenSans-Regular.ttf b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 000000000..39b6aff94
Binary files /dev/null and b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Fonts/OpenSans-Semibold.ttf b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 000000000..e0d2fa56e
Binary files /dev/null and b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Images/dotnet_bot.svg b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Images/dotnet_bot.svg
new file mode 100644
index 000000000..abfaff26a
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Raw/AboutAssets.txt b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Raw/AboutAssets.txt
new file mode 100644
index 000000000..15d624484
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Splash/splash.svg b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Splash/splash.svg
new file mode 100644
index 000000000..21dfb25f1
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Styles/Colors.xaml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Styles/Colors.xaml
new file mode 100644
index 000000000..43ebcdedc
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Styles/Colors.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+ #512BD4
+ #DFD8F7
+ #2B0B98
+ White
+ Black
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #F7B548
+ #FFD590
+ #FFE5B9
+ #28C2D1
+ #7BDDEF
+ #C3F2F4
+ #3E8EED
+ #72ACF1
+ #A7CBF6
+
+
\ No newline at end of file
diff --git a/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Styles/Styles.xaml b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Styles/Styles.xaml
new file mode 100644
index 000000000..7159c06db
--- /dev/null
+++ b/samples/MAUI/LibVLCSharp.MAUI.Sample.MediaElement/Resources/Styles/Styles.xaml
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/LibVLCSharp.MAUI/AppHostBuilderExtensions.cs b/src/LibVLCSharp.MAUI/AppHostBuilderExtensions.cs
index ae0515d8d..9b40c1ec9 100644
--- a/src/LibVLCSharp.MAUI/AppHostBuilderExtensions.cs
+++ b/src/LibVLCSharp.MAUI/AppHostBuilderExtensions.cs
@@ -10,10 +10,22 @@ public static class AppHostBuilderExtensions
///
/// MauiAppBuilder
/// configured builder for libvlcsharp
- public static MauiAppBuilder UseLibVLCSharp(this MauiAppBuilder builder) =>
+ public static MauiAppBuilder UseLibVLCSharp(this MauiAppBuilder builder)
+ {
+ // Register LibVLCSharp handlers
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(VideoView), typeof(VideoViewHandler));
});
+
+ // Configure custom fonts
+ builder.ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("FontAwesome5Brands.otf", "FontAwesomeBrands");
+ fonts.AddFont("FontAwesome5Solid.otf", "FontAwesomeSolid");
+ });
+
+ return builder;
+ }
}
}
diff --git a/src/LibVLCSharp.MAUI/Converters/BufferingProgressToBoolConverter.cs b/src/LibVLCSharp.MAUI/Converters/BufferingProgressToBoolConverter.cs
new file mode 100644
index 000000000..39d705c09
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/Converters/BufferingProgressToBoolConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Globalization;
+using Microsoft.Maui.Controls;
+
+namespace LibVLCSharp.MAUI.Converters
+{
+ ///
+ /// Converts a value not equals to 0 and 1 to true.
+ ///
+ internal class BufferingProgressToBoolConverter : IValueConverter
+ {
+ ///
+ /// Modifies the source data before passing it to the target for display in the UI.
+ ///
+ /// The source data being passed to the target.
+ /// The type of the target property.
+ /// An optional parameter to be used in the converter logic.
+ /// The culture of the conversion.
+ /// true if value is not equals to 0 or 1, false otherwise
+ public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value is double d && d != 0 && d != 1;
+ }
+
+ ///
+ /// Not implemented
+ ///
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/Converters/ObjectToBoolConverter.cs b/src/LibVLCSharp.MAUI/Converters/ObjectToBoolConverter.cs
new file mode 100644
index 000000000..6bad71bec
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/Converters/ObjectToBoolConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Globalization;
+using Microsoft.Maui.Controls;
+
+namespace LibVLCSharp.MAUI.Converters
+{
+ ///
+ /// Converts not null object to true.
+ ///
+ internal class ObjectToBoolConverter : IValueConverter
+ {
+ ///
+ /// Modifies the source data before passing it to the target for display in the UI.
+ ///
+ /// The source data being passed to the target.
+ /// The type of the target property.
+ /// An optional parameter to be used in the converter logic.
+ /// The culture of the conversion.
+ /// true if value is not null, false otherwise
+ public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value != null;
+ }
+
+ ///
+ /// Not implemented
+ ///
+ public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/Dispatcher.cs b/src/LibVLCSharp.MAUI/Dispatcher.cs
new file mode 100644
index 000000000..eabc330c4
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/Dispatcher.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Threading.Tasks;
+using LibVLCSharp.Shared.MediaPlayerElement;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui;
+
+namespace LibVLCSharp.MAUI
+{
+ ///
+ /// Object that provides services for managing the queue of work items for a thread
+ ///
+ internal class Dispatcher : Shared.MediaPlayerElement.IDispatcher
+ {
+ ///
+ /// Schedules the provided callback on the UI thread from a worker thread
+ ///
+ /// The callback on which the dispatcher returns when the event is dispatched
+ /// The task object representing the asynchronous operation
+ public Task InvokeAsync(Action action)
+ {
+ Application.Current?.Dispatcher.Dispatch(action);
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/DisplayInformation.cs b/src/LibVLCSharp.MAUI/DisplayInformation.cs
new file mode 100644
index 000000000..b1ff2b516
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/DisplayInformation.cs
@@ -0,0 +1,17 @@
+using LibVLCSharp.Shared.MediaPlayerElement;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui;
+
+namespace LibVLCSharp.MAUI
+{
+ ///
+ /// Monitors display-related information for an application view.
+ ///
+ internal class DisplayInformation : IDisplayInformation
+ {
+ ///
+ /// Gets the scale factor
+ ///
+ public double ScalingFactor => DeviceDisplay.MainDisplayInfo.Density;
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/DisplayRequest.cs b/src/LibVLCSharp.MAUI/DisplayRequest.cs
new file mode 100644
index 000000000..720915834
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/DisplayRequest.cs
@@ -0,0 +1,28 @@
+using LibVLCSharp.Shared.MediaPlayerElement;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui;
+
+namespace LibVLCSharp.MAUI
+{
+ ///
+ /// Represents a display request
+ ///
+ internal class DisplayRequest : IDisplayRequest
+ {
+ ///
+ /// Activates a display request
+ ///
+ public void RequestActive()
+ {
+ DeviceDisplay.Current.KeepScreenOn = true;
+ }
+
+ ///
+ /// Deactivates a display request
+ ///
+ public void RequestRelease()
+ {
+ DeviceDisplay.Current.KeepScreenOn = false;
+ }
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/Effects/ClickEffect.cs b/src/LibVLCSharp.MAUI/Effects/ClickEffect.cs
new file mode 100644
index 000000000..5b6642e66
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/Effects/ClickEffect.cs
@@ -0,0 +1,20 @@
+using Microsoft.Maui.Controls;
+
+namespace LibVLCSharp.MAUI.Effects
+{
+ ///
+ /// Click effect.
+ ///
+ internal class ClickEffect : TriggerAction
+ {
+ ///
+ /// Apply a click effect.
+ ///
+ /// The object on which to invoke the trigger action.
+ protected override async void Invoke(VisualElement sender)
+ {
+ await sender.ScaleTo(0.85, 100);
+ await sender.ScaleTo(1, 50);
+ }
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/IOrientationHandler.cs b/src/LibVLCSharp.MAUI/IOrientationHandler.cs
new file mode 100644
index 000000000..2a8e868ec
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/IOrientationHandler.cs
@@ -0,0 +1,18 @@
+namespace LibVLCSharp.MAUI
+{
+ ///
+ /// Force Device Orientation.
+ ///
+ public interface IOrientationHandler
+ {
+ ///
+ /// Lock device's orientation.
+ ///
+ void LockOrientation();
+
+ ///
+ /// Unlock device's orientation.
+ ///
+ void UnLockOrientation();
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/IPowerManager.cs b/src/LibVLCSharp.MAUI/IPowerManager.cs
new file mode 100644
index 000000000..fee934bb7
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/IPowerManager.cs
@@ -0,0 +1,13 @@
+namespace LibVLCSharp.MAUI
+{
+ ///
+ /// Interface for power management.
+ ///
+ public interface IPowerManager
+ {
+ ///
+ /// Gets or sets a value indicating whether the screen should be kept on.
+ ///
+ bool KeepScreenOn { get; set; }
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/ISystemUI.cs b/src/LibVLCSharp.MAUI/ISystemUI.cs
new file mode 100644
index 000000000..07c6e5a5f
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/ISystemUI.cs
@@ -0,0 +1,8 @@
+namespace LibVLCSharp.MAUI
+{
+ internal interface ISystemUI
+ {
+ void ShowSystemUI();
+ void HideSystemUI();
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/LibVLCSharp.MAUI.csproj b/src/LibVLCSharp.MAUI/LibVLCSharp.MAUI.csproj
index d1af6db4b..40905a1ed 100644
--- a/src/LibVLCSharp.MAUI/LibVLCSharp.MAUI.csproj
+++ b/src/LibVLCSharp.MAUI/LibVLCSharp.MAUI.csproj
@@ -16,8 +16,40 @@ LibVLC needs to be installed separately, see VideoLAN.LibVLC.* packages.
+
+
+
+
+
+
+
+
+ %(Filename)
+ Code
+
+
+ %(Filename)
+ Code
+
+
+ %(Filename)
+ Code
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+
diff --git a/src/LibVLCSharp.MAUI/MediaPlayerElement.xaml b/src/LibVLCSharp.MAUI/MediaPlayerElement.xaml
new file mode 100644
index 000000000..1e7ea8494
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/MediaPlayerElement.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/LibVLCSharp.MAUI/MediaPlayerElement.xaml.cs b/src/LibVLCSharp.MAUI/MediaPlayerElement.xaml.cs
new file mode 100644
index 000000000..ca2e5b735
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/MediaPlayerElement.xaml.cs
@@ -0,0 +1,315 @@
+using System;
+using LibVLCSharp.Shared;
+using Microsoft.Maui.Controls.Xaml;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Storage;
+
+namespace LibVLCSharp.MAUI
+{
+ ///
+ /// Represents event data for page-related events.
+ ///
+ public class PageEventArgs : EventArgs
+ {
+ ///
+ /// Gets the associated instance.
+ ///
+ public Page Page { get; }
+
+ ///
+ /// Initializes a new instance of the class with the specified page.
+ ///
+ /// The page associated with the event.
+ public PageEventArgs(Page page)
+ {
+ Page = page;
+ }
+ }
+
+ ///
+ /// Provides helper methods for handling page lifecycle events.
+ ///
+ public static class LifecycleHelper
+ {
+ private static readonly WeakEventManager _pageAppearingEventManager = new();
+ private static readonly WeakEventManager _pageDisappearingEventManager = new();
+
+ ///
+ /// Occurs when a page is appearing.
+ ///
+ public static event EventHandler PageAppearing
+ {
+ add => _pageAppearingEventManager.AddEventHandler(value);
+ remove => _pageAppearingEventManager.RemoveEventHandler(value);
+ }
+
+ ///
+ /// Occurs when a page is disappearing.
+ ///
+ public static event EventHandler PageDisappearing
+ {
+ add => _pageDisappearingEventManager.AddEventHandler(value);
+ remove => _pageDisappearingEventManager.RemoveEventHandler(value);
+ }
+
+ ///
+ /// Registers the page lifecycle events to track page appearing and disappearing.
+ ///
+ public static void RegisterPageLifecycleEvents()
+ {
+ if (Application.Current != null)
+ {
+ Application.Current.PageAppearing += (s, e) => _pageAppearingEventManager.HandleEvent(s ?? Application.Current, new PageEventArgs(e), nameof(PageAppearing));
+ Application.Current.PageDisappearing += (s, e) => _pageDisappearingEventManager.HandleEvent(s ?? Application.Current, new PageEventArgs(e), nameof(PageDisappearing));
+ }
+ }
+ }
+
+ ///
+ /// Represents an object that uses a to render audio and video to the display.
+ ///
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class MediaPlayerElement : ContentView
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MediaPlayerElement()
+ {
+ InitializeComponent();
+ LifecycleHelper.RegisterPageLifecycleEvents();
+ }
+
+ private bool Initialized { get; set; }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty LibVLCProperty = BindableProperty.Create(nameof(LibVLC), typeof(LibVLC),
+ typeof(MediaPlayerElement), propertyChanged: LibVLCPropertyChanged);
+
+ ///
+ /// Gets the instance.
+ ///
+ public LibVLC LibVLC
+ {
+ get => (LibVLC)GetValue(LibVLCProperty);
+ set => SetValue(LibVLCProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty MediaPlayerProperty = BindableProperty.Create(nameof(MediaPlayer),
+ typeof(LibVLCSharp.Shared.MediaPlayer), typeof(MediaPlayerElement), propertyChanged: MediaPlayerPropertyChanged);
+
+ ///
+ /// Gets the instance.
+ ///
+ public LibVLCSharp.Shared.MediaPlayer MediaPlayer
+ {
+ get => (LibVLCSharp.Shared.MediaPlayer)GetValue(MediaPlayerProperty);
+ set => SetValue(MediaPlayerProperty, value);
+ }
+
+ private static readonly BindableProperty PlaybackControlsProperty = BindableProperty.Create(nameof(PlaybackControls),
+ typeof(PlaybackControls), typeof(MediaPlayerElement), propertyChanged: PlaybackControlsPropertyChanged);
+
+ ///
+ /// Gets or sets the playback controls for the media.
+ ///
+ public PlaybackControls PlaybackControls
+ {
+ get => (PlaybackControls)GetValue(PlaybackControlsProperty);
+ set => SetValue(PlaybackControlsProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ private static readonly BindableProperty VideoViewProperty = BindableProperty.Create(nameof(VideoView), typeof(VideoView),
+ typeof(MediaPlayerElement), propertyChanged: VideoViewPropertyChanged);
+
+ ///
+ /// Gets or sets the video view.
+ ///
+ public VideoView? VideoView
+ {
+ get => (VideoView)GetValue(VideoViewProperty);
+ private set => SetValue(VideoViewProperty, value);
+ }
+
+ private static readonly BindableProperty EnableRendererDiscoveryProperty = BindableProperty.Create(nameof(EnableRendererDiscovery),
+ typeof(bool), typeof(PlaybackControls), true, propertyChanged: EnableRendererDiscoveryPropertyChanged);
+
+ ///
+ /// Enable or disable renderer discovery.
+ ///
+ public bool EnableRendererDiscovery
+ {
+ get => (bool)GetValue(EnableRendererDiscoveryProperty);
+ set => SetValue(EnableRendererDiscoveryProperty, value);
+ }
+
+ private void OnVideoViewChanged(VideoView videoView)
+ {
+ if (videoView != null)
+ {
+ videoView.MediaPlayer = MediaPlayer;
+ var playbackControls = PlaybackControls;
+ if (playbackControls != null)
+ {
+ playbackControls.VideoView = videoView;
+ }
+ }
+ }
+
+ private void OnLibVLCChanged(LibVLC libVLC)
+ {
+ var playbackControls = PlaybackControls;
+ if (playbackControls != null)
+ {
+ playbackControls.LibVLC = libVLC;
+ }
+ }
+
+ private void OnMediaPlayerChanged(LibVLCSharp.Shared.MediaPlayer mediaPlayer)
+ {
+ var videoView = VideoView;
+ if (videoView != null)
+ {
+ videoView.MediaPlayer = mediaPlayer;
+ }
+ var playbackControls = PlaybackControls;
+ if (playbackControls != null)
+ {
+ playbackControls.MediaPlayer = mediaPlayer;
+ }
+ }
+
+ private void OnPlayControlsChanged(PlaybackControls playbackControls)
+ {
+ if (playbackControls != null)
+ {
+ playbackControls.IsCastButtonVisible = EnableRendererDiscovery;
+ playbackControls.LibVLC = LibVLC;
+ playbackControls.MediaPlayer = MediaPlayer;
+ playbackControls.VideoView = VideoView;
+ }
+ }
+
+ private void OnEnableRendererDiscoveryChanged(bool enableRendererDiscovery)
+ {
+ var playbackControls = PlaybackControls;
+ if (playbackControls != null)
+ {
+ playbackControls.IsCastButtonVisible = enableRendererDiscovery;
+ }
+ }
+
+ private static void VideoViewPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ ((MediaPlayerElement)bindable).OnVideoViewChanged((VideoView)newValue);
+ }
+
+ private static void LibVLCPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ ((MediaPlayerElement)bindable).OnLibVLCChanged((LibVLC)newValue);
+ }
+
+ private static void MediaPlayerPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ ((MediaPlayerElement)bindable).OnMediaPlayerChanged((LibVLCSharp.Shared.MediaPlayer)newValue);
+ }
+
+ private static void PlaybackControlsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ ((MediaPlayerElement)bindable).OnPlayControlsChanged((PlaybackControls)newValue);
+ }
+
+ private static void EnableRendererDiscoveryPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ ((MediaPlayerElement)bindable).OnEnableRendererDiscoveryChanged((bool)newValue);
+ }
+
+ ///
+ /// Invoked whenever the of an element is set.
+ /// Implement this method in order to add behavior when the element is added to a parent.
+ ///
+ /// Implementors must call the base method.
+ protected override void OnParentSet()
+ {
+ base.OnParentSet();
+
+ if (Parent != null && !Initialized)
+ {
+ Initialized = true;
+
+ if (VideoView == null)
+ {
+ VideoView = new VideoView();
+ }
+
+ if (PlaybackControls == null)
+ {
+ PlaybackControls = new PlaybackControls();
+ }
+
+ AttachLifecycleEvents();
+ }
+ }
+
+ private void AttachLifecycleEvents()
+ {
+ LifecycleHelper.PageAppearing += OnPageAppearing;
+ LifecycleHelper.PageDisappearing += OnPageDisappearing;
+ }
+
+ ///
+ /// Handle page appearing logic
+ ///
+ ///
+ ///
+ private void OnPageAppearing(object? sender, EventArgs e)
+ {
+ if (sender is Page page && page == this.FindAncestor())
+ {
+ var mediaPlayer = MediaPlayer;
+ if (mediaPlayer != null)
+ {
+ Preferences.Set($"VLC_{mediaPlayer.NativeReference}_MediaPlayerElement_Position", mediaPlayer.Position);
+ Preferences.Set($"VLC_{mediaPlayer.NativeReference}_MediaPlayerElement_IsPlaying", mediaPlayer.State == VLCState.Playing);
+ mediaPlayer.Stop();
+ }
+ VideoView = null;
+ }
+ }
+
+ ///
+ /// Handle page disappearing logic
+ /// minimize change.
+ ///
+ ///
+ private void OnPageDisappearing(object? sender, EventArgs e)
+ {
+ if (sender is Page page && page == this.FindAncestor())
+ {
+ VideoView = new VideoView();
+ var mediaPlayer = MediaPlayer;
+ if (mediaPlayer != null)
+ {
+ if (Preferences.Get($"VLC_{mediaPlayer.NativeReference}_MediaPlayerElement_IsPlaying", false))
+ {
+ mediaPlayer.Play();
+ mediaPlayer.Position = Preferences.Get($"VLC_{mediaPlayer.NativeReference}_MediaPlayerElement_Position", 0f);
+ }
+ }
+ }
+ }
+
+ private void GestureRecognized(object sender, EventArgs e)
+ {
+ PlaybackControls.Show();
+ }
+ }
+}
diff --git a/src/LibVLCSharp.MAUI/PlaybackControls.xaml b/src/LibVLCSharp.MAUI/PlaybackControls.xaml
new file mode 100644
index 000000000..fd279fb27
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/PlaybackControls.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/src/LibVLCSharp.MAUI/PlaybackControls.xaml.cs b/src/LibVLCSharp.MAUI/PlaybackControls.xaml.cs
new file mode 100644
index 000000000..8cceda26c
--- /dev/null
+++ b/src/LibVLCSharp.MAUI/PlaybackControls.xaml.cs
@@ -0,0 +1,1363 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Resources;
+using System.Threading.Tasks;
+using LibVLCSharp.MAUI.Resources;
+using LibVLCSharp.Shared;
+using LibVLCSharp.Shared.MediaPlayerElement;
+using Microsoft.Maui.Controls.Xaml;
+using Microsoft.Maui.Graphics;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui;
+
+namespace LibVLCSharp.MAUI
+{
+ ///
+ /// Represents the playback controls for a .
+ ///
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class PlaybackControls : TemplatedView
+ {
+ private const string AudioSelectionAvailableState = "AudioSelectionAvailable";
+ private const string VideoSelectionAvailableState = "VideoSelectionAvailable";
+ private const string AudioSelectionUnavailableState = "AudioSelectionUnavailable";
+ private const string VideoSelectionUnavailableState = "videoSelectionUnavailable";
+ private const string ClosedCaptionsSelectionAvailableState = "ClosedCaptionsSelectionAvailable";
+ private const string ClosedCaptionsSelectionUnavailableState = "ClosedCaptionsSelectionUnavailable";
+ private const string PlayState = "PlayState";
+ private const string PauseState = "PauseState";
+ private const string PauseAvailableState = "PauseAvailable";
+ private const string PauseUnavailableState = "PauseUnavailable";
+ private const string SeekAvailableState = "SeekAvailable";
+ private const string SeekUnavailableState = "SeekUnavailable";
+ private const string CastAvailableState = "CastAvailable";
+ private const string CastUnavailableState = "CastUnavailable";
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public PlaybackControls()
+ {
+ InitializeComponent();
+
+ try
+ {
+ ButtonColor = (Color)(Resources[nameof(ButtonColor)] ?? Colors.Transparent);
+ Foreground = (Color)(Resources[nameof(Foreground)] ?? Colors.White);
+ MainColor = (Color)(Resources[nameof(MainColor)] ?? Colors.Transparent);
+ TracksButtonStyle = Resources[nameof(TracksButtonStyle)] as Style;
+ BufferingProgressBarStyle = Resources[nameof(BufferingProgressBarStyle)] as Style;
+ ButtonBarStyle = Resources[nameof(ButtonBarStyle)] as Style;
+ CastButtonStyle = Resources[nameof(CastButtonStyle)] as Style;
+ ControlsPanelStyle = Resources[nameof(ControlsPanelStyle)] as Style;
+ MessageStyle = Resources[nameof(MessageStyle)] as Style;
+ PlayPauseButtonStyle = Resources[nameof(PlayPauseButtonStyle)] as Style;
+ RemainingTimeLabelStyle = Resources[nameof(RemainingTimeLabelStyle)] as Style;
+ ElapsedTimeLabelStyle = Resources[nameof(ElapsedTimeLabelStyle)] as Style;
+ SeekBarStyle = Resources[nameof(SeekBarStyle)] as Style;
+ StopButtonStyle = Resources[nameof(StopButtonStyle)] as Style;
+ AspectRatioButtonStyle = Resources[nameof(AspectRatioButtonStyle)] as Style;
+ RewindButtonStyle = Resources[nameof(RewindButtonStyle)] as Style;
+ SeekButtonStyle = Resources[nameof(SeekButtonStyle)] as Style;
+ LockButtonStyle = Resources[nameof(LockButtonStyle)] as Style;
+ UnLockButtonStyle = Resources[nameof(UnLockButtonStyle)] as Style;
+ UnLockControlsPanelStyle = Resources[nameof(UnLockControlsPanelStyle)] as Style;
+
+ Manager = new MediaPlayerElementManager(new Dispatcher(), new DisplayInformation(), new DisplayRequest());
+ var autoHideManager = Manager.Get();
+ autoHideManager.Shown += async (sender, e) => await FadeInAsync();
+ autoHideManager.Hidden += async (sender, e) => await FadeOutAsync();
+ autoHideManager.Enabled = ShowAndHideAutomatically;
+ var audioTrackManager = Manager.Get();
+ audioTrackManager.TrackAdded += OnAudioTracksChanged;
+ audioTrackManager.TrackDeleted += OnAudioTracksChanged;
+ audioTrackManager.TracksCleared += OnAudioTracksChanged;
+ var videoTrackManager = Manager.Get();
+ videoTrackManager.TrackAdded += OnVideoTracksChanged;
+ videoTrackManager.TrackDeleted += OnVideoTracksChanged;
+ videoTrackManager.TracksCleared += OnVideoTracksChanged;
+ var subTitlesTrackManager = Manager.Get();
+ subTitlesTrackManager.TrackAdded += OnSubtitlesTracksChanged;
+ subTitlesTrackManager.TrackDeleted += OnSubtitlesTracksChanged;
+ subTitlesTrackManager.TracksCleared += OnSubtitlesTracksChanged;
+ var castRenderersDiscoverer = Manager.Get();
+ castRenderersDiscoverer.CastAvailableChanged += (sender, e) => UpdateCastAvailability();
+ castRenderersDiscoverer.Enabled = IsCastButtonVisible;
+ var seekBarManager = Manager.Get();
+ seekBarManager.PositionChanged += SeekBarManager_PositionChanged;
+ seekBarManager.SeekableChanged += (sender, e) => UpdateSeekAvailability();
+ var bufferingProgressNotifier = Manager.Get();
+ bufferingProgressNotifier.Buffering += (sender, e) => OnBuffering();
+ var stateManager = Manager.Get();
+ stateManager.ErrorOccured += (sender, e) => ShowError();
+ stateManager.ErrorCleared += (sender, e) => ErrorMessage = null;
+ stateManager.Playing += (sender, e) => OnPlaying();
+ stateManager.Paused += (sender, e) => OnStoppedOrPaused();
+ stateManager.Stopped += (sender, e) => OnStoppedOrPaused();
+ stateManager.PlayPauseAvailableChanged += (sender, e) => UpdatePauseAvailability();
+ }
+ catch (Exception ex)
+ {
+ ShowErrorMessageBox(ex);
+ }
+ }
+
+ private void SeekBarManager_PositionChanged(object? sender, EventArgs e)
+ {
+ if (!Manager.Get().IsDragging)
+ {
+ UpdateTime();
+ }
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ~PlaybackControls()
+ {
+ Manager.Dispose();
+ }
+
+ private MediaPlayerElementManager Manager { get; } = default!;
+ private Button? TracksButton { get; set; }
+ private Button? CastButton { get; set; }
+ private VisualElement? ControlsPanel { get; set; }
+ private VisualElement? ButtonBar { get; set; }
+ private VisualElement? UnLockControlsPanel { get; set; }
+ private VisualElement? TracksOverlayView { get; set; }
+ private SwipeToUnLockView? SwipeToUnLock { get; set; }
+ private Label? TrackBarLabel { get; set; }
+ private Label? AudioTracksLabel { get; set; }
+ private Label? VideoTracksLabel { get; set; }
+ private Label? SubtileTracksLabel { get; set; }
+ private Button? PlayPauseButton { get; set; }
+ private Label? RemainingTimeLabel { get; set; }
+ private Label? ElapsedTimeLabel { get; set; }
+ private Label? AspectRatioLabel { get; set; }
+
+ private Slider? SeekBar { get; set; }
+ private ListView? AudioTracksListView { get; set; }
+ private ListView? VideoTracksListView { get; set; }
+ private ListView? SubtitlesTracksListView { get; set; }
+ private bool ScreenLockModeEnable { get; set; } = false;
+
+ private bool Initialized { get; set; }
+ private ISystemUI? SystemUI => DependencyService.Get();
+ private IOrientationHandler? OrientationHandler => DependencyService.Get();
+
+ private const int SEEK_OFFSET = 2000;
+ private bool RemoteRendering { get; set; } = false;
+ private const string Disconnect = "Disconnect";
+ private const string Cancel = "Cancel";
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ButtonColorProperty = BindableProperty.Create(nameof(ButtonColor), typeof(Color),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the button color.
+ ///
+ public Color ButtonColor
+ {
+ get => (Color)GetValue(ButtonColorProperty);
+ set => SetValue(ButtonColorProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ForegroundProperty = BindableProperty.Create(nameof(Foreground), typeof(Color),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the button color.
+ ///
+ public Color Foreground
+ {
+ get => (Color)GetValue(ForegroundProperty);
+ set => SetValue(ForegroundProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty MainColorProperty = BindableProperty.Create(nameof(MainColor), typeof(Color),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the main color.
+ ///
+ public Color MainColor
+ {
+ get => (Color)GetValue(MainColorProperty);
+ set => SetValue(MainColorProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty TracksButtonStyleProperty = BindableProperty.Create(
+ nameof(TracksButtonStyle), typeof(Style), typeof(PlaybackControls));
+ ///
+ /// Gets or sets the tracks button style.
+ ///
+ public Style? TracksButtonStyle
+ {
+ get => (Style)GetValue(TracksButtonStyleProperty);
+ set => SetValue(TracksButtonStyleProperty, value);
+ }
+
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty BufferingProgressBarStyleProperty = BindableProperty.Create(nameof(BufferingProgressBarStyle),
+ typeof(Style), typeof(PlaybackControls));
+ ///
+ /// Gets or sets the controls panel style.
+ ///
+ public Style? BufferingProgressBarStyle
+ {
+ get => (Style)GetValue(BufferingProgressBarStyleProperty);
+ set => SetValue(BufferingProgressBarStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ButtonBarStyleProperty = BindableProperty.Create(nameof(ButtonBarStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the button bar style.
+ ///
+ public Style? ButtonBarStyle
+ {
+ get => (Style)GetValue(ButtonBarStyleProperty);
+ set => SetValue(ButtonBarStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty CastButtonStyleProperty = BindableProperty.Create(nameof(CastButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the cast button style.
+ ///
+ public Style? CastButtonStyle
+ {
+ get => (Style)GetValue(CastButtonStyleProperty);
+ set => SetValue(CastButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ControlsPanelStyleProperty = BindableProperty.Create(nameof(ControlsPanelStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the controls panel style.
+ ///
+ public Style? ControlsPanelStyle
+ {
+ get => (Style)GetValue(ControlsPanelStyleProperty);
+ set => SetValue(ControlsPanelStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty UnLockControlsPanelStyleProperty = BindableProperty.Create(nameof(UnLockControlsPanelStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the unlock controls panel style.
+ ///
+ public Style? UnLockControlsPanelStyle
+ {
+ get => (Style)GetValue(UnLockControlsPanelStyleProperty);
+ set => SetValue(UnLockControlsPanelStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty UnLockButtonStyleProperty = BindableProperty.Create(nameof(UnLockButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the unlock controls panel style.
+ ///
+ public Style? UnLockButtonStyle
+ {
+ get => (Style)GetValue(UnLockButtonStyleProperty);
+ set => SetValue(UnLockButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty MessageStyleProperty = BindableProperty.Create(nameof(MessageStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the message style.
+ ///
+ public Style? MessageStyle
+ {
+ get => (Style)GetValue(MessageStyleProperty);
+ set => SetValue(MessageStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty PlayPauseButtonStyleProperty = BindableProperty.Create(nameof(PlayPauseButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the play/pause button style.
+ ///
+ public Style? PlayPauseButtonStyle
+ {
+ get => (Style)GetValue(PlayPauseButtonStyleProperty);
+ set => SetValue(PlayPauseButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty RemainingTimeLabelStyleProperty = BindableProperty.Create(nameof(RemainingTimeLabelStyle),
+ typeof(Style), typeof(PlaybackControls));
+ ///
+ /// Gets or sets the remaining time label style.
+ ///
+ public Style? RemainingTimeLabelStyle
+ {
+ get => (Style)GetValue(RemainingTimeLabelStyleProperty);
+ set => SetValue(RemainingTimeLabelStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ElapsedTimeLabelStyleProperty = BindableProperty.Create(nameof(ElapsedTimeLabelStyle),
+ typeof(Style), typeof(PlaybackControls));
+ ///
+ /// Gets or sets the elapsed time label style.
+ ///
+ public Style? ElapsedTimeLabelStyle
+ {
+ get => (Style)GetValue(ElapsedTimeLabelStyleProperty);
+ set => SetValue(ElapsedTimeLabelStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty SeekBarStyleProperty = BindableProperty.Create(nameof(SeekBarStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the seek bar style.
+ ///
+ public Style? SeekBarStyle
+ {
+ get => (Style)GetValue(SeekBarStyleProperty);
+ set => SetValue(SeekBarStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty StopButtonStyleProperty = BindableProperty.Create(nameof(StopButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the stop button style.
+ ///
+ public Style? StopButtonStyle
+ {
+ get => (Style)GetValue(StopButtonStyleProperty);
+ set => SetValue(StopButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty VideoViewProperty = BindableProperty.Create(nameof(VideoView), typeof(VideoView),
+ typeof(PlaybackControls),
+ propertyChanged: (bindable, oldValue, newValue) => ((PlaybackControls)bindable).Manager.VideoView = (IVideoControl)newValue);
+
+ ///
+ /// Gets or sets the associated .
+ ///
+ /// It is only useful to set this property for the aspect ratio feature.
+ public VideoView? VideoView
+ {
+ get => (VideoView)GetValue(VideoViewProperty);
+ set => SetValue(VideoViewProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty LockButtonStyleProperty = BindableProperty.Create(nameof(LockButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the Lock button style.
+ ///
+ public Style? LockButtonStyle
+ {
+ get => (Style)GetValue(LockButtonStyleProperty);
+ set => SetValue(LockButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty AspectRatioButtonStyleProperty = BindableProperty.Create(nameof(AspectRatioButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the aspect ratio button style.
+ ///
+ public Style? AspectRatioButtonStyle
+ {
+ get => (Style)GetValue(AspectRatioButtonStyleProperty);
+ set => SetValue(AspectRatioButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty RewindButtonStyleProperty = BindableProperty.Create(nameof(RewindButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the rewind button style.
+ ///
+ public Style? RewindButtonStyle
+ {
+ get => (Style)GetValue(RewindButtonStyleProperty);
+ set => SetValue(RewindButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty SeekButtonStyleProperty = BindableProperty.Create(nameof(SeekButtonStyle), typeof(Style),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the rewind button style.
+ ///
+ public Style? SeekButtonStyle
+ {
+ get => (Style)GetValue(SeekButtonStyleProperty);
+ set => SetValue(SeekButtonStyleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ButtonBarStartAreaProperty = BindableProperty.Create(nameof(ButtonBarStartArea), typeof(View),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the view in the button bar start area.
+ ///
+ public View ButtonBarStartArea
+ {
+ get => (View)GetValue(ButtonBarStartAreaProperty);
+ set => SetValue(ButtonBarStartAreaProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ButtonBarEndAreaProperty = BindableProperty.Create(nameof(ButtonBarEndArea), typeof(View),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the view in the button bar end area.
+ ///
+ public View ButtonBarEndArea
+ {
+ get => (View)GetValue(ButtonBarEndAreaProperty);
+ set => SetValue(ButtonBarEndAreaProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty LibVLCProperty = BindableProperty.Create(nameof(LibVLC), typeof(LibVLC), typeof(PlaybackControls),
+ propertyChanged: LibVLCPropertyChanged);
+ ///
+ /// Gets or sets the instance.
+ ///
+ public LibVLC LibVLC
+ {
+ get => (LibVLC)GetValue(LibVLCProperty);
+ set => SetValue(LibVLCProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty MediaPlayerProperty = BindableProperty.Create(nameof(MediaPlayer),
+ typeof(LibVLCSharp.Shared.MediaPlayer), typeof(PlaybackControls), propertyChanged: MediaPlayerPropertyChanged);
+ ///
+ /// Gets or sets the instance.
+ ///
+ public LibVLCSharp.Shared.MediaPlayer MediaPlayer
+ {
+ get => (LibVLCSharp.Shared.MediaPlayer)GetValue(MediaPlayerProperty);
+ set => SetValue(MediaPlayerProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty BufferingProgressProperty = BindableProperty.Create(nameof(BufferingProgress), typeof(double),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets a value corresponding to the buffering progress.
+ ///
+ public double BufferingProgress
+ {
+ get => (double)GetValue(BufferingProgressProperty);
+ set => SetValue(BufferingProgressProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ErrorMessageProperty = BindableProperty.Create(nameof(ErrorMessage), typeof(string),
+ typeof(PlaybackControls));
+ ///
+ /// Gets the last error message.
+ ///
+ public string? ErrorMessage
+ {
+ get => (string)GetValue(ErrorMessageProperty);
+ private set => SetValue(ErrorMessageProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty KeepScreenOnProperty = BindableProperty.Create(nameof(KeepScreenOn), typeof(bool),
+ typeof(PlaybackControls), true, propertyChanged: KeepScreenOnPropertyChangedAsync);
+ ///
+ /// Gets or sets a value indicating whether the screen must be kept on when playing.
+ ///
+ public bool KeepScreenOn
+ {
+ get => (bool)GetValue(KeepScreenOnProperty);
+ set => SetValue(KeepScreenOnProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty PositionProperty = BindableProperty.Create(nameof(Position), typeof(TimeSpan),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the playback position within the media.
+ ///
+ public TimeSpan Position
+ {
+ get => (TimeSpan)GetValue(PositionProperty);
+ set => SetValue(PositionProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ResourceManagerProperty = BindableProperty.Create(nameof(ResourceManager), typeof(ResourceManager),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets the resource manager to localize strings.
+ ///
+ public ResourceManager ResourceManager
+ {
+ get => (ResourceManager)GetValue(ResourceManagerProperty) ?? Strings.ResourceManager;
+ set => SetValue(ResourceManagerProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty ShowAndHideAutomaticallyProperty = BindableProperty.Create(nameof(ShowAndHideAutomatically),
+ typeof(bool), typeof(PlaybackControls), true,
+ propertyChanged: (bindable, oldValue, newValue) => ((PlaybackControls)bindable).OnShowAndHideAutomaticallyPropertyChanged());
+ ///
+ /// Gets or sets a value that indicates whether the controls are shown and hidden automatically.
+ ///
+ public bool ShowAndHideAutomatically
+ {
+ get => (bool)GetValue(ShowAndHideAutomaticallyProperty);
+ set => SetValue(ShowAndHideAutomaticallyProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsLockButtonVisibleProperty = BindableProperty.Create(nameof(IsLockButtonVisible), typeof(bool),
+ typeof(PlaybackControls), true);
+ ///
+ /// Gets or sets a value that indicates whether the lock button is shown.
+ ///
+ public bool IsLockButtonVisible
+ {
+ get => (bool)GetValue(IsLockButtonVisibleProperty);
+ set => SetValue(IsLockButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsTracksButtonVisibleProperty = BindableProperty.Create(nameof(IsTracksButtonVisible), typeof(bool),
+ typeof(PlaybackControls), true);
+ ///
+ /// Gets or sets a value that indicates whether the tracks button is shown.
+ ///
+ public bool IsTracksButtonVisible
+ {
+ get => (bool)GetValue(IsTracksButtonVisibleProperty);
+ set => SetValue(IsTracksButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsCastButtonVisibleProperty = BindableProperty.Create(nameof(IsCastButtonVisible), typeof(bool),
+ typeof(PlaybackControls), true, propertyChanged: IsCastButtonVisiblePropertyChangedAsync);
+ ///
+ /// Gets or sets a value indicating whether the cast button is shown.
+ ///
+ public bool IsCastButtonVisible
+ {
+ get => (bool)GetValue(IsCastButtonVisibleProperty);
+ set => SetValue(IsCastButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsPlayPauseButtonVisibleProperty = BindableProperty.Create(nameof(IsPlayPauseButtonVisible),
+ typeof(bool), typeof(PlaybackControls), true, propertyChanged: IsPlayPauseButtonVisiblePropertyChanged);
+ ///
+ /// Gets or sets a value indicating whether the play/pause button is shown.
+ ///
+ public bool IsPlayPauseButtonVisible
+ {
+ get => (bool)GetValue(IsPlayPauseButtonVisibleProperty);
+ set => SetValue(IsPlayPauseButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsSeekEnabledProperty = BindableProperty.Create(nameof(IsSeekEnabled), typeof(bool),
+ typeof(PlaybackControls), true);
+ ///
+ /// Gets or sets a value that indicates whether a user can use the seek bar to find a location in the media.
+ ///
+ public bool IsSeekEnabled
+ {
+ get => (bool)GetValue(IsSeekEnabledProperty);
+ set => SetValue(IsSeekEnabledProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsSeekBarVisibleProperty = BindableProperty.Create(nameof(IsSeekBarVisible), typeof(bool),
+ typeof(PlaybackControls), true);
+ ///
+ /// Gets or sets a value that indicates whether the seek bar is shown.
+ ///
+ public bool IsSeekBarVisible
+ {
+ get => (bool)GetValue(IsSeekBarVisibleProperty);
+ set => SetValue(IsSeekBarVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsStopButtonVisibleProperty = BindableProperty.Create(nameof(IsStopButtonVisible), typeof(bool),
+ typeof(PlaybackControls));
+ ///
+ /// Gets or sets a value that indicates whether the stop button is shown.
+ ///
+ public bool IsStopButtonVisible
+ {
+ get => (bool)GetValue(IsStopButtonVisibleProperty);
+ set => SetValue(IsStopButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsAspectRatioButtonVisibleProperty = BindableProperty.Create(nameof(IsAspectRatioButtonVisible),
+ typeof(bool), typeof(PlaybackControls), true);
+ ///
+ /// Gets or sets a value indicating whether the aspect ratio button is shown.
+ ///
+ public bool IsAspectRatioButtonVisible
+ {
+ get => (bool)GetValue(IsAspectRatioButtonVisibleProperty);
+ set => SetValue(IsAspectRatioButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ /// s
+ public static readonly BindableProperty IsRewindButtonVisibleProperty = BindableProperty.Create(nameof(IsRewindButtonVisible),
+ typeof(bool), typeof(PlaybackControls), true);
+ ///
+ /// Gets or sets a value indicating whether the rewind button is shown.
+ ///
+ public bool IsRewindButtonVisible
+ {
+ get => (bool)GetValue(IsRewindButtonVisibleProperty);
+ set => SetValue(IsRewindButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly BindableProperty IsSeekButtonVisibleProperty = BindableProperty.Create(nameof(IsSeekButtonVisible),
+ typeof(bool), typeof(PlaybackControls), true);
+ ///
+ /// Gets or sets a value indicating whether the seek button is shown.
+ ///
+ public bool IsSeekButtonVisible
+ {
+ get => (bool)GetValue(IsSeekButtonVisibleProperty);
+ set => SetValue(IsSeekButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Called when the property has changed.
+ ///
+ protected override void OnParentSet()
+ {
+ base.OnParentSet();
+ if (Parent != null && !Initialized)
+ {
+ Initialized = true;
+ OnApplyCustomTemplate();
+ }
+ }
+
+ private void OnApplyCustomTemplate()
+ {
+ TracksButton = SetClickEventHandler(nameof(TracksButton), TracksButton_Clicked);
+ CastButton = SetClickEventHandler(nameof(CastButton), CastButton_ClickedAsync);
+ PlayPauseButton = SetClickEventHandler(nameof(PlayPauseButton), PlayPauseButton_Clicked);
+ SetClickEventHandler("StopButton", StopButton_Clicked);
+ SetClickEventHandler("LockButton", LockButton_ClickedAsync);
+ SetClickEventHandler("AspectRatioButton", AspectRatioButton_ClickedAsync);
+ ControlsPanel = this.FindChild(nameof(ControlsPanel));
+ ButtonBar = this.FindChild(nameof(ButtonBar));
+ UnLockControlsPanel = this.FindChild(nameof(UnLockControlsPanel));
+ TracksOverlayView = this.FindChild(nameof(TracksOverlayView));
+ SwipeToUnLock = this.FindChild(nameof(SwipeToUnLock));
+ SeekBar = this.FindChild(nameof(SeekBar));
+ AudioTracksListView = this.FindChild(nameof(AudioTracksListView));
+ if (AudioTracksListView != null)
+ AudioTracksListView.ItemTapped += AudioTracksItemTapped;
+ VideoTracksListView = this.FindChild(nameof(VideoTracksListView));
+ if (VideoTracksListView != null)
+ VideoTracksListView.ItemTapped += VideoTracksItemTapped;
+ SubtitlesTracksListView = this.FindChild(nameof(SubtitlesTracksListView));
+ if (SubtitlesTracksListView != null)
+ SubtitlesTracksListView.ItemTapped += SubtitlesTracksItemTapped;
+ TrackBarLabel = this.FindChild