diff --git a/samples/NotificationFlyoutSample.Host/Program.cs b/samples/NotificationFlyoutSample.Host/Program.cs index 2501313..b6b01c8 100644 --- a/samples/NotificationFlyoutSample.Host/Program.cs +++ b/samples/NotificationFlyoutSample.Host/Program.cs @@ -13,7 +13,7 @@ namespace NotificationFlyoutSample.Host var app = new App(); new NotificationFlyoutApplication { - Flyout = new Shell() + Flyout = new NowPlayingFlyout() }; app.Run(); } diff --git a/samples/NotificationFlyoutSample/Class1.cs b/samples/NotificationFlyoutSample/Class1.cs new file mode 100644 index 0000000..a574a9f --- /dev/null +++ b/samples/NotificationFlyoutSample/Class1.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System; + +namespace NotificationFlyoutSample +{ + public static class DispatcherQueueExtensions + { + /// + /// Indicates whether or not is available. + /// + private static bool IsHasThreadAccessPropertyAvailable => true; + + public static Task EnqueueAsync(this DispatcherQueue dispatcher, Action function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) + { + // Run the function directly when we have thread access. + // Also reuse Task.CompletedTask in case of success, + // to skip an unnecessary heap allocation for every invocation. + if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) + { + try + { + function(); + + return Task.CompletedTask; + } + catch (Exception e) + { + return Task.FromException(e); + } + } + + static Task TryEnqueueAsync(DispatcherQueue dispatcher, Action function, DispatcherQueuePriority priority) + { + var taskCompletionSource = new TaskCompletionSource(); + + if (!dispatcher.TryEnqueue(priority, () => + { + try + { + function(); + + taskCompletionSource.SetResult(null); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + })) + { + taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); + } + + return taskCompletionSource.Task; + } + + return TryEnqueueAsync(dispatcher, function, priority); + } + + /// + /// Invokes a given function on the target and returns a + /// that completes when the invocation of the function is completed. + /// + /// The return type of to relay through the returned . + /// The target to invoke the code on. + /// The to invoke. + /// The priority level for the function to invoke. + /// A that completes when the invocation of is over. + /// If the current thread has access to , will be invoked directly. + public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) + { + if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) + { + try + { + return Task.FromResult(function()); + } + catch (Exception e) + { + return Task.FromException(e); + } + } + + static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority) + { + var taskCompletionSource = new TaskCompletionSource(); + + if (!dispatcher.TryEnqueue(priority, () => + { + try + { + taskCompletionSource.SetResult(function()); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + })) + { + taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); + } + + return taskCompletionSource.Task; + } + + return TryEnqueueAsync(dispatcher, function, priority); + } + + /// + /// Invokes a given function on the target and returns a + /// that acts as a proxy for the one returned by the given function. + /// + /// The target to invoke the code on. + /// The to invoke. + /// The priority level for the function to invoke. + /// A that acts as a proxy for the one returned by . + /// If the current thread has access to , will be invoked directly. + public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) + { + // If we have thread access, we can retrieve the task directly. + // We don't use ConfigureAwait(false) in this case, in order + // to let the caller continue its execution on the same thread + // after awaiting the task returned by this function. + if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) + { + try + { + if (function() is Task awaitableResult) + { + return awaitableResult; + } + + return Task.FromException(GetEnqueueException("The Task returned by function cannot be null.")); + } + catch (Exception e) + { + return Task.FromException(e); + } + } + + static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority) + { + var taskCompletionSource = new TaskCompletionSource(); + + if (!dispatcher.TryEnqueue(priority, async () => + { + try + { + if (function() is Task awaitableResult) + { + await awaitableResult.ConfigureAwait(false); + + taskCompletionSource.SetResult(null); + } + else + { + taskCompletionSource.SetException(GetEnqueueException("The Task returned by function cannot be null.")); + } + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + })) + { + taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); + } + + return taskCompletionSource.Task; + } + + return TryEnqueueAsync(dispatcher, function, priority); + } + + /// + /// Invokes a given function on the target and returns a + /// that acts as a proxy for the one returned by the given function. + /// + /// The return type of to relay through the returned . + /// The target to invoke the code on. + /// The to invoke. + /// The priority level for the function to invoke. + /// A that relays the one returned by . + /// If the current thread has access to , will be invoked directly. + public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal) + { + if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess) + { + try + { + if (function() is Task awaitableResult) + { + return awaitableResult; + } + + return Task.FromException(GetEnqueueException("The Task returned by function cannot be null.")); + } + catch (Exception e) + { + return Task.FromException(e); + } + } + + static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func> function, DispatcherQueuePriority priority) + { + var taskCompletionSource = new TaskCompletionSource(); + + if (!dispatcher.TryEnqueue(priority, async () => + { + try + { + if (function() is Task awaitableResult) + { + var result = await awaitableResult.ConfigureAwait(false); + + taskCompletionSource.SetResult(result); + } + else + { + taskCompletionSource.SetException(GetEnqueueException("The Task returned by function cannot be null.")); + } + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + } + })) + { + taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation")); + } + + return taskCompletionSource.Task; + } + + return TryEnqueueAsync(dispatcher, function, priority); + } + + /// + /// Creates an to return when an enqueue operation fails. + /// + /// The message of the exception. + /// An with a specified message. + [MethodImpl(MethodImplOptions.NoInlining)] + private static InvalidOperationException GetEnqueueException(string message) + { + return new InvalidOperationException(message); + } + } +} diff --git a/samples/NotificationFlyoutSample/INavigation.cs b/samples/NotificationFlyoutSample/INavigation.cs new file mode 100644 index 0000000..78df783 --- /dev/null +++ b/samples/NotificationFlyoutSample/INavigation.cs @@ -0,0 +1,7 @@ +namespace NotificationFlyoutSample +{ + public interface INavigation + { + void OnNavigatedTo(); + } +} diff --git a/samples/NotificationFlyoutSample/NotificationFlyoutSample.csproj b/samples/NotificationFlyoutSample/NotificationFlyoutSample.csproj index 87d33c2..32a7545 100644 --- a/samples/NotificationFlyoutSample/NotificationFlyoutSample.csproj +++ b/samples/NotificationFlyoutSample/NotificationFlyoutSample.csproj @@ -119,9 +119,16 @@ App.xaml + + + + NowPlayingPage.xaml + + + - - Shell.xaml + + NowPlayingFlyout.xaml @@ -158,14 +165,18 @@ 6.1.2 - 2.6.0-prerelease.210129001 + 2.6.0-prerelease.210113001 - + MSBuild:Compile Designer + + Designer + MSBuild:Compile + @@ -192,5 +203,6 @@ false false CS8305 + 8.0 \ No newline at end of file diff --git a/samples/NotificationFlyoutSample/NowPlayingFlyout.xaml b/samples/NotificationFlyoutSample/NowPlayingFlyout.xaml new file mode 100644 index 0000000..8ff6dfa --- /dev/null +++ b/samples/NotificationFlyoutSample/NowPlayingFlyout.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/NotificationFlyoutSample/NowPlayingFlyout.xaml.cs b/samples/NotificationFlyoutSample/NowPlayingFlyout.xaml.cs new file mode 100644 index 0000000..3d8dcb2 --- /dev/null +++ b/samples/NotificationFlyoutSample/NowPlayingFlyout.xaml.cs @@ -0,0 +1,24 @@ +using Windows.UI.Xaml; + +namespace NotificationFlyoutSample +{ + public sealed partial class NowPlayingFlyout + { + public NowPlayingFlyout() + { + InitializeComponent(); + Opened += OnOpened; + } + + private void OnOpened(object sender, object args) + { + RootFrame.Navigate(typeof(NowPlayingPage)); + } + + private void OnCloseMenuFlyoutItemClick(object sender, RoutedEventArgs args) + { + var app = GetApplication(); + app.Exit(); + } + } +} \ No newline at end of file diff --git a/samples/NotificationFlyoutSample/Shell.xaml b/samples/NotificationFlyoutSample/NowPlayingPage.xaml similarity index 55% rename from samples/NotificationFlyoutSample/Shell.xaml rename to samples/NotificationFlyoutSample/NowPlayingPage.xaml index 1e77c15..c4886b8 100644 --- a/samples/NotificationFlyoutSample/Shell.xaml +++ b/samples/NotificationFlyoutSample/NowPlayingPage.xaml @@ -1,25 +1,11 @@ - - - - - - - - - - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + - + @@ -34,11 +20,11 @@ Height="94" Background="Gray" CornerRadius="4"> - + - - + + @@ -51,21 +37,44 @@ HorizontalAlignment="Center" Background="Transparent" DefaultLabelPosition="Collapsed"> - + - + - + + + + + + - \ No newline at end of file + diff --git a/samples/NotificationFlyoutSample/NowPlayingPage.xaml.cs b/samples/NotificationFlyoutSample/NowPlayingPage.xaml.cs new file mode 100644 index 0000000..2425dbb --- /dev/null +++ b/samples/NotificationFlyoutSample/NowPlayingPage.xaml.cs @@ -0,0 +1,29 @@ +using System; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; + +namespace NotificationFlyoutSample +{ + public sealed partial class NowPlayingPage : Page + { + public NowPlayingPage() + { + InitializeComponent(); + + ViewModel = new NowPlayingPageViewModel(); + DataContext = ViewModel; + } + + public NowPlayingPageViewModel ViewModel { get; } + + protected override void OnNavigatedTo(NavigationEventArgs args) + { + if (DataContext is INavigation navigation) + { + navigation.OnNavigatedTo(); + } + + base.OnNavigatedTo(args); + } + } +} diff --git a/samples/NotificationFlyoutSample/NowPlayingPageViewModel.cs b/samples/NotificationFlyoutSample/NowPlayingPageViewModel.cs new file mode 100644 index 0000000..56fe897 --- /dev/null +++ b/samples/NotificationFlyoutSample/NowPlayingPageViewModel.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading.Tasks; +using Windows.ApplicationModel.Core; +using Windows.Media.Control; +using Windows.Storage.Streams; +using Windows.System; +using Windows.UI.Core; +using Windows.UI.Xaml.Media.Imaging; + +namespace NotificationFlyoutSample +{ + + public class NowPlayingPageViewModel : ObservableObject, INavigation + { + private string _artist; + private bool _isPlaying; + private bool _isPaused; + private GlobalSystemMediaTransportControlsSession _session; + private string _song; + private BitmapImage _thumbnail; + + public bool IsPaused + { + get => _isPaused; + set => SetProperty(ref _isPaused, value); + } + + public string Artist + { + get => _artist; + set => SetProperty(ref _artist, value); + } + + public bool IsPlaying + { + get => _isPlaying; + set => SetProperty(ref _isPlaying, value); + } + + public string Song + { + get => _song; + set => SetProperty(ref _song, value); + } + + public async Task Next() + { + await _session.TrySkipNextAsync(); + } + + DispatcherQueue d; + public async void OnNavigatedTo() + { + d = DispatcherQueue.GetForCurrentThread(); + + var sessionManager = await GlobalSystemMediaTransportControlsSessionManager.RequestAsync(); + _session = sessionManager.GetCurrentSession(); + if (_session != null) + { + _session.MediaPropertiesChanged += OnMediaPropertiesChanged; + } + } + + public BitmapImage Thumbnail + { + get => _thumbnail; + set => SetProperty(ref _thumbnail, value); + } + + private async void OnMediaPropertiesChanged(GlobalSystemMediaTransportControlsSession sender, MediaPropertiesChangedEventArgs args) + { + var mediaProperties = await _session.TryGetMediaPropertiesAsync(); + + await d.EnqueueAsync(async () => { + Artist = mediaProperties.Artist; + + var foo = mediaProperties.Thumbnail; + + + if (foo != null) + { + var f = await foo.OpenReadAsync(); + var image = new BitmapImage(); + await image.SetSourceAsync(f); + + Thumbnail = image; + } + }); + + } + + public async Task Pause() + { + var result = await _session.TryPauseAsync().AsTask().ConfigureAwait(false); + if (result) + { + IsPlaying = false; + } + } + + public async Task Play() + { + await _session.TryPlayAsync().AsTask().ConfigureAwait(false); + } + + public async Task Previous() + { + await _session.TrySkipPreviousAsync().AsTask().ConfigureAwait(false); + } + + private async Task UpdateNowPlaying() + { + var playbackInfo = _session.GetPlaybackInfo(); + var mediaProperties = await _session.TryGetMediaPropertiesAsync(); + + Artist = mediaProperties.Artist; + Song = mediaProperties.Title; + + } + } +} diff --git a/samples/NotificationFlyoutSample/ObservableObject.cs b/samples/NotificationFlyoutSample/ObservableObject.cs new file mode 100644 index 0000000..cb4d71e --- /dev/null +++ b/samples/NotificationFlyoutSample/ObservableObject.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace NotificationFlyoutSample +{ + public class ObservableObject : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + protected virtual bool SetProperty(ref T backingStore, T value, [CallerMemberName] string propertyName = "", + Action onChanged = null, Func validateValue = null) + { + if (EqualityComparer.Default.Equals(backingStore, value)) + return false; + + if (validateValue != null && !validateValue(backingStore, value)) + return false; + + backingStore = value; + onChanged?.Invoke(); + OnPropertyChanged(propertyName); + return true; + } + } +} diff --git a/samples/NotificationFlyoutSample/Shell.xaml.cs b/samples/NotificationFlyoutSample/Shell.xaml.cs deleted file mode 100644 index c5f5cd3..0000000 --- a/samples/NotificationFlyoutSample/Shell.xaml.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Threading.Tasks; -using Windows.Media.Control; - -namespace NotificationFlyoutSample -{ - public sealed partial class Shell - { - public Shell() - { - InitializeComponent(); - } - - - private void MenuFlyoutItem_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) - { - var app = GetApplication(); - app.Exit(); - } - } -} \ No newline at end of file diff --git a/samples/WapProjTemplate1/Images/LockScreenLogo.scale-200.png b/samples/WapProjTemplate1/Images/LockScreenLogo.scale-200.png new file mode 100644 index 0000000..735f57a Binary files /dev/null and b/samples/WapProjTemplate1/Images/LockScreenLogo.scale-200.png differ diff --git a/samples/WapProjTemplate1/Images/SplashScreen.scale-200.png b/samples/WapProjTemplate1/Images/SplashScreen.scale-200.png new file mode 100644 index 0000000..023e7f1 Binary files /dev/null and b/samples/WapProjTemplate1/Images/SplashScreen.scale-200.png differ diff --git a/samples/WapProjTemplate1/Images/Square150x150Logo.scale-200.png b/samples/WapProjTemplate1/Images/Square150x150Logo.scale-200.png new file mode 100644 index 0000000..af49fec Binary files /dev/null and b/samples/WapProjTemplate1/Images/Square150x150Logo.scale-200.png differ diff --git a/samples/WapProjTemplate1/Images/Square44x44Logo.scale-200.png b/samples/WapProjTemplate1/Images/Square44x44Logo.scale-200.png new file mode 100644 index 0000000..ce342a2 Binary files /dev/null and b/samples/WapProjTemplate1/Images/Square44x44Logo.scale-200.png differ diff --git a/samples/WapProjTemplate1/Images/Square44x44Logo.targetsize-24_altform-unplated.png b/samples/WapProjTemplate1/Images/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000..f6c02ce Binary files /dev/null and b/samples/WapProjTemplate1/Images/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/samples/WapProjTemplate1/Images/StoreLogo.png b/samples/WapProjTemplate1/Images/StoreLogo.png new file mode 100644 index 0000000..7385b56 Binary files /dev/null and b/samples/WapProjTemplate1/Images/StoreLogo.png differ diff --git a/samples/WapProjTemplate1/Images/Wide310x150Logo.scale-200.png b/samples/WapProjTemplate1/Images/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000..288995b Binary files /dev/null and b/samples/WapProjTemplate1/Images/Wide310x150Logo.scale-200.png differ diff --git a/samples/WapProjTemplate1/Package.appxmanifest b/samples/WapProjTemplate1/Package.appxmanifest new file mode 100644 index 0000000..404ce86 --- /dev/null +++ b/samples/WapProjTemplate1/Package.appxmanifest @@ -0,0 +1,49 @@ + + + + + + + + WapProjTemplate1 + dan + Images\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/WapProjTemplate1/WapProjTemplate1.wapproj b/samples/WapProjTemplate1/WapProjTemplate1.wapproj new file mode 100644 index 0000000..b27a1b3 --- /dev/null +++ b/samples/WapProjTemplate1/WapProjTemplate1.wapproj @@ -0,0 +1,121 @@ + + + + 15.0 + + + + Debug + x86 + + + Release + x86 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + AnyCPU + + + Release + AnyCPU + + + + $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\ + + + + 28fdf14a-6429-424a-a556-3f0a13f8b48f + 10.0.19041.0 + 10.0.19041.0 + en-US + True + ..\NotificationFlyoutSample.Host\NotificationFlyoutSample.Host.csproj + False + WapProjTemplate1_TemporaryKey.pfx + SHA256 + True + True + x64 + 0 + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + Designer + + + + + + + + + + + + + + + + + + + True + + + \ No newline at end of file diff --git a/src/TheXamlGuy.NotificationFlyout.Common/TheXamlGuy.NotificationFlyout.Common.csproj b/src/TheXamlGuy.NotificationFlyout.Common/TheXamlGuy.NotificationFlyout.Common.csproj index 756b42f..57a89e3 100644 --- a/src/TheXamlGuy.NotificationFlyout.Common/TheXamlGuy.NotificationFlyout.Common.csproj +++ b/src/TheXamlGuy.NotificationFlyout.Common/TheXamlGuy.NotificationFlyout.Common.csproj @@ -5,9 +5,10 @@ 9.0 false TheXamlGuy - TheXamlGuy - NotificationFlyout - 1.0.0 + TheXamlGuy + Daniel Clark + TheXamlGuy.NotificationFlyout + 1.1.0 AnyCPU;x64 diff --git a/src/TheXamlGuy.NotificationFlyout.Shared.UI/TheXamlGuy.NotificationFlyout.Shared.UI.csproj b/src/TheXamlGuy.NotificationFlyout.Shared.UI/TheXamlGuy.NotificationFlyout.Shared.UI.csproj index a036c86..89586cf 100644 --- a/src/TheXamlGuy.NotificationFlyout.Shared.UI/TheXamlGuy.NotificationFlyout.Shared.UI.csproj +++ b/src/TheXamlGuy.NotificationFlyout.Shared.UI/TheXamlGuy.NotificationFlyout.Shared.UI.csproj @@ -4,11 +4,11 @@ netcoreapp3.1;uap10.0.19041 9.0 false - TheXamlGuy - TheXamlGuy - NotificationFlyout - 1.0.0 AnyCPU;x64 + TheXamlGuy + Daniel Clark + TheXamlGuy.NotificationFlyout + 1.1.0 diff --git a/src/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls.csproj b/src/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls.csproj index 99fc891..9fec24e 100644 --- a/src/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls.csproj +++ b/src/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/TheXamlGuy.NotificationFlyout.Uwp.UI.Controls.csproj @@ -8,9 +8,9 @@ 9.0 false TheXamlGuy - TheXamlGuy - NotificationFlyout - 1.0.0 + Daniel Clark + TheXamlGuy.NotificationFlyout + 1.1.0 diff --git a/src/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls.csproj b/src/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls.csproj index bddaa8f..008b36b 100644 --- a/src/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls.csproj +++ b/src/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls/TheXamlGuy.NotificationFlyout.Wpf.UI.Controls.csproj @@ -7,9 +7,9 @@ AnyCPU;x64 NU1702 TheXamlGuy - TheXamlGuy - NotificationFlyout - 1.0.0 + Daniel Clark + TheXamlGuy.NotificationFlyout + 1.1.0 diff --git a/src/TheXamlGuy.NotificationFlyout.Wpf.UI/TheXamlGuy.NotificationFlyout.Wpf.UI.csproj b/src/TheXamlGuy.NotificationFlyout.Wpf.UI/TheXamlGuy.NotificationFlyout.Wpf.UI.csproj index 983797b..ca8c6bc 100644 --- a/src/TheXamlGuy.NotificationFlyout.Wpf.UI/TheXamlGuy.NotificationFlyout.Wpf.UI.csproj +++ b/src/TheXamlGuy.NotificationFlyout.Wpf.UI/TheXamlGuy.NotificationFlyout.Wpf.UI.csproj @@ -6,9 +6,9 @@ 9.0 AnyCPU;x64 TheXamlGuy - TheXamlGuy - NotificationFlyout - 1.0.0 + Daniel Clark + TheXamlGuy.NotificationFlyout + 1.1.0