### Description of Change

We all know the drill!
This commit is contained in:
Rui Marinho 2024-08-23 21:42:28 +01:00 коммит произвёл GitHub
Родитель a15cf9b55a eeafab4a0b
Коммит ad8275e2bb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
24 изменённых файлов: 267 добавлений и 237 удалений

Просмотреть файл

@ -45,124 +45,6 @@
</Attributes> </Attributes>
<Docs> <Docs>
<summary>An <see cref="T:Microsoft.Maui.Controls.ItemsView`1" /> that displays a collection of data as a vertical list.</summary> <summary>An <see cref="T:Microsoft.Maui.Controls.ItemsView`1" /> that displays a collection of data as a vertical list.</summary>
<remarks>
<para>
<img href="~/xml/Microsoft.Maui.Controls/_images/ListView.TripleScreenShot.png" />
</para>
<para>The following example shows a basic use:</para>
<example>
<code lang="csharp lang-csharp"><![CDATA[
using System;
using System.Collections.Generic;
using Microsoft.Maui.Controls;
namespace FormsGallery
{
class ListViewDemoPage : ContentPage
{
class Person
{
public Person(string name, DateTime birthday, Color favoriteColor)
{
this.Name = name;
this.Birthday = birthday;
this.FavoriteColor = favoriteColor;
}
public string Name { private set; get; }
public DateTime Birthday { private set; get; }
public Color FavoriteColor { private set; get; }
};
public ListViewDemoPage()
{
Label header = new Label
{
Text = "ListView",
FontSize = Device.GetNamedSize (NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center
};
// Define some data.
List<Person> people = new List<Person>
{
new Person("Abigail", new DateTime(1975, 1, 15), Color.Aqua),
new Person("Bob", new DateTime(1976, 2, 20), Color.Black),
// ...etc.,...
new Person("Yvonne", new DateTime(1987, 1, 10), Color.Purple),
new Person("Zachary", new DateTime(1988, 2, 5), Color.Red)
};
// Create the ListView.
ListView listView = new ListView
{
// Source of data items.
ItemsSource = people,
// Define template for displaying each item.
// (Argument of DataTemplate constructor is called for
// each item; it must return a Cell derivative.)
ItemTemplate = new DataTemplate(() =>
{
// Create views with bindings for displaying each property.
Label nameLabel = new Label();
nameLabel.SetBinding(Label.TextProperty, "Name");
Label birthdayLabel = new Label();
birthdayLabel.SetBinding(Label.TextProperty,
new Binding("Birthday", BindingMode.OneWay,
null, null, "Born {0:d}"));
BoxView boxView = new BoxView();
boxView.SetBinding(BoxView.ColorProperty, "FavoriteColor");
// Return an assembled ViewCell.
return new ViewCell
{
View = new StackLayout
{
Padding = new Thickness(0, 5),
Orientation = StackOrientation.Horizontal,
Children =
{
boxView,
new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Spacing = 0,
Children =
{
nameLabel,
birthdayLabel
}
}
}
}
};
})
};
// Accomodate iPhone status bar.
this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);
// Build the page.
this.Content = new StackLayout
{
Children =
{
header,
listView
}
};
}
}
}
]]></code>
</example>
</remarks>
</Docs> </Docs>
<Members> <Members>
<Member MemberName=".ctor"> <Member MemberName=".ctor">

Просмотреть файл

@ -330,7 +330,10 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
var actuallyRemoved = poppedViewController == null ? true : !await task; var actuallyRemoved = poppedViewController == null ? true : !await task;
_ignorePopCall = false; _ignorePopCall = false;
poppedViewController?.Dispose(); if (poppedViewController is ParentingViewController pvc)
pvc.Disconnect(false);
else
poppedViewController?.Dispose();
UpdateToolBarVisible(); UpdateToolBarVisible();
return actuallyRemoved; return actuallyRemoved;
@ -1144,7 +1147,9 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
return; return;
if (child is not null) if (child is not null)
{
child.PropertyChanged -= HandleChildPropertyChanged; child.PropertyChanged -= HandleChildPropertyChanged;
}
if (value is not null) if (value is not null)
{ {
@ -1274,6 +1279,57 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
} }
} }
internal void Disconnect(bool dispose)
{
if (Child is Page child)
{
child.SendDisappearing();
child.PropertyChanged -= HandleChildPropertyChanged;
Child = null;
}
if (_tracker is not null)
{
_tracker.Target = null;
_tracker.CollectionChanged -= TrackerOnCollectionChanged;
_tracker = null;
}
if (NavigationItem.TitleView is not null)
{
if (dispose)
NavigationItem.TitleView.Dispose();
NavigationItem.TitleView = null;
}
if (NavigationItem.RightBarButtonItems is not null && dispose)
{
for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++)
NavigationItem.RightBarButtonItems[i].Dispose();
}
if (ToolbarItems is not null && dispose)
{
for (var i = 0; i < ToolbarItems.Length; i++)
ToolbarItems[i].Dispose();
}
for (int i = View.Subviews.Length - 1; i >= 0; i--)
{
View.Subviews[i].RemoveFromSuperview();
}
for (int i = ChildViewControllers.Length - 1; i >= 0; i--)
{
var childViewController = ChildViewControllers[i];
childViewController.View.RemoveFromSuperview();
childViewController.RemoveFromParentViewController();
}
}
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if (_disposed) if (_disposed)
@ -1285,34 +1341,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
if (disposing) if (disposing)
{ {
if (Child is Page child) Disconnect(true);
{
child.SendDisappearing();
child.PropertyChanged -= HandleChildPropertyChanged;
Child = null;
}
_tracker.Target = null;
_tracker.CollectionChanged -= TrackerOnCollectionChanged;
_tracker = null;
if (NavigationItem.TitleView != null)
{
NavigationItem.TitleView.Dispose();
NavigationItem.TitleView = null;
}
if (NavigationItem.RightBarButtonItems != null)
{
for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++)
NavigationItem.RightBarButtonItems[i].Dispose();
}
if (ToolbarItems != null)
{
for (var i = 0; i < ToolbarItems.Length; i++)
ToolbarItems[i].Dispose();
}
} }
base.Dispose(disposing); base.Dispose(disposing);
@ -1973,7 +2002,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
if (_child?.IsConnected() == true) if (_child?.IsConnected() == true)
{ {
_child.PlatformView.RemoveFromSuperview(); (_child.ContainerView ?? _child.PlatformView).RemoveFromSuperview();
_child.DisconnectHandler(); _child.DisconnectHandler();
_child = null; _child = null;
} }

Просмотреть файл

@ -208,7 +208,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
public override void SetNeedsLayout() public override void SetNeedsLayout()
{ {
base.SetNeedsLayout(); base.SetNeedsLayout();
Superview?.SetNeedsLayout(); this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
} }
[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)] [Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]

Просмотреть файл

@ -402,6 +402,8 @@ namespace Microsoft.Maui.Controls
void RemoveFromInnerChildren(Element page) void RemoveFromInnerChildren(Element page)
{ {
InternalChildren.Remove(page); InternalChildren.Remove(page);
// TODO For NET9 we should remove this because the DisconnectHandlers will take care of it
page.Handler = null; page.Handler = null;
} }

Просмотреть файл

@ -188,7 +188,7 @@ namespace Microsoft.Maui.Controls.Platform
{ {
platformButton.ImageEdgeInsets = imageInsets; platformButton.ImageEdgeInsets = imageInsets;
platformButton.TitleEdgeInsets = titleInsets; platformButton.TitleEdgeInsets = titleInsets;
platformButton.Superview?.SetNeedsLayout(); platformButton.GetSuperViewIfWindowSet()?.SetNeedsLayout();
} }
#pragma warning restore CA1416, CA1422 #pragma warning restore CA1416, CA1422
} }

Просмотреть файл

@ -51,7 +51,7 @@ namespace Microsoft.Maui.Controls
newToolBarItems.AddRange(toolbarItems); newToolBarItems.AddRange(toolbarItems);
if (sender is ToolbarItem ti) if (sender is ToolbarItem ti)
PlatformView.OnToolbarItemPropertyChanged(e, ti, newToolBarItems, MauiContext!, null, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon); PlatformView.OnToolbarItemPropertyChanged(e, ti, newToolBarItems, MauiContext!, BarTextColor, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon);
} }
void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem) void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem)

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 27 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 25 KiB

Просмотреть файл

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ns="clr-namespace:Maui.Controls.Sample.Issues"
Shell.TitleColor="Red"
x:Class="Maui.Controls.Sample.Issues.Issue10660">
<Shell.ToolbarItems>
<ToolbarItem x:Name="StateButton" Text="Close"/>
</Shell.ToolbarItems>
<ShellContent Title="Home page"
Route="MainPage">
<ShellContent.ContentTemplate>
<DataTemplate>
<ContentPage>
<StackLayout>
<Button Text="Change Text"
AutomationId="ChangeText"
Clicked="ChangeTextClicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
</DataTemplate>
</ShellContent.ContentTemplate>
</ShellContent>
</Shell>

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using System.Collections.ObjectModel;
using Microsoft.Maui.Controls;
namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.Github, 10660, "Inconsistent toolbar text color on interaction", PlatformAffected.Android)]
public partial class Issue10660 : Shell
{
public Issue10660()
{
InitializeComponent();
}
private void ChangeTextClicked(object sender, EventArgs e)
{
StateButton.Text = StateButton.Text == "Close" ? "Open" : "Close";
}
}

Просмотреть файл

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ns="clr-namespace:Maui.Controls.Sample.Issues"
Shell.TitleColor="Red"
x:Class="Maui.Controls.Sample.Issues.Issue22937">
<Shell.ToolbarItems>
<ToolbarItem x:Name="EditButton" Text="Edit"/>
<ToolbarItem x:Name="SaveButton" Text="Save"/>
</Shell.ToolbarItems>
<ShellContent Title="Home page"
Route="MainPage">
<ShellContent.ContentTemplate>
<DataTemplate>
<ContentPage>
<StackLayout>
<Button Text="Change State"
AutomationId="ChangeState"
Clicked="ChangeStateClicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
</DataTemplate>
</ShellContent.ContentTemplate>
</ShellContent>
</Shell>

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using System.Collections.ObjectModel;
using Microsoft.Maui.Controls;
namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.Github, 22937, "ToolbarItem font color not updating properly after changing the available state at runtime", PlatformAffected.Android)]
public partial class Issue22937 : Shell
{
public Issue22937()
{
InitializeComponent();
}
private void ChangeStateClicked(object sender, EventArgs e)
{
EditButton.IsEnabled = !EditButton.IsEnabled;
}
}

Просмотреть файл

@ -0,0 +1,27 @@
#if ANDROID || WINDOWS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue10660 : _IssuesUITest
{
public override string Issue => "Inconsistent toolbar text color on interaction";
public Issue10660(TestDevice device)
: base(device)
{ }
[Test]
[Category(UITestCategories.ToolbarItem)]
public void ToolbarTextColorOnInteraction()
{
App.WaitForElement("ChangeText");
App.Tap("ChangeText");
VerifyScreenshot();
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,27 @@
#if ANDROID || WINDOWS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue22937 : _IssuesUITest
{
public override string Issue => "ToolbarItem font color not updating properly after changing the available state at runtime";
public Issue22937(TestDevice device)
: base(device)
{ }
[Test]
[Category(UITestCategories.ToolbarItem)]
public void ToolbarItemFontColorDynamicUpdate()
{
App.WaitForElement("ChangeState");
App.Tap("ChangeState");
VerifyScreenshot();
}
}
}
#endif

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.0 KiB

Просмотреть файл

@ -63,7 +63,7 @@ namespace Microsoft.Maui.Handlers
if (containerView is WrapperView wrapperView) if (containerView is WrapperView wrapperView)
{ {
wrapperView.RemoveFromSuperview(); wrapperView.RemoveFromSuperview();
wrapperView.Dispose(); wrapperView.Disconnect();
} }
} }
} }

Просмотреть файл

@ -11,14 +11,14 @@ namespace Microsoft.Maui.Platform
{ {
InvalidateConstraintsCache(); InvalidateConstraintsCache();
base.SubviewAdded(uiview); base.SubviewAdded(uiview);
Superview?.SetNeedsLayout(); this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
} }
public override void WillRemoveSubview(UIView uiview) public override void WillRemoveSubview(UIView uiview)
{ {
InvalidateConstraintsCache(); InvalidateConstraintsCache();
base.WillRemoveSubview(uiview); base.WillRemoveSubview(uiview);
Superview?.SetNeedsLayout(); this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
} }
public override UIView? HitTest(CGPoint point, UIEvent? uievent) public override UIView? HitTest(CGPoint point, UIEvent? uievent)

Просмотреть файл

@ -140,7 +140,7 @@ namespace Microsoft.Maui.Platform
{ {
InvalidateConstraintsCache(); InvalidateConstraintsCache();
base.SetNeedsLayout(); base.SetNeedsLayout();
Superview?.SetNeedsLayout(); this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
} }
IVisualTreeElement? IVisualTreeElementProvidable.GetElement() IVisualTreeElement? IVisualTreeElementProvidable.GetElement()

Просмотреть файл

@ -266,10 +266,18 @@ namespace Microsoft.Maui.Platform
} }
} }
internal static UIView? GetSuperViewIfWindowSet(this UIView? view)
{
if (view?.Window is null)
return null;
return view.Superview;
}
public static void InvalidateMeasure(this UIView platformView, IView view) public static void InvalidateMeasure(this UIView platformView, IView view)
{ {
platformView.SetNeedsLayout(); platformView.SetNeedsLayout();
platformView.Superview?.SetNeedsLayout(); platformView.GetSuperViewIfWindowSet()?.SetNeedsLayout();
} }
public static void UpdateWidth(this UIView platformView, IView view) public static void UpdateWidth(this UIView platformView, IView view)

Просмотреть файл

@ -105,12 +105,19 @@ namespace Microsoft.Maui.Platform
SetBorder(); SetBorder();
} }
internal void Disconnect()
{
MaskLayer = null;
BackgroundMaskLayer = null;
ShadowLayer = null;
_borderView?.RemoveFromSuperview();
}
// TODO obsolete or delete this for NET9
public new void Dispose() public new void Dispose()
{ {
DisposeClip(); Disconnect();
DisposeShadow();
DisposeBorder();
base.Dispose(); base.Dispose();
} }
@ -158,7 +165,7 @@ namespace Microsoft.Maui.Platform
{ {
base.SetNeedsLayout(); base.SetNeedsLayout();
Superview?.SetNeedsLayout(); this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
} }
partial void ClipChanged() partial void ClipChanged()
@ -200,12 +207,6 @@ namespace Microsoft.Maui.Platform
backgroundMask.Path = nativePath; backgroundMask.Path = nativePath;
} }
void DisposeClip()
{
MaskLayer = null;
BackgroundMaskLayer = null;
}
void SetShadow() void SetShadow()
{ {
var shadowLayer = ShadowLayer; var shadowLayer = ShadowLayer;
@ -230,11 +231,6 @@ namespace Microsoft.Maui.Platform
shadowLayer.SetShadow(Shadow); shadowLayer.SetShadow(Shadow);
} }
void DisposeShadow()
{
ShadowLayer = null;
}
void SetBorder() void SetBorder()
{ {
if (Border == null) if (Border == null)
@ -251,11 +247,6 @@ namespace Microsoft.Maui.Platform
_borderView.UpdateMauiCALayer(Border); _borderView.UpdateMauiCALayer(Border);
} }
void DisposeBorder()
{
_borderView?.RemoveFromSuperview();
}
CALayer? GetLayer() CALayer? GetLayer()
{ {
var sublayers = Layer?.Sublayers; var sublayers = Layer?.Sublayers;

Просмотреть файл

@ -19,11 +19,9 @@ namespace Microsoft.Maui.Authentication
/// <param name="webAuthenticatorOptions">A <see cref="WebAuthenticatorOptions"/> instance containing additional configuration for this authentication call.</param> /// <param name="webAuthenticatorOptions">A <see cref="WebAuthenticatorOptions"/> instance containing additional configuration for this authentication call.</param>
/// <returns>A <see cref="WebAuthenticatorResult"/> object with the results of this operation.</returns> /// <returns>A <see cref="WebAuthenticatorResult"/> object with the results of this operation.</returns>
/// <exception cref="TaskCanceledException">Thrown when the user canceled the authentication flow.</exception> /// <exception cref="TaskCanceledException">Thrown when the user canceled the authentication flow.</exception>
/// <exception cref="HttpRequestException">Windows: Thrown when a HTTP Request error occured.</exception> /// <exception cref="PlatformNotSupportedException">Windows: Thrown when called on Windows.</exception>
/// <exception cref="Exception">Windows: Thrown when a unexpected HTTP response was received.</exception>
/// <exception cref="FeatureNotSupportedException">iOS/macOS: Thrown when iOS version is less than 13 is used or macOS less than 13.1 is used.</exception> /// <exception cref="FeatureNotSupportedException">iOS/macOS: Thrown when iOS version is less than 13 is used or macOS less than 13.1 is used.</exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// <para>Windows: Thrown when the callback custom URL scheme is not registered in the AppxManifest.xml file.</para>
/// <para>Android: Thrown when the no IntentFilter has been created for the callback URL.</para> /// <para>Android: Thrown when the no IntentFilter has been created for the callback URL.</para>
/// </exception> /// </exception>
Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions); Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions);
@ -76,13 +74,19 @@ namespace Microsoft.Maui.Authentication
/// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.</summary> /// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.</summary>
/// <param name="url"> Url to navigate to, beginning the authentication flow.</param> /// <param name="url"> Url to navigate to, beginning the authentication flow.</param>
/// <param name="callbackUrl"> Expected callback url that the navigation flow will eventually redirect to.</param> /// <param name="callbackUrl"> Expected callback url that the navigation flow will eventually redirect to.</param>
/// <returns>Returns a result parsed out from the callback url.</returns> /// <returns>Returns a result parsed out from the callback url.</returns>
#if !NETSTANDARD
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
#endif
public static Task<WebAuthenticatorResult> AuthenticateAsync(Uri url, Uri callbackUrl) public static Task<WebAuthenticatorResult> AuthenticateAsync(Uri url, Uri callbackUrl)
=> Current.AuthenticateAsync(url, callbackUrl); => Current.AuthenticateAsync(url, callbackUrl);
/// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.The start url and callbackUrl are specified in the webAuthenticatorOptions.</summary> /// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.The start url and callbackUrl are specified in the webAuthenticatorOptions.</summary>
/// <param name="webAuthenticatorOptions">Options to configure the authentication request.</param> /// <param name="webAuthenticatorOptions">Options to configure the authentication request.</param>
/// <returns>Returns a result parsed out from the callback url.</returns> /// <returns>Returns a result parsed out from the callback url.</returns>
#if !NETSTANDARD
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
#endif
public static Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions) public static Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
=> Current.AuthenticateAsync(webAuthenticatorOptions); => Current.AuthenticateAsync(webAuthenticatorOptions);

Просмотреть файл

@ -1,64 +1,13 @@
using System; using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Storage;
using Windows.Security.Authentication.Web;
namespace Microsoft.Maui.Authentication namespace Microsoft.Maui.Authentication
{ {
partial class WebAuthenticatorImplementation : IWebAuthenticator partial class WebAuthenticatorImplementation : IWebAuthenticator
{ {
public async Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions) public Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
{ {
var url = webAuthenticatorOptions?.Url; throw new PlatformNotSupportedException("This implementation of WebAuthenticator does not support Windows. See https://github.com/microsoft/WindowsAppSDK/issues/441 for more details.");
var callbackUrl = webAuthenticatorOptions?.CallbackUrl;
if (!IsUriProtocolDeclared(callbackUrl.Scheme))
throw new InvalidOperationException($"You need to declare the windows.protocol usage of the protocol/scheme `{callbackUrl.Scheme}` in your AppxManifest.xml file");
try
{
var r = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, url, callbackUrl);
switch (r.ResponseStatus)
{
case WebAuthenticationStatus.Success:
// For GET requests this is a URI:
var resultUri = new Uri(r.ResponseData.ToString());
return new WebAuthenticatorResult(resultUri, webAuthenticatorOptions?.ResponseDecoder);
case WebAuthenticationStatus.UserCancel:
throw new TaskCanceledException();
case WebAuthenticationStatus.ErrorHttp:
throw new HttpRequestException("Error: " + r.ResponseErrorDetail);
default:
throw new Exception("Response: " + r.ResponseData.ToString() + "\nStatus: " + r.ResponseStatus);
}
}
catch (FileNotFoundException)
{
throw new TaskCanceledException();
}
}
static bool IsUriProtocolDeclared(string scheme)
{
var docPath = FileSystemUtils.PlatformGetFullAppPackageFilePath(PlatformUtils.AppManifestFilename);
var doc = XDocument.Load(docPath, LoadOptions.None);
var reader = doc.CreateReader();
var namespaceManager = new XmlNamespaceManager(reader.NameTable);
namespaceManager.AddNamespace("x", PlatformUtils.AppManifestXmlns);
namespaceManager.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10");
// Check if the protocol was declared
var decl = doc.Root.XPathSelectElements($"//uap:Extension[@Category='windows.protocol']/uap:Protocol[@Name='{scheme}']", namespaceManager);
return decl != null && decl.Any();
} }
} }
} }

Просмотреть файл

@ -21,14 +21,21 @@ namespace Microsoft.Maui.Essentials.DeviceTests
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)] [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public async Task Redirect(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires) public async Task Redirect(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires)
{ {
var r = await WebAuthenticator.AuthenticateAsync( #pragma warning disable CA1416 // Validate platform compatibility: Not supported on Windows
var authenticationTask = WebAuthenticator.AuthenticateAsync(
new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"), new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"),
new Uri($"{callbackScheme}://")); new Uri($"{callbackScheme}://"));
#pragma warning restore CA1416 // Validate platform compatibility
#if WINDOWS
var exception = await Assert.ThrowsAsync<PlatformNotSupportedException>(async () => await authenticationTask);
#else
var r = await authenticationTask;
Assert.Equal(accessToken, r?.AccessToken); Assert.Equal(accessToken, r?.AccessToken);
Assert.Equal(refreshToken, r?.RefreshToken); Assert.Equal(refreshToken, r?.RefreshToken);
Assert.NotNull(r?.ExpiresIn); Assert.NotNull(r?.ExpiresIn);
Assert.True(r?.ExpiresIn > DateTime.UtcNow); Assert.True(r?.ExpiresIn > DateTime.UtcNow);
#endif
} }
[Theory] [Theory]
@ -42,18 +49,24 @@ namespace Microsoft.Maui.Essentials.DeviceTests
public async Task RedirectWithResponseDecoder(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires) public async Task RedirectWithResponseDecoder(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires)
{ {
var responseDecoder = new TestResponseDecoder(); var responseDecoder = new TestResponseDecoder();
var r = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions #pragma warning disable CA1416 // Validate platform compatibility: Not supported on Windows
var authenticationTask = WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{ {
Url = new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"), Url = new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"),
CallbackUrl = new Uri($"{callbackScheme}://"), CallbackUrl = new Uri($"{callbackScheme}://"),
ResponseDecoder = responseDecoder ResponseDecoder = responseDecoder
}); });
#pragma warning restore CA1416 // Validate platform compatibility
#if WINDOWS
var exception = await Assert.ThrowsAsync<PlatformNotSupportedException>(async () => await authenticationTask);
#else
var r = await authenticationTask;
Assert.Equal(accessToken, r?.AccessToken); Assert.Equal(accessToken, r?.AccessToken);
Assert.Equal(refreshToken, r?.RefreshToken); Assert.Equal(refreshToken, r?.RefreshToken);
Assert.NotNull(r?.ExpiresIn); Assert.NotNull(r?.ExpiresIn);
Assert.True(r?.ExpiresIn > DateTime.UtcNow); Assert.True(r?.ExpiresIn > DateTime.UtcNow);
Assert.Equal(1, responseDecoder.CallCount); Assert.Equal(1, responseDecoder.CallCount);
#endif
} }
[Theory] [Theory]