Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Commit 650031a

Browse files
JoonghyunChoAndreiMisiukevichTheCodeTraveler
authored
[Tizen] Added MediaElement implementation (#1140)
* [Tizen] Added MediaElement implementation * Resolve reviews and update EmbeddingControl * Update src/CommunityToolkit/Xamarin.CommunityToolkit/Views/MediaElement/Tizen/EmbeddingControls.tizen.cs * Update src/CommunityToolkit/Xamarin.CommunityToolkit/Views/MediaElement/Tizen/EmbeddingControls.tizen.cs * Resolve reviews * Renamed MediaPlayerImplementation, Added `readonly` modifiers where applicable, Fix formatting * Implement Nullable * Implement Nullable * Add readonly modifier where applicable, update formatting * Update IntToTimeSpanConverter.tizen.cs * Remove discarding Task * Implement Task, ValueTask & async/await * Update Exceptions * Update AspectToDisplayAspectModeConverter.tizen.cs * Update Seek() Co-authored-by: Andrei <[email protected]> Co-authored-by: Brandon Minnick <[email protected]>
1 parent 053f2fb commit 650031a

15 files changed

+1727
-1
lines changed

samples/XCT.Sample.Tizen/Main.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ static void Main(string[] args)
1515
{
1616
var app = new Program();
1717
global::Xamarin.Forms.Forms.SetFlags("CollectionView_Experimental");
18-
global::Xamarin.Forms.Forms.Init(app, true);
18+
var option = new InitializationOptions(app)
19+
{
20+
UseDeviceIndependentPixel = true,
21+
UseSkiaSharp = true
22+
};
23+
global::Xamarin.Forms.Forms.Init(option);
1924
app.Run(args);
2025
}
2126
}
Loading
7.07 KB
Loading
3.22 KB
Loading
1.09 KB
Loading
1.92 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Globalization;
3+
using Xamarin.Forms;
4+
5+
namespace Xamarin.CommunityToolkit.UI.Views
6+
{
7+
public class AspectToDisplayAspectModeConverter : IValueConverter
8+
{
9+
public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
10+
{
11+
if (value == null)
12+
throw new ArgumentNullException(nameof(value));
13+
14+
return ((Aspect)value).ToDisplayAspectMode();
15+
}
16+
17+
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
18+
}
19+
}
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Threading.Tasks;
4+
using Xamarin.CommunityToolkit.ObjectModel;
5+
using Xamarin.Forms;
6+
using ShapesPath = Xamarin.Forms.Shapes.Path;
7+
using ShapesPathGeometry = Xamarin.Forms.Shapes.PathGeometry;
8+
using XLabel = Xamarin.Forms.Label;
9+
using XTextAlignment = Xamarin.Forms.TextAlignment;
10+
11+
namespace Xamarin.CommunityToolkit.UI.Views
12+
{
13+
public class EmbeddingControls : ContentView
14+
{
15+
public Grid PlayIcon { get; private set; }
16+
17+
public Grid PauseIcon { get; private set; }
18+
19+
public EmbeddingControls()
20+
{
21+
var iconTapCommand = new AsyncValueCommand(async () =>
22+
{
23+
if (BindingContext is not MediaPlayer player)
24+
throw new InvalidOperationException($"{nameof(BindingContext)} must be {nameof(MediaPlayer)}");
25+
26+
if (player.State == PlaybackState.Playing)
27+
{
28+
player.Pause();
29+
}
30+
else
31+
{
32+
await player.Start();
33+
}
34+
});
35+
36+
PlayIcon = new Grid
37+
{
38+
Children =
39+
{
40+
new ShapesPath
41+
{
42+
Scale = 0.7,
43+
Data = (ShapesPathGeometry)new Forms.Shapes.PathGeometryConverter().ConvertFromInvariantString("M93.5 52.4019C95.5 53.5566 95.5 56.4434 93.5 57.5981L5 108.694C3 109.848 0.499996 108.405 0.499996 106.096V3.9045C0.499996 1.5951 3 0.151723 5 1.30642L93.5 52.4019Z"),
44+
Fill = Brush.White,
45+
Opacity = 0.4,
46+
Aspect = Stretch.Uniform,
47+
HorizontalOptions = LayoutOptions.Center
48+
}
49+
}
50+
};
51+
52+
PlayIcon.GestureRecognizers.Add(new TapGestureRecognizer()
53+
{
54+
Command = iconTapCommand
55+
});
56+
AbsoluteLayout.SetLayoutFlags(PlayIcon, AbsoluteLayoutFlags.All);
57+
AbsoluteLayout.SetLayoutBounds(PlayIcon, new Rectangle(0.5, 0.5, 0.25, 0.25));
58+
59+
PauseIcon = new Grid
60+
{
61+
HorizontalOptions = LayoutOptions.Center,
62+
Children =
63+
{
64+
new ShapesPath
65+
{
66+
Scale = 0.7,
67+
Data = (ShapesPathGeometry)new Forms.Shapes.PathGeometryConverter().ConvertFromInvariantString("M1 1H36V131H1V1Z"),
68+
Fill = Brush.White,
69+
Opacity = 0.4,
70+
Aspect = Stretch.Uniform,
71+
HorizontalOptions = LayoutOptions.Start
72+
},
73+
new ShapesPath
74+
{
75+
Scale = 0.7,
76+
Data = (ShapesPathGeometry)new Forms.Shapes.PathGeometryConverter().ConvertFromInvariantString("M71 1H106V131H71V1Z"),
77+
Fill = Brush.White,
78+
Opacity = 0.4,
79+
Aspect = Stretch.Uniform,
80+
HorizontalOptions = LayoutOptions.Start
81+
}
82+
}
83+
};
84+
85+
PauseIcon.GestureRecognizers.Add(new TapGestureRecognizer()
86+
{
87+
Command = iconTapCommand
88+
});
89+
AbsoluteLayout.SetLayoutFlags(PauseIcon, AbsoluteLayoutFlags.All);
90+
AbsoluteLayout.SetLayoutBounds(PauseIcon, new Rectangle(0.5, 0.5, 0.25, 0.25));
91+
92+
var bufferingLabel = new XLabel
93+
{
94+
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label), false),
95+
HorizontalTextAlignment = XTextAlignment.Center,
96+
TextColor = Color.FromHex("#eeeeeeee")
97+
};
98+
bufferingLabel.SetBinding(XLabel.TextProperty, new Binding
99+
{
100+
Path = "BufferingProgress",
101+
StringFormat = "{0:0%}"
102+
});
103+
bufferingLabel.SetBinding(IsVisibleProperty, new Binding
104+
{
105+
Path = "IsBuffering",
106+
});
107+
AbsoluteLayout.SetLayoutFlags(bufferingLabel, AbsoluteLayoutFlags.All);
108+
AbsoluteLayout.SetLayoutBounds(bufferingLabel, new Rectangle(0.5, 0.5, 0.25, 0.25));
109+
110+
var progressBoxView = new BoxView
111+
{
112+
Color = Color.FromHex($"#4286f4")
113+
};
114+
progressBoxView.SetBinding(AbsoluteLayout.LayoutBoundsProperty, new Binding
115+
{
116+
Path = "Progress",
117+
Converter = new ProgressToBoundTextConverter()
118+
});
119+
AbsoluteLayout.SetLayoutFlags(progressBoxView, AbsoluteLayoutFlags.All);
120+
121+
var posLabel = new XLabel
122+
{
123+
Margin = new Thickness(10, 0, 0, 0),
124+
FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(XLabel)),
125+
HorizontalTextAlignment = XTextAlignment.Start
126+
};
127+
posLabel.SetBinding(XLabel.TextProperty, new Binding
128+
{
129+
Path = "Position",
130+
Converter = new MillisecondToTextConverter()
131+
});
132+
AbsoluteLayout.SetLayoutFlags(posLabel, AbsoluteLayoutFlags.All);
133+
AbsoluteLayout.SetLayoutBounds(posLabel, new Rectangle(0, 0, 1, 1));
134+
135+
var durationLabel = new XLabel
136+
{
137+
Margin = new Thickness(0, 0, 10, 0),
138+
FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(XLabel)),
139+
HorizontalTextAlignment = XTextAlignment.End
140+
};
141+
durationLabel.SetBinding(XLabel.TextProperty, new Binding
142+
{
143+
Path = "Duration",
144+
Converter = new MillisecondToTextConverter()
145+
});
146+
AbsoluteLayout.SetLayoutFlags(durationLabel, AbsoluteLayoutFlags.All);
147+
AbsoluteLayout.SetLayoutBounds(durationLabel, new Rectangle(0, 0, 1, 1));
148+
149+
var progressInnerLayout = new AbsoluteLayout
150+
{
151+
HorizontalOptions = LayoutOptions.FillAndExpand,
152+
HeightRequest = 23,
153+
BackgroundColor = Color.FromHex("#80000000"),
154+
Children =
155+
{
156+
progressBoxView,
157+
posLabel,
158+
durationLabel
159+
}
160+
};
161+
162+
var progressLayout = new StackLayout
163+
{
164+
Children =
165+
{
166+
new StackLayout { VerticalOptions = LayoutOptions.FillAndExpand },
167+
new StackLayout
168+
{
169+
Margin = Device.Idiom == TargetIdiom.Watch ? new Thickness(80, 0, 80, 0) : 20,
170+
VerticalOptions = LayoutOptions.End,
171+
HorizontalOptions = LayoutOptions.FillAndExpand,
172+
BackgroundColor = Color.FromHex("#50000000"),
173+
Children = { progressInnerLayout }
174+
}
175+
}
176+
};
177+
AbsoluteLayout.SetLayoutFlags(progressLayout, AbsoluteLayoutFlags.All);
178+
AbsoluteLayout.SetLayoutBounds(progressLayout, new Rectangle(0, 0, 1, 1));
179+
180+
Content = new AbsoluteLayout
181+
{
182+
HorizontalOptions = LayoutOptions.FillAndExpand,
183+
VerticalOptions = LayoutOptions.FillAndExpand,
184+
Children =
185+
{
186+
progressLayout,
187+
PlayIcon,
188+
PauseIcon,
189+
bufferingLabel
190+
}
191+
};
192+
}
193+
194+
protected override void OnBindingContextChanged()
195+
{
196+
base.OnBindingContextChanged();
197+
if (BindingContext is not IMediaPlayer player)
198+
throw new InvalidOperationException($"{nameof(BindingContext)} must be {nameof(IMediaPlayer)}");
199+
200+
player.PlaybackPaused += OnPlaybackStateChanged;
201+
player.PlaybackStarted += OnPlaybackStateChanged;
202+
player.PlaybackStopped += OnPlaybackStateChanged;
203+
}
204+
205+
async void OnPlaybackStateChanged(object sender, EventArgs e)
206+
{
207+
if (BindingContext is not IMediaPlayer player)
208+
throw new InvalidOperationException($"{nameof(BindingContext)} must be {nameof(IMediaPlayer)}");
209+
210+
if (player.State == PlaybackState.Playing)
211+
{
212+
await Task.WhenAll(PlayIcon.FadeTo(0, 100), PlayIcon.ScaleTo(3.0, 300));
213+
214+
PlayIcon.IsVisible = false;
215+
PlayIcon.Scale = 1.0;
216+
PauseIcon.IsVisible = true;
217+
218+
await PauseIcon.FadeTo(1, 50);
219+
}
220+
else
221+
{
222+
await Task.WhenAll(PauseIcon.FadeTo(0, 100), PauseIcon.ScaleTo(3.0, 300));
223+
224+
PauseIcon.IsVisible = false;
225+
PauseIcon.Scale = 1.0;
226+
PlayIcon.IsVisible = true;
227+
228+
await PlayIcon.FadeTo(1, 50);
229+
}
230+
}
231+
}
232+
233+
class ProgressToBoundTextConverter : IValueConverter
234+
{
235+
public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
236+
{
237+
var progress = (double)(value ?? throw new ArgumentNullException(nameof(value)));
238+
239+
if (double.IsNaN(progress))
240+
{
241+
progress = 0d;
242+
}
243+
244+
return new Rectangle(0, 0, progress, 1);
245+
}
246+
247+
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
248+
{
249+
var rect = (Rectangle)(value ?? throw new ArgumentNullException(nameof(value)));
250+
return rect.Width;
251+
}
252+
}
253+
254+
class MillisecondToTextConverter : IValueConverter
255+
{
256+
public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
257+
{
258+
var millisecond = (int)(value ?? throw new ArgumentNullException(nameof(value)));
259+
var second = (millisecond / 1000) % 60;
260+
var min = (millisecond / 1000 / 60) % 60;
261+
var hour = millisecond / 1000 / 60 / 60;
262+
263+
if (hour > 0)
264+
{
265+
return string.Format("{0:d2}:{1:d2}:{2:d2}", hour, min, second);
266+
}
267+
else
268+
{
269+
return string.Format("{0:d2}:{1:d2}", min, second);
270+
}
271+
}
272+
273+
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
274+
}
275+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Globalization;
3+
using Xamarin.Forms;
4+
5+
namespace Xamarin.CommunityToolkit.UI.Views
6+
{
7+
public class IntToTimeSpanConverter : IValueConverter
8+
{
9+
public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
10+
{
11+
if (value == null)
12+
throw new ArgumentNullException(nameof(value));
13+
14+
return TimeSpan.FromMilliseconds((int)value);
15+
}
16+
17+
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
18+
{
19+
if (value == null)
20+
throw new ArgumentNullException(nameof(value));
21+
22+
return ((TimeSpan)value).Milliseconds;
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)