Merge branch '4.5.0'
This commit is contained in:
Коммит
85bd80e5c3
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>Xamarin.Forms.DualScreen</id>
|
||||
<version>$version$</version>
|
||||
<authors>Microsoft</authors>
|
||||
<owners>microsoft xamarin</owners>
|
||||
<tags>xamarin forms twopaneview DualScreen xamarinforms xamarinformsdualscreen xamarin.forms.dualscreen</tags>
|
||||
<license type="expression">MIT</license>
|
||||
<iconUrl>https://raw.githubusercontent.com/xamarin/Xamarin.Forms/master/Assets/xamarin_128x128.png</iconUrl>
|
||||
<projectUrl>http://xamarin.com/forms</projectUrl>
|
||||
<repository type="git" url="https://github.com/xamarin/xamarin.forms"/>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>DualScreen support for Xamarin.Forms</description>
|
||||
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
|
||||
<dependencies>
|
||||
<group>
|
||||
<dependency id="Xamarin.Forms" version="$version$"/>
|
||||
</group>
|
||||
<group targetFramework="MonoAndroid90">
|
||||
<dependency id="Xamarin.DuoSdk" version="0.0.3.2" />
|
||||
</group>
|
||||
<group targetFramework="MonoAndroid10.0">
|
||||
<dependency id="Xamarin.DuoSdk" version="0.0.3.2" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
||||
<!--netstandard 1.0-->
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\netstandard1.0\Xamarin.Forms.DualScreen.dll" target="lib\netstandard1.0" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\netstandard1.0\Xamarin.Forms.DualScreen.*pdb" target="lib\netstandard1.0" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\netstandard1.0\Xamarin.Forms.DualScreen.*mdb" target="lib\netstandard1.0" />
|
||||
|
||||
<!--netstandard 2.0 -->
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\netstandard2.0\Xamarin.Forms.DualScreen.dll" target="lib\netstandard2.0" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\netstandard2.0\Xamarin.Forms.DualScreen.*pdb" target="lib\netstandard2.0" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\netstandard2.0\Xamarin.Forms.DualScreen.*mdb" target="lib\netstandard2.0" />
|
||||
|
||||
<!--Android 90-->
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\monoandroid90\Xamarin.Forms.DualScreen.dll" target="lib\MonoAndroid90" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\monoandroid90\Xamarin.Forms.DualScreen.*pdb" target="lib\MonoAndroid90" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\monoandroid90\Xamarin.Forms.DualScreen.*mdb" target="lib\MonoAndroid90" />
|
||||
|
||||
<!--Android 10.0-->
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\monoandroid10.0\Xamarin.Forms.DualScreen.dll" target="lib\MonoAndroid10.0" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\monoandroid10.0\Xamarin.Forms.DualScreen.*pdb" target="lib\MonoAndroid10.0" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\monoandroid10.0\Xamarin.Forms.DualScreen.*mdb" target="lib\MonoAndroid10.0" />
|
||||
|
||||
<!--UAP-->
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\uap10.0.16299\Xamarin.Forms.DualScreen.dll" target="lib\uap10.0.16299" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\uap10.0.16299\Xamarin.Forms.DualScreen.*pdb" target="lib\uap10.0.16299" />
|
||||
<file src="..\Xamarin.Forms.DualScreen\bin\$Configuration$\uap10.0.16299\Xamarin.Forms.DualScreen.*mdb" target="lib\uap10.0.16299" />
|
||||
|
||||
</files>
|
||||
</package>
|
|
@ -7,10 +7,6 @@
|
|||
<Name>Windows Mobile Extensions for the UWP</Name>
|
||||
</SDKReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(OS)' != 'Windows_NT' ">
|
||||
<Compile Remove="**\*.*" />
|
||||
<None Include="**\*.*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith('uap10.0')) AND '$(OS)' == 'Windows_NT' ">
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.0.15" />
|
||||
</ItemGroup>
|
||||
|
|
Двоичный файл не отображается.
|
@ -187,6 +187,10 @@
|
|||
<Project>{4dcd0420-1168-4b77-86db-6196ee4bd491}</Project>
|
||||
<Name>Xamarin.Forms.CustomAttributes</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj">
|
||||
<Project>{fb4a866a-5721-4545-9e5d-b7f7d59875a4}</Project>
|
||||
<Name>Xamarin.Forms.DualScreen</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Maps.Android\Xamarin.Forms.Maps.Android.csproj">
|
||||
<Project>{bd50b39a-ebc5-408f-9c5e-923a8ebae473}</Project>
|
||||
<Name>Xamarin.Forms.Maps.Android</Name>
|
||||
|
|
|
@ -115,6 +115,10 @@
|
|||
<Project>{57b8b73d-c3b5-4c42-869e-7b2f17d354ac}</Project>
|
||||
<Name>Xamarin.Forms.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj">
|
||||
<Project>{fb4a866a-5721-4545-9e5d-b7f7d59875a4}</Project>
|
||||
<Name>Xamarin.Forms.DualScreen</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Maps.GTK\Xamarin.Forms.Maps.GTK.csproj">
|
||||
<Project>{a9772bb1-0e17-42f5-a6db-60bfccbfdb9d}</Project>
|
||||
<Name>Xamarin.Forms.Maps.GTK</Name>
|
||||
|
|
|
@ -120,6 +120,10 @@
|
|||
<Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project>
|
||||
<Name>Xamarin.Forms.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj">
|
||||
<Project>{fb4a866a-5721-4545-9e5d-b7f7d59875a4}</Project>
|
||||
<Name>Xamarin.Forms.DualScreen</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Maps\Xamarin.Forms.Maps.csproj">
|
||||
<Project>{7D13BAC2-C6A4-416A-B07E-C169B199E52B}</Project>
|
||||
<Name>Xamarin.Forms.Maps</Name>
|
||||
|
|
|
@ -123,6 +123,10 @@
|
|||
<Project>{57b8b73d-c3b5-4c42-869e-7b2f17d354ac}</Project>
|
||||
<Name>Xamarin.Forms.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj">
|
||||
<Project>{fb4a866a-5721-4545-9e5d-b7f7d59875a4}</Project>
|
||||
<Name>Xamarin.Forms.DualScreen</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Maps.WPF\Xamarin.Forms.Maps.WPF.csproj">
|
||||
<Project>{89b0db73-a32e-447c-9390-a2a59d89b2e4}</Project>
|
||||
<Name>Xamarin.Forms.Maps.WPF</Name>
|
||||
|
|
|
@ -105,6 +105,10 @@
|
|||
<Project>{57b8b73d-c3b5-4c42-869e-7b2f17d354ac}</Project>
|
||||
<Name>Xamarin.Forms.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj">
|
||||
<Project>{fb4a866a-5721-4545-9e5d-b7f7d59875a4}</Project>
|
||||
<Name>Xamarin.Forms.DualScreen</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Maps.UWP\Xamarin.Forms.Maps.UWP.csproj">
|
||||
<Project>{04d89a60-78ef-4a32-ae17-87e47e0233a5}</Project>
|
||||
<Name>Xamarin.Forms.Maps.UWP</Name>
|
||||
|
@ -272,4 +276,4 @@
|
|||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -142,6 +142,10 @@
|
|||
<Project>{cb9c96ce-125c-4a68-b6a1-c3ff1fbf93e1}</Project>
|
||||
<Name>Xamarin.Forms.Controls</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj">
|
||||
<Project>{fb4a866a-5721-4545-9e5d-b7f7d59875a4}</Project>
|
||||
<Name>Xamarin.Forms.DualScreen</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Maps.iOS\Xamarin.Forms.Maps.iOS.csproj">
|
||||
<Project>{aba078c4-f9bb-4924-8b2b-10fe0d2f5491}</Project>
|
||||
<Name>Xamarin.Forms.Maps.iOS</Name>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<controls:TestContentPage
|
||||
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.Issues.Issue8779">
|
||||
<StackLayout Margin="20">
|
||||
<Label
|
||||
Text="Questions"
|
||||
FontAttributes="Bold"
|
||||
HorizontalOptions="Center" />
|
||||
<Label
|
||||
Text="Swipe right to answer the following questions." />
|
||||
<SwipeView
|
||||
x:Name="swipeView">
|
||||
<SwipeView.LeftItems>
|
||||
<SwipeItems
|
||||
SwipeBehaviorOnInvoked="RemainOpen">
|
||||
<SwipeItemView
|
||||
Command="{Binding CheckAnswerCommand}"
|
||||
CommandParameter="{Binding Source={x:Reference resultEntry}, Path=Text}">
|
||||
<StackLayout
|
||||
Margin="10"
|
||||
WidthRequest="300">
|
||||
<Entry
|
||||
x:Name="resultEntry"
|
||||
Placeholder="Enter answer"
|
||||
HorizontalOptions="CenterAndExpand" />
|
||||
<Label
|
||||
Text="Check"
|
||||
FontAttributes="Bold"
|
||||
HorizontalOptions="Center" />
|
||||
</StackLayout>
|
||||
</SwipeItemView>
|
||||
</SwipeItems>
|
||||
</SwipeView.LeftItems>
|
||||
<Grid
|
||||
HeightRequest="70"
|
||||
WidthRequest="300"
|
||||
BackgroundColor="LightGray">
|
||||
<Label
|
||||
Text="What's 2+2?"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center" />
|
||||
</Grid>
|
||||
</SwipeView>
|
||||
</StackLayout>
|
||||
</controls:TestContentPage>
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.Xaml;
|
||||
using System.Windows.Input;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.Queries;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
#if UITEST
|
||||
[Category(UITestCategories.SwipeView)]
|
||||
#endif
|
||||
#if APP
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
#endif
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 8779, "[iOS][Android] Entry in custom SwipeItemView can't gain focus", PlatformAffected.Android | PlatformAffected.iOS)]
|
||||
public partial class Issue8779 : TestContentPage
|
||||
{
|
||||
public Issue8779()
|
||||
{
|
||||
#if APP
|
||||
Title = "Issue 8779";
|
||||
InitializeComponent();
|
||||
|
||||
CheckAnswerCommand = new Command<string>(CheckAnswer);
|
||||
BindingContext = this;
|
||||
#endif
|
||||
}
|
||||
|
||||
public ICommand CheckAnswerCommand { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#if APP
|
||||
async void OnIncorrectAnswerInvoked(object sender, EventArgs e)
|
||||
{
|
||||
((SwipeView)sender).Close();
|
||||
await DisplayAlert("Incorrect!", "Try again.", "OK");
|
||||
}
|
||||
|
||||
async void OnCorrectAnswerInvoked(object sender, EventArgs e)
|
||||
{
|
||||
((SwipeView)sender).Close();
|
||||
await DisplayAlert("Correct!", "The answer is 4.", "OK");
|
||||
}
|
||||
|
||||
void CheckAnswer(string result)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
Int32.TryParse(resultEntry.Text, out int number);
|
||||
|
||||
if (number.Equals(4))
|
||||
OnCorrectAnswerInvoked(swipeView, EventArgs.Empty);
|
||||
else
|
||||
OnIncorrectAnswerInvoked(swipeView, EventArgs.Empty);
|
||||
|
||||
resultEntry.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1257,6 +1257,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Issue8294.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8392.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8672.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8779.xaml.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8806.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8781.xaml.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8782.xaml.cs" />
|
||||
|
@ -1413,6 +1414,7 @@
|
|||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8417.xaml" />
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue7813.xaml" />
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8638.xaml" />
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8779.xaml" />
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8781.xaml" />
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8782.xaml" />
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8326.xaml" />
|
||||
|
|
|
@ -26,37 +26,40 @@
|
|||
Text="Test"
|
||||
BackgroundColor="Red" />
|
||||
<SwipeItemView>
|
||||
<Grid
|
||||
<StackLayout
|
||||
BackgroundColor="LightSteelBlue"
|
||||
WidthRequest="200">
|
||||
<Label
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Text="This is the RightItems Content"
|
||||
Margin="12"/>
|
||||
</Grid>
|
||||
Text="This is the RightItems Content"/>
|
||||
<Button
|
||||
Text="Test Click from RightItems"
|
||||
Clicked="OnRightItemsClicked"/>
|
||||
</StackLayout>
|
||||
</SwipeItemView>
|
||||
</SwipeView.RightItems>
|
||||
<SwipeView.TopItems>
|
||||
<SwipeItemView>
|
||||
<Grid
|
||||
<StackLayout
|
||||
BackgroundColor="LightSkyBlue"
|
||||
HeightRequest="100">
|
||||
<Label
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Text="This is the TopItems Content"
|
||||
Margin="12"/>
|
||||
</Grid>
|
||||
<Button
|
||||
Text="Click me!"
|
||||
Clicked="OnButtonClicked"/>
|
||||
</StackLayout>
|
||||
</SwipeItemView>
|
||||
</SwipeView.TopItems>
|
||||
<Grid
|
||||
BackgroundColor="LightGreen">
|
||||
<Label
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Text="This is the Content"
|
||||
Margin="12"/>
|
||||
<StackLayout>
|
||||
<Label
|
||||
Text="This is the SwipeView Content"/>
|
||||
<Button
|
||||
Text="Test Click from Content"
|
||||
Clicked="OnContentClicked"/>
|
||||
</StackLayout>
|
||||
</Grid>
|
||||
</SwipeView>
|
||||
</Grid>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Xamarin.Forms.Internals;
|
||||
using System;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
|
||||
{
|
||||
|
@ -9,5 +10,21 @@ namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
|
|||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
void OnContentClicked(object sender, EventArgs args)
|
||||
{
|
||||
DisplayAlert("OnClicked", "The Content Button has been clicked.", "Ok");
|
||||
}
|
||||
|
||||
void OnRightItemsClicked(object sender, EventArgs args)
|
||||
{
|
||||
DisplayAlert("OnClicked", "The RightItems Button has been clicked.", "Ok");
|
||||
}
|
||||
|
||||
void OnButtonClicked(object sender, EventArgs e)
|
||||
{
|
||||
DisplayAlert("Custom SwipeItem", "Button Clicked!", "Ok");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -31,6 +31,7 @@
|
|||
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||
<ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj" />
|
||||
<ProjectReference Include="..\Xamarin.Forms.CustomAttributes\Xamarin.Forms.CustomAttributes.csproj" />
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj" />
|
||||
<ProjectReference Include="..\Xamarin.Forms.Maps\Xamarin.Forms.Maps.csproj" />
|
||||
<ProjectReference Include="..\Xamarin.Forms.Xaml\Xamarin.Forms.Xaml.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
@ -174,6 +175,12 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
((AsyncTicker)Ticker.Default).SetEnabled(false);
|
||||
}
|
||||
|
||||
static async Task EnableTicker()
|
||||
{
|
||||
await Task.Delay(32);
|
||||
((AsyncTicker)Ticker.Default).SetEnabled(true);
|
||||
}
|
||||
|
||||
async Task SwapFadeViews(View view1, View view2)
|
||||
{
|
||||
await view1.FadeTo(0, 1000);
|
||||
|
@ -237,5 +244,41 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
|
||||
Assert.That(view.RotationY, Is.EqualTo(200));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AnimationExtensionsReturnTrueIfAnimationsDisabled()
|
||||
{
|
||||
await DisableTicker();
|
||||
|
||||
var label = new Label { Text = "Foo" };
|
||||
var result = await label.ScaleTo(2, 500);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test, Timeout(2000)]
|
||||
public async Task CanExitAnimationLoopIfAnimationsDisabled()
|
||||
{
|
||||
await DisableTicker();
|
||||
|
||||
var run = true;
|
||||
var label = new Label { Text = "Foo" };
|
||||
|
||||
while (run)
|
||||
{
|
||||
await label.ScaleTo(2, 500);
|
||||
run = !(await label.ScaleTo(0.5, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanCheckThatAnimationsAreEnabled()
|
||||
{
|
||||
await EnableTicker();
|
||||
Assert.That(Animation.IsEnabled, Is.True);
|
||||
|
||||
await DisableTicker();
|
||||
Assert.That(Animation.IsEnabled, Is.False);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,5 +140,13 @@ namespace Xamarin.Forms
|
|||
_children.Add(child);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static bool IsEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return Internals.Ticker.Default.SystemEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -239,7 +239,9 @@ namespace Xamarin.Forms
|
|||
var repeat = false;
|
||||
|
||||
// If the Ticker has been disabled (e.g., by power save mode), then don't repeat the animation
|
||||
if (info.Repeat != null && Ticker.Default.SystemEnabled)
|
||||
var animationsEnabled = Ticker.Default.SystemEnabled;
|
||||
|
||||
if (info.Repeat != null && animationsEnabled)
|
||||
repeat = info.Repeat();
|
||||
|
||||
if (!repeat)
|
||||
|
@ -249,7 +251,7 @@ namespace Xamarin.Forms
|
|||
tweener.Finished -= HandleTweenerFinished;
|
||||
}
|
||||
|
||||
info.Finished?.Invoke(tweener.Value, false);
|
||||
info.Finished?.Invoke(tweener.Value, !animationsEnabled);
|
||||
|
||||
if (info.Owner.TryGetTarget(out owner))
|
||||
owner.BatchCommit();
|
||||
|
|
|
@ -32,6 +32,7 @@ using Xamarin.Forms.StyleSheets;
|
|||
[assembly: InternalsVisibleTo("Xamarin.Forms.Pages")]
|
||||
[assembly: InternalsVisibleTo("Xamarin.Forms.Pages.UnitTests")]
|
||||
[assembly: InternalsVisibleTo("Xamarin.Forms.CarouselView")]
|
||||
[assembly: InternalsVisibleTo("Xamarin.Forms.DualScreen")]
|
||||
[assembly: Preserve]
|
||||
|
||||
[assembly: XmlnsDefinition("http://xamarin.com/schemas/2014/forms", "Xamarin.Forms")]
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen.UnitTests
|
||||
{
|
||||
public class BaseTestFixture
|
||||
{
|
||||
CultureInfo _defaultCulture;
|
||||
CultureInfo _defaultUICulture;
|
||||
|
||||
[SetUp]
|
||||
public virtual void Setup()
|
||||
{
|
||||
_defaultCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
|
||||
_defaultUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
Device.PlatformServices = null;
|
||||
System.Threading.Thread.CurrentThread.CurrentCulture = _defaultCulture;
|
||||
System.Threading.Thread.CurrentThread.CurrentUICulture = _defaultUICulture;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Collections.Generic;
|
||||
using Xamarin.Forms;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using FileMode = System.IO.FileMode;
|
||||
using FileAccess = System.IO.FileAccess;
|
||||
using FileShare = System.IO.FileShare;
|
||||
using Stream = System.IO.Stream;
|
||||
using Xamarin.Forms.DualScreen.UnitTests;
|
||||
|
||||
[assembly: Dependency(typeof(MockDeserializer))]
|
||||
[assembly: Dependency(typeof(MockResourcesProvider))]
|
||||
|
||||
namespace Xamarin.Forms.DualScreen.UnitTests
|
||||
{
|
||||
internal class MockPlatformServices : Internals.IPlatformServices
|
||||
{
|
||||
Action<Action> invokeOnMainThread;
|
||||
Action<Uri> openUriAction;
|
||||
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync;
|
||||
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc;
|
||||
readonly bool useRealisticLabelMeasure;
|
||||
readonly bool _isInvokeRequired;
|
||||
|
||||
public MockPlatformServices(Action<Action> invokeOnMainThread = null, Action<Uri> openUriAction = null,
|
||||
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync = null,
|
||||
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc = null,
|
||||
bool useRealisticLabelMeasure = false, bool isInvokeRequired = false)
|
||||
{
|
||||
this.invokeOnMainThread = invokeOnMainThread;
|
||||
this.openUriAction = openUriAction;
|
||||
this.getStreamAsync = getStreamAsync;
|
||||
this.getNativeSizeFunc = getNativeSizeFunc;
|
||||
this.useRealisticLabelMeasure = useRealisticLabelMeasure;
|
||||
_isInvokeRequired = isInvokeRequired;
|
||||
}
|
||||
|
||||
static MD5CryptoServiceProvider checksum = new MD5CryptoServiceProvider();
|
||||
|
||||
public string GetMD5Hash(string input)
|
||||
{
|
||||
var bytes = checksum.ComputeHash(Encoding.UTF8.GetBytes(input));
|
||||
var ret = new char[32];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ret[i * 2] = (char)hex(bytes[i] >> 4);
|
||||
ret[i * 2 + 1] = (char)hex(bytes[i] & 0xf);
|
||||
}
|
||||
return new string(ret);
|
||||
}
|
||||
static int hex(int v)
|
||||
{
|
||||
if (v < 10)
|
||||
return '0' + v;
|
||||
return 'a' + v - 10;
|
||||
}
|
||||
|
||||
public double GetNamedSize(NamedSize size, Type targetElement, bool useOldSizes)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case NamedSize.Default:
|
||||
return 10;
|
||||
case NamedSize.Micro:
|
||||
return 4;
|
||||
case NamedSize.Small:
|
||||
return 8;
|
||||
case NamedSize.Medium:
|
||||
return 12;
|
||||
case NamedSize.Large:
|
||||
return 16;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenUriAction(Uri uri)
|
||||
{
|
||||
if (openUriAction != null)
|
||||
openUriAction(uri);
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsInvokeRequired
|
||||
{
|
||||
get { return _isInvokeRequired; }
|
||||
}
|
||||
|
||||
public string RuntimePlatform { get; set; }
|
||||
|
||||
public void BeginInvokeOnMainThread(Action action)
|
||||
{
|
||||
if (invokeOnMainThread == null)
|
||||
action();
|
||||
else
|
||||
invokeOnMainThread(action);
|
||||
}
|
||||
|
||||
public Internals.Ticker CreateTicker()
|
||||
{
|
||||
return new MockTicker();
|
||||
}
|
||||
|
||||
public void StartTimer(TimeSpan interval, Func<bool> callback)
|
||||
{
|
||||
Timer timer = null;
|
||||
TimerCallback onTimeout = o => BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
if (callback())
|
||||
return;
|
||||
|
||||
timer.Dispose();
|
||||
});
|
||||
timer = new Timer(onTimeout, null, interval, interval);
|
||||
}
|
||||
|
||||
public Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
if (getStreamAsync == null)
|
||||
throw new NotImplementedException();
|
||||
return getStreamAsync(uri, cancellationToken);
|
||||
}
|
||||
|
||||
public Assembly[] GetAssemblies()
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies();
|
||||
}
|
||||
|
||||
public Internals.IIsolatedStorageFile GetUserStoreForApplication()
|
||||
{
|
||||
return new MockIsolatedStorageFile(IsolatedStorageFile.GetUserStoreForAssembly());
|
||||
}
|
||||
|
||||
public class MockIsolatedStorageFile : Internals.IIsolatedStorageFile
|
||||
{
|
||||
readonly IsolatedStorageFile isolatedStorageFile;
|
||||
public MockIsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
|
||||
{
|
||||
this.isolatedStorageFile = isolatedStorageFile;
|
||||
}
|
||||
|
||||
public Task<bool> GetDirectoryExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.DirectoryExists(path));
|
||||
}
|
||||
|
||||
public Task CreateDirectoryAsync(string path)
|
||||
{
|
||||
isolatedStorageFile.CreateDirectory(path);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
|
||||
{
|
||||
Stream stream = isolatedStorageFile.OpenFile(path, mode, access);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
Stream stream = isolatedStorageFile.OpenFile(path, mode, access, share);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<bool> GetFileExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.FileExists(path));
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.GetLastWriteTime(path));
|
||||
}
|
||||
}
|
||||
|
||||
public void QuitApplication()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SizeRequest GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
|
||||
{
|
||||
if (getNativeSizeFunc != null)
|
||||
return getNativeSizeFunc(view, widthConstraint, heightConstraint);
|
||||
// EVERYTHING IS 100 x 20
|
||||
|
||||
var label = view as Label;
|
||||
if (label != null && useRealisticLabelMeasure)
|
||||
{
|
||||
var letterSize = new Size(5, 10);
|
||||
var w = label.Text.Length * letterSize.Width;
|
||||
var h = letterSize.Height;
|
||||
if (!double.IsPositiveInfinity(widthConstraint) && w > widthConstraint)
|
||||
{
|
||||
h = ((int)w / (int)widthConstraint) * letterSize.Height;
|
||||
w = widthConstraint - (widthConstraint % letterSize.Width);
|
||||
|
||||
}
|
||||
return new SizeRequest(new Size(w, h), new Size(Math.Min(10, w), h));
|
||||
}
|
||||
|
||||
return new SizeRequest(new Size(100, 20));
|
||||
}
|
||||
}
|
||||
|
||||
internal class MockDeserializer : Internals.IDeserializer
|
||||
{
|
||||
public Task<IDictionary<string, object>> DeserializePropertiesAsync()
|
||||
{
|
||||
return Task.FromResult<IDictionary<string, object>>(new Dictionary<string, object>());
|
||||
}
|
||||
|
||||
public Task SerializePropertiesAsync(IDictionary<string, object> properties)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal class MockResourcesProvider : Internals.ISystemResourcesProvider
|
||||
{
|
||||
public Internals.IResourceDictionary GetSystemResources()
|
||||
{
|
||||
var dictionary = new ResourceDictionary();
|
||||
Style style;
|
||||
style = new Style(typeof(Label));
|
||||
dictionary[Device.Styles.BodyStyleKey] = style;
|
||||
|
||||
style = new Style(typeof(Label));
|
||||
style.Setters.Add(Label.FontSizeProperty, 50);
|
||||
dictionary[Device.Styles.TitleStyleKey] = style;
|
||||
|
||||
style = new Style(typeof(Label));
|
||||
style.Setters.Add(Label.FontSizeProperty, 40);
|
||||
dictionary[Device.Styles.SubtitleStyleKey] = style;
|
||||
|
||||
style = new Style(typeof(Label));
|
||||
style.Setters.Add(Label.FontSizeProperty, 30);
|
||||
dictionary[Device.Styles.CaptionStyleKey] = style;
|
||||
|
||||
style = new Style(typeof(Label));
|
||||
style.Setters.Add(Label.FontSizeProperty, 20);
|
||||
dictionary[Device.Styles.ListItemTextStyleKey] = style;
|
||||
|
||||
style = new Style(typeof(Label));
|
||||
style.Setters.Add(Label.FontSizeProperty, 10);
|
||||
dictionary[Device.Styles.ListItemDetailTextStyleKey] = style;
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
|
||||
public class MockApplication : Application
|
||||
{
|
||||
public MockApplication()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class MockTicker : Internals.Ticker
|
||||
{
|
||||
bool _enabled;
|
||||
|
||||
protected override void EnableTimer()
|
||||
{
|
||||
_enabled = true;
|
||||
|
||||
while (_enabled)
|
||||
{
|
||||
SendSignals(16);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisableTimer()
|
||||
{
|
||||
_enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TwoPaneViewTests : BaseTestFixture
|
||||
{
|
||||
[SetUp]
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
Device.info = new TestDeviceInfo();
|
||||
}
|
||||
|
||||
TwoPaneView CreateTwoPaneView(View pane1 = null, View pane2 = null)
|
||||
{
|
||||
TwoPaneView view = new TwoPaneView()
|
||||
{
|
||||
IsPlatformEnabled = true,
|
||||
Pane1 = pane1,
|
||||
Pane2 = pane2
|
||||
};
|
||||
|
||||
if (pane1 != null)
|
||||
pane1.IsPlatformEnabled = true;
|
||||
|
||||
if (pane2 != null)
|
||||
pane2.IsPlatformEnabled = true;
|
||||
|
||||
view.Children[0].IsPlatformEnabled = true;
|
||||
view.Children[1].IsPlatformEnabled = true;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GettersAndSetters()
|
||||
{
|
||||
var Pane1 = new StackLayout();
|
||||
var Pane2 = new Grid();
|
||||
|
||||
TwoPaneView twoPaneView = new TwoPaneView()
|
||||
{
|
||||
TallModeConfiguration = TwoPaneViewTallModeConfiguration.SinglePane,
|
||||
WideModeConfiguration = TwoPaneViewWideModeConfiguration.SinglePane,
|
||||
Pane1 = Pane1,
|
||||
Pane2 = Pane2,
|
||||
PanePriority = TwoPaneViewPriority.Pane2,
|
||||
MinTallModeHeight = 1000,
|
||||
MinWideModeWidth = 2000,
|
||||
};
|
||||
|
||||
Assert.AreEqual(TwoPaneViewTallModeConfiguration.SinglePane, twoPaneView.TallModeConfiguration);
|
||||
Assert.AreEqual(TwoPaneViewWideModeConfiguration.SinglePane, twoPaneView.WideModeConfiguration);
|
||||
Assert.AreEqual(Pane1, twoPaneView.Pane1);
|
||||
Assert.AreEqual(Pane2, twoPaneView.Pane2);
|
||||
Assert.AreEqual(TwoPaneViewPriority.Pane2, twoPaneView.PanePriority);
|
||||
Assert.AreEqual(1000, twoPaneView.MinTallModeHeight);
|
||||
Assert.AreEqual(2000, twoPaneView.MinWideModeWidth);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BasicLayoutTest()
|
||||
{
|
||||
TwoPaneView twoPaneView = new TwoPaneView();
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
|
||||
Assert.AreEqual(300, twoPaneView.Height);
|
||||
Assert.AreEqual(300, twoPaneView.Width);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModeSwitchesWithMinWideModeWidth()
|
||||
{
|
||||
TwoPaneView twoPaneView = new TwoPaneView();
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
|
||||
twoPaneView.MinWideModeWidth = 400;
|
||||
Assert.AreEqual(TwoPaneViewMode.SinglePane, twoPaneView.Mode);
|
||||
twoPaneView.MinWideModeWidth = 100;
|
||||
Assert.AreEqual(TwoPaneViewMode.Wide, twoPaneView.Mode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModeSwitchesWithMinTallModeHeight()
|
||||
{
|
||||
TwoPaneView twoPaneView = new TwoPaneView();
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
|
||||
twoPaneView.MinTallModeHeight = 400;
|
||||
Assert.AreEqual(TwoPaneViewMode.SinglePane, twoPaneView.Mode);
|
||||
twoPaneView.MinTallModeHeight = 100;
|
||||
Assert.AreEqual(TwoPaneViewMode.Tall, twoPaneView.Mode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Pane1LengthTallMode()
|
||||
{
|
||||
var pane1 = new BoxView();
|
||||
var pane2 = new BoxView();
|
||||
|
||||
TwoPaneView twoPaneView = CreateTwoPaneView(pane1, pane2);
|
||||
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
twoPaneView.MinTallModeHeight = 100;
|
||||
Assert.AreNotEqual(100, twoPaneView.Pane1.Height);
|
||||
twoPaneView.Pane1Length = 100;
|
||||
Assert.AreEqual(100, twoPaneView.Pane1.Height);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Pane1LengthWideMode()
|
||||
{
|
||||
var pane1 = new BoxView();
|
||||
var pane2 = new BoxView();
|
||||
|
||||
TwoPaneView twoPaneView = CreateTwoPaneView(pane1, pane2);
|
||||
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
twoPaneView.MinWideModeWidth = 100;
|
||||
Assert.AreNotEqual(100, twoPaneView.Pane1.Width);
|
||||
twoPaneView.Pane1Length = 100;
|
||||
Assert.AreEqual(100, twoPaneView.Pane1.Width);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Pane2LengthTallMode()
|
||||
{
|
||||
var pane1 = new BoxView();
|
||||
var pane2 = new BoxView();
|
||||
|
||||
TwoPaneView twoPaneView = CreateTwoPaneView(pane1, pane2);
|
||||
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
twoPaneView.MinTallModeHeight = 100;
|
||||
Assert.AreNotEqual(100, twoPaneView.Pane2.Height);
|
||||
twoPaneView.Pane2Length = 100;
|
||||
Assert.AreEqual(100, twoPaneView.Pane2.Height);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Pane2LengthWideMode()
|
||||
{
|
||||
var pane1 = new BoxView();
|
||||
var pane2 = new BoxView();
|
||||
|
||||
TwoPaneView twoPaneView = CreateTwoPaneView(pane1, pane2);
|
||||
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
twoPaneView.MinWideModeWidth = 100;
|
||||
Assert.AreNotEqual(100, twoPaneView.Pane2.Width);
|
||||
twoPaneView.Pane2Length = 100;
|
||||
Assert.AreEqual(100, twoPaneView.Pane2.Width);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void PanePriority()
|
||||
{
|
||||
var pane1 = new BoxView();
|
||||
var pane2 = new BoxView();
|
||||
|
||||
TwoPaneView twoPaneView = CreateTwoPaneView(pane1, pane2);
|
||||
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
twoPaneView.MinWideModeWidth = 500;
|
||||
twoPaneView.MinTallModeHeight = 500;
|
||||
|
||||
Assert.IsFalse(twoPaneView.Children[1].IsVisible);
|
||||
Assert.IsTrue(twoPaneView.Children[0].IsVisible);
|
||||
|
||||
Assert.AreEqual(pane1.Height, 300);
|
||||
Assert.AreEqual(pane1.Width, 300);
|
||||
|
||||
twoPaneView.PanePriority = TwoPaneViewPriority.Pane2;
|
||||
|
||||
Assert.AreEqual(pane2.Height, 300);
|
||||
Assert.AreEqual(pane2.Width, 300);
|
||||
|
||||
Assert.IsFalse(twoPaneView.Children[0].IsVisible);
|
||||
Assert.IsTrue(twoPaneView.Children[1].IsVisible);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TallModeConfiguration()
|
||||
{
|
||||
var pane1 = new BoxView();
|
||||
var pane2 = new BoxView();
|
||||
TwoPaneView twoPaneView = CreateTwoPaneView(pane1, pane2);
|
||||
twoPaneView.MinTallModeHeight = 100;
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
|
||||
Assert.AreEqual(0, twoPaneView.Children[0].Bounds.Y);
|
||||
Assert.AreEqual(150, twoPaneView.Children[1].Bounds.Y);
|
||||
|
||||
twoPaneView.TallModeConfiguration = TwoPaneViewTallModeConfiguration.BottomTop;
|
||||
|
||||
Assert.AreEqual(150, twoPaneView.Children[0].Bounds.Y);
|
||||
Assert.AreEqual(0, twoPaneView.Children[1].Bounds.Y);
|
||||
|
||||
twoPaneView.TallModeConfiguration = TwoPaneViewTallModeConfiguration.SinglePane;
|
||||
|
||||
Assert.IsTrue(twoPaneView.Children[0].IsVisible);
|
||||
Assert.IsFalse(twoPaneView.Children[1].IsVisible);
|
||||
|
||||
twoPaneView.PanePriority = TwoPaneViewPriority.Pane2;
|
||||
|
||||
Assert.IsFalse(twoPaneView.Children[0].IsVisible);
|
||||
Assert.IsTrue(twoPaneView.Children[1].IsVisible);
|
||||
|
||||
twoPaneView.PanePriority = TwoPaneViewPriority.Pane1;
|
||||
|
||||
Assert.IsTrue(twoPaneView.Children[0].IsVisible);
|
||||
Assert.IsFalse(twoPaneView.Children[1].IsVisible);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WideModeConfiguration()
|
||||
{
|
||||
var pane1 = new BoxView();
|
||||
var pane2 = new BoxView();
|
||||
TwoPaneView twoPaneView = CreateTwoPaneView(pane1, pane2);
|
||||
twoPaneView.MinWideModeWidth = 100;
|
||||
twoPaneView.Layout(new Rectangle(0, 0, 300, 300));
|
||||
|
||||
Assert.AreEqual(0, twoPaneView.Children[0].Bounds.X);
|
||||
Assert.AreEqual(150, twoPaneView.Children[1].Bounds.X);
|
||||
|
||||
twoPaneView.WideModeConfiguration = TwoPaneViewWideModeConfiguration.RightLeft;
|
||||
|
||||
Assert.AreEqual(150, twoPaneView.Children[0].Bounds.X);
|
||||
Assert.AreEqual(0, twoPaneView.Children[1].Bounds.X);
|
||||
|
||||
twoPaneView.WideModeConfiguration = TwoPaneViewWideModeConfiguration.SinglePane;
|
||||
|
||||
Assert.IsTrue(twoPaneView.Children[0].IsVisible);
|
||||
Assert.IsFalse(twoPaneView.Children[1].IsVisible);
|
||||
|
||||
twoPaneView.PanePriority = TwoPaneViewPriority.Pane2;
|
||||
|
||||
Assert.IsFalse(twoPaneView.Children[0].IsVisible);
|
||||
Assert.IsTrue(twoPaneView.Children[1].IsVisible);
|
||||
|
||||
twoPaneView.PanePriority = TwoPaneViewPriority.Pane1;
|
||||
|
||||
Assert.IsTrue(twoPaneView.Children[0].IsVisible);
|
||||
Assert.IsFalse(twoPaneView.Children[1].IsVisible);
|
||||
}
|
||||
|
||||
|
||||
internal class TestDeviceInfo : DeviceInfo
|
||||
{
|
||||
public TestDeviceInfo()
|
||||
{
|
||||
CurrentOrientation = DeviceOrientation.Portrait;
|
||||
}
|
||||
public override Size PixelScreenSize
|
||||
{
|
||||
get { return new Size(1000, 2000); }
|
||||
}
|
||||
|
||||
public override Size ScaledScreenSize
|
||||
{
|
||||
get { return new Size(500, 1000); }
|
||||
}
|
||||
|
||||
public override double ScalingFactor
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net47;</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter">
|
||||
<Version>3.16.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj" />
|
||||
<ProjectReference Include="..\Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<Project>
|
||||
<Import Project="../Directory.Build.props" />
|
||||
<Import Project="../UWP.Build.props" />
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
<Project>
|
||||
<Import Project="../Directory.Build.targets" />
|
||||
<Import Project="../UWP.Build.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
public class DualScreenInfo : INotifyPropertyChanged
|
||||
{
|
||||
static Lazy<DualScreenInfo> _dualScreenInfo { get; } = new Lazy<DualScreenInfo>(OnCreate);
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
Rectangle[] _spanningBounds;
|
||||
Rectangle _hingeBounds;
|
||||
bool _isLandscape;
|
||||
TwoPaneViewMode _spanMode;
|
||||
|
||||
public static DualScreenInfo Current => _dualScreenInfo.Value;
|
||||
public Rectangle[] SpanningBounds
|
||||
{
|
||||
get => GetSpanningBounds();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _spanningBounds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle HingeBounds
|
||||
{
|
||||
get => GetHingeBounds();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _hingeBounds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLandscape
|
||||
{
|
||||
get => GetIsLandscape();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _isLandscape, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TwoPaneViewMode SpanMode
|
||||
{
|
||||
get => GetSpanMode();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _spanMode, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle[] GetSpanningBounds()
|
||||
{
|
||||
var guide = TwoPaneViewLayoutGuide.Instance;
|
||||
var hinge = guide.Hinge;
|
||||
guide.UpdateLayouts();
|
||||
|
||||
if (hinge == Rectangle.Zero)
|
||||
return new Rectangle[0];
|
||||
|
||||
if (guide.Pane2 == Rectangle.Zero)
|
||||
return new Rectangle[0];
|
||||
|
||||
return new[] { guide.Pane1, guide.Pane2 };
|
||||
}
|
||||
|
||||
Rectangle GetHingeBounds()
|
||||
{
|
||||
var guide = TwoPaneViewLayoutGuide.Instance;
|
||||
guide.UpdateLayouts();
|
||||
return guide.Hinge;
|
||||
}
|
||||
|
||||
bool GetIsLandscape() => TwoPaneViewLayoutGuide.Instance.IsLandscape;
|
||||
|
||||
TwoPaneViewMode GetSpanMode() => TwoPaneViewLayoutGuide.Instance.Mode;
|
||||
|
||||
static DualScreenInfo OnCreate()
|
||||
{
|
||||
DualScreenInfo dualScreenInfo = new DualScreenInfo();
|
||||
TwoPaneViewLayoutGuide.Instance.PropertyChanged += dualScreenInfo.OnTwoPaneViewLayoutGuideChanged;
|
||||
return dualScreenInfo;
|
||||
}
|
||||
|
||||
void OnTwoPaneViewLayoutGuideChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
SpanningBounds = GetSpanningBounds();
|
||||
IsLandscape = GetIsLandscape();
|
||||
HingeBounds = GetHingeBounds();
|
||||
SpanMode = GetSpanMode();
|
||||
}
|
||||
|
||||
protected bool SetProperty<T>(ref T backingStore, T value,
|
||||
[CallerMemberName]string propertyName = "",
|
||||
Action onChanged = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(backingStore, value))
|
||||
return false;
|
||||
|
||||
backingStore = value;
|
||||
onChanged?.Invoke();
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Views;
|
||||
using Microsoft.Device.Display;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.DualScreen;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
|
||||
[assembly: Dependency(typeof(DualScreenService.DualScreenServiceImpl))]
|
||||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
public class DualScreenService
|
||||
{
|
||||
public static void Init(Activity activity)
|
||||
{
|
||||
DependencyService.Register<DualScreenServiceImpl>();
|
||||
DualScreenServiceImpl.Init(activity);
|
||||
}
|
||||
|
||||
internal class DualScreenServiceImpl : IDualScreenService, IDisposable
|
||||
{
|
||||
ScreenHelper _helper;
|
||||
bool _isDuo = false;
|
||||
HingeSensor _hingeSensor;
|
||||
static Activity _mainActivity;
|
||||
static DualScreenServiceImpl _HingeService;
|
||||
|
||||
int _hingeAngle;
|
||||
Rectangle _hingeLocation;
|
||||
|
||||
Activity MainActivity
|
||||
{
|
||||
get => _mainActivity;
|
||||
set => _mainActivity = value;
|
||||
}
|
||||
|
||||
public DualScreenServiceImpl()
|
||||
{
|
||||
_HingeService = this;
|
||||
|
||||
if (_mainActivity != null)
|
||||
Init(_mainActivity);
|
||||
}
|
||||
|
||||
public static void Init(Activity activity)
|
||||
{
|
||||
if (_HingeService == null)
|
||||
{
|
||||
_mainActivity = activity;
|
||||
return;
|
||||
}
|
||||
|
||||
if (activity == _HingeService.MainActivity && _HingeService._helper != null)
|
||||
return;
|
||||
|
||||
_mainActivity = activity;
|
||||
if (_HingeService._helper == null)
|
||||
_HingeService._helper = new ScreenHelper();
|
||||
|
||||
if (_HingeService._hingeSensor != null)
|
||||
{
|
||||
_HingeService._hingeSensor.OnSensorChanged -= _HingeService.OnSensorChanged;
|
||||
_HingeService._hingeSensor.StopListening();
|
||||
}
|
||||
|
||||
_HingeService._isDuo = _HingeService._helper.Initialize(_HingeService.MainActivity);
|
||||
|
||||
if (_HingeService._isDuo)
|
||||
{
|
||||
_HingeService._hingeSensor = new HingeSensor(_HingeService.MainActivity);
|
||||
_HingeService._hingeSensor.OnSensorChanged += _HingeService.OnSensorChanged;
|
||||
_HingeService._hingeSensor.StartListening();
|
||||
}
|
||||
}
|
||||
|
||||
void OnSensorChanged(object sender, HingeSensor.HingeSensorChangedEventArgs e)
|
||||
{
|
||||
if (_hingeLocation != GetHinge())
|
||||
{
|
||||
_hingeLocation = GetHinge();
|
||||
}
|
||||
|
||||
if (_hingeAngle != e.HingeAngle)
|
||||
OnScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
_hingeAngle = e.HingeAngle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_hingeSensor != null)
|
||||
{
|
||||
_hingeSensor.OnSensorChanged -= OnSensorChanged;
|
||||
_hingeSensor.StopListening();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSpanned
|
||||
=> _isDuo && (_helper?.IsDualMode ?? false);
|
||||
|
||||
public Rectangle GetHinge()
|
||||
{
|
||||
if (!_isDuo || _helper == null)
|
||||
return Rectangle.Zero;
|
||||
|
||||
var rotation = ScreenHelper.GetRotation(_helper.Activity);
|
||||
var hinge = _helper.DisplayMask.GetBoundingRectsForRotation(rotation).FirstOrDefault();
|
||||
var hingeDp = new Rectangle(PixelsToDp(hinge.Left), PixelsToDp(hinge.Top), PixelsToDp(hinge.Width()), PixelsToDp(hinge.Height()));
|
||||
|
||||
return hingeDp;
|
||||
}
|
||||
|
||||
public bool IsLandscape
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_isDuo || _helper == null)
|
||||
return false;
|
||||
|
||||
var rotation = ScreenHelper.GetRotation(_helper.Activity);
|
||||
|
||||
return (rotation == SurfaceOrientation.Rotation270 || rotation == SurfaceOrientation.Rotation90);
|
||||
}
|
||||
}
|
||||
|
||||
double PixelsToDp(double px)
|
||||
=> px / MainActivity.Resources.DisplayMetrics.Density;
|
||||
|
||||
public event EventHandler OnScreenChanged;
|
||||
|
||||
|
||||
public Point? GetLocationOnScreen(VisualElement visualElement)
|
||||
{
|
||||
var view = Platform.Android.Platform.GetRenderer(visualElement);
|
||||
|
||||
if (view?.View == null)
|
||||
return null;
|
||||
|
||||
int[] location = new int[2];
|
||||
view.View.GetLocationOnScreen(location);
|
||||
return new Point(view.View.Context.FromPixels(location[0]), view.View.Context.FromPixels(location[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Display;
|
||||
using Windows.UI.ViewManagement;
|
||||
|
||||
#if UWP_18362
|
||||
using Windows.UI.WindowManagement;
|
||||
#endif
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Hosting;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.DualScreen;
|
||||
using Xamarin.Forms.Platform.UWP;
|
||||
|
||||
[assembly: Dependency(typeof(DualScreenService))]
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
internal partial class DualScreenService : IDualScreenService
|
||||
{
|
||||
#pragma warning disable CS0067
|
||||
public event EventHandler OnScreenChanged;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public DualScreenService()
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsSpanned
|
||||
{
|
||||
get
|
||||
{
|
||||
var visibleBounds = ApplicationView.GetForCurrentView().VisibleBounds;
|
||||
|
||||
if (visibleBounds.Height > 1200 || visibleBounds.Width > 1200)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLandscape
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsSpanned)
|
||||
return ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Portrait;
|
||||
else
|
||||
return ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Landscape;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public Rectangle GetHinge()
|
||||
{
|
||||
var screen = DisplayInformation.GetForCurrentView();
|
||||
|
||||
if (IsLandscape)
|
||||
{
|
||||
if (IsSpanned)
|
||||
return new Rectangle(0, 664 + 24, ScaledPixels(screen.ScreenWidthInRawPixels), 0);
|
||||
else
|
||||
return new Rectangle(0, 664, ScaledPixels(screen.ScreenWidthInRawPixels), 0);
|
||||
}
|
||||
else
|
||||
return new Rectangle(720, 0, 0, ScaledPixels(screen.ScreenHeightInRawPixels));
|
||||
}
|
||||
|
||||
double ScaledPixels(double n)
|
||||
=> n / DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel;
|
||||
|
||||
public Point? GetLocationOnScreen(VisualElement visualElement)
|
||||
{
|
||||
var view = Platform.UWP.Platform.GetRenderer(visualElement);
|
||||
|
||||
if (view?.ContainerElement == null)
|
||||
return null;
|
||||
|
||||
var ttv = view.ContainerElement.TransformToVisual(Window.Current.Content);
|
||||
Windows.Foundation.Point screenCoords = ttv.TransformPoint(new Windows.Foundation.Point(0, 0));
|
||||
|
||||
return new Point(screenCoords.X, screenCoords.Y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
internal interface IDualScreenService : IDisposable
|
||||
{
|
||||
event EventHandler OnScreenChanged;
|
||||
bool IsSpanned { get; }
|
||||
bool IsLandscape { get; }
|
||||
Rectangle GetHinge();
|
||||
Point? GetLocationOnScreen(VisualElement visualElement);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
internal class NoDualScreenServiceImpl : IDualScreenService
|
||||
{
|
||||
static Lazy<NoDualScreenServiceImpl> _Instance = new Lazy<NoDualScreenServiceImpl>(() => new NoDualScreenServiceImpl());
|
||||
public static NoDualScreenServiceImpl Instance => _Instance.Value;
|
||||
|
||||
public NoDualScreenServiceImpl()
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsSpanned => false;
|
||||
|
||||
public bool IsLandscape => Device.info.CurrentOrientation.IsLandscape();
|
||||
|
||||
public event EventHandler OnScreenChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
|
||||
}
|
||||
remove
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public Rectangle GetHinge()
|
||||
{
|
||||
return Rectangle.Zero;
|
||||
}
|
||||
|
||||
public Point? GetLocationOnScreen(VisualElement visualElement)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,471 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
public enum TwoPaneViewMode
|
||||
{
|
||||
SinglePane, Wide, Tall
|
||||
}
|
||||
|
||||
public enum TwoPaneViewTallModeConfiguration
|
||||
{
|
||||
SinglePane,
|
||||
TopBottom,
|
||||
BottomTop,
|
||||
}
|
||||
|
||||
public enum TwoPaneViewWideModeConfiguration
|
||||
{
|
||||
SinglePane,
|
||||
LeftRight,
|
||||
RightLeft,
|
||||
}
|
||||
|
||||
public enum TwoPaneViewPriority
|
||||
{
|
||||
Pane1,
|
||||
Pane2
|
||||
}
|
||||
|
||||
[ContentProperty("")]
|
||||
public partial class TwoPaneView : Grid
|
||||
{
|
||||
|
||||
static TwoPaneView()
|
||||
{
|
||||
#if UWP
|
||||
DependencyService.Register<DualScreenService>();
|
||||
#elif !ANDROID
|
||||
DependencyService.Register<NoDualScreenServiceImpl>();
|
||||
#endif
|
||||
}
|
||||
|
||||
enum ViewMode
|
||||
{
|
||||
Pane1Only,
|
||||
Pane2Only,
|
||||
LeftRight,
|
||||
RightLeft,
|
||||
TopBottom,
|
||||
BottomTop,
|
||||
None
|
||||
};
|
||||
|
||||
TwoPaneViewLayoutGuide _twoPaneViewLayoutGuide;
|
||||
VisualStateGroup _modeStates;
|
||||
ContentView _content1;
|
||||
ContentView _content2;
|
||||
ViewMode _currentMode;
|
||||
bool _hasMeasured = false;
|
||||
bool _updatingMode = false;
|
||||
bool _performingLayout = false;
|
||||
bool _processPendingChange = false;
|
||||
|
||||
public static readonly BindableProperty TallModeConfigurationProperty
|
||||
= BindableProperty.Create("TallModeConfiguration", typeof(TwoPaneViewTallModeConfiguration), typeof(TwoPaneView), defaultValue: TwoPaneViewTallModeConfiguration.TopBottom, propertyChanged: OnJustInvalidateLayout);
|
||||
|
||||
public static readonly BindableProperty WideModeConfigurationProperty
|
||||
= BindableProperty.Create("WideModeConfiguration", typeof(TwoPaneViewWideModeConfiguration), typeof(TwoPaneView), defaultValue: TwoPaneViewWideModeConfiguration.LeftRight, propertyChanged: OnJustInvalidateLayout);
|
||||
|
||||
public static readonly BindableProperty Pane1Property
|
||||
= BindableProperty.Create("Pane1", typeof(View), typeof(TwoPaneView), propertyChanged: (b, o, n) => OnPanePropertyChanged(b, o, n, 0));
|
||||
|
||||
public static readonly BindableProperty Pane2Property
|
||||
= BindableProperty.Create("Pane2", typeof(View), typeof(TwoPaneView), propertyChanged: (b, o, n) => OnPanePropertyChanged(b, o, n, 1));
|
||||
|
||||
public static readonly BindablePropertyKey ModePropertyKey
|
||||
= BindableProperty.CreateReadOnly("Mode", typeof(TwoPaneViewMode), typeof(TwoPaneView), defaultValue: TwoPaneViewMode.SinglePane, propertyChanged: OnModePropertyChanged);
|
||||
|
||||
public static readonly BindableProperty ModeProperty = ModePropertyKey.BindableProperty;
|
||||
|
||||
public static readonly BindableProperty PanePriorityProperty
|
||||
= BindableProperty.Create("PanePriority", typeof(TwoPaneViewPriority), typeof(TwoPaneView), defaultValue: TwoPaneViewPriority.Pane1, propertyChanged: OnJustInvalidateLayout);
|
||||
|
||||
public static readonly BindableProperty MinTallModeHeightProperty
|
||||
= BindableProperty.Create("MinTallModeHeight", typeof(double), typeof(TwoPaneView), defaultValueCreator:OnMinModePropertyCreate, propertyChanged: OnJustInvalidateLayout);
|
||||
|
||||
public static readonly BindableProperty MinWideModeWidthProperty
|
||||
= BindableProperty.Create("MinWideModeWidth", typeof(double), typeof(TwoPaneView), defaultValueCreator: OnMinModePropertyCreate, propertyChanged: OnJustInvalidateLayout);
|
||||
|
||||
public static readonly BindableProperty Pane1LengthProperty
|
||||
= BindableProperty.Create("Pane1Length", typeof(GridLength), typeof(TwoPaneView), defaultValue: GridLength.Star, propertyChanged: OnJustInvalidateLayout);
|
||||
|
||||
public static readonly BindableProperty Pane2LengthProperty
|
||||
= BindableProperty.Create("Pane2Length", typeof(GridLength), typeof(TwoPaneView), defaultValue: GridLength.Star, propertyChanged: OnJustInvalidateLayout);
|
||||
|
||||
public event EventHandler ModeChanged;
|
||||
|
||||
static object OnMinModePropertyCreate(BindableObject bindable)
|
||||
{
|
||||
if (Device.info == null)
|
||||
return 641;
|
||||
|
||||
return 641 / Device.info.ScalingFactor;
|
||||
}
|
||||
|
||||
|
||||
static void OnModePropertyChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
((TwoPaneView)bindable).ModeChanged?.Invoke(bindable, EventArgs.Empty);
|
||||
}
|
||||
|
||||
static void OnJustInvalidateLayout(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var b = (TwoPaneView)bindable;
|
||||
if (!b._performingLayout && !b._updatingMode)
|
||||
{
|
||||
b.UpdateMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
b._processPendingChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPanePropertyChanged(BindableObject bindable, object oldValue, object newValue, int paneIndex)
|
||||
{
|
||||
TwoPaneView twoPaneView = (TwoPaneView)bindable;
|
||||
var newView = (View)newValue;
|
||||
|
||||
if (paneIndex == 0)
|
||||
twoPaneView._content1.Content = newView;
|
||||
else
|
||||
twoPaneView._content2.Content = newView;
|
||||
|
||||
OnJustInvalidateLayout(bindable, null, null);
|
||||
}
|
||||
|
||||
public double MinTallModeHeight
|
||||
{
|
||||
get { return (double)GetValue(MinTallModeHeightProperty); }
|
||||
set { SetValue(MinTallModeHeightProperty, value); }
|
||||
}
|
||||
public double MinWideModeWidth
|
||||
{
|
||||
get { return (double)GetValue(MinWideModeWidthProperty); }
|
||||
set { SetValue(MinWideModeWidthProperty, value); }
|
||||
}
|
||||
|
||||
public GridLength Pane1Length
|
||||
{
|
||||
get { return (GridLength)GetValue(Pane1LengthProperty); }
|
||||
set { SetValue(Pane1LengthProperty, value); }
|
||||
}
|
||||
public GridLength Pane2Length
|
||||
{
|
||||
get { return (GridLength)GetValue(Pane2LengthProperty); }
|
||||
set { SetValue(Pane2LengthProperty, value); }
|
||||
}
|
||||
|
||||
public TwoPaneViewMode Mode { get => (TwoPaneViewMode)GetValue(ModeProperty); }
|
||||
|
||||
public TwoPaneViewTallModeConfiguration TallModeConfiguration
|
||||
{
|
||||
get { return (TwoPaneViewTallModeConfiguration)GetValue(TallModeConfigurationProperty); }
|
||||
set { SetValue(TallModeConfigurationProperty, value); }
|
||||
}
|
||||
|
||||
public TwoPaneViewWideModeConfiguration WideModeConfiguration
|
||||
{
|
||||
get { return (TwoPaneViewWideModeConfiguration)GetValue(WideModeConfigurationProperty); }
|
||||
set { SetValue(WideModeConfigurationProperty, value); }
|
||||
}
|
||||
|
||||
public View Pane1
|
||||
{
|
||||
get { return (View)GetValue(Pane1Property); }
|
||||
set { SetValue(Pane1Property, value); }
|
||||
}
|
||||
|
||||
public View Pane2
|
||||
{
|
||||
get { return (View)GetValue(Pane2Property); }
|
||||
set { SetValue(Pane2Property, value); }
|
||||
}
|
||||
|
||||
public TwoPaneViewPriority PanePriority
|
||||
{
|
||||
get { return (TwoPaneViewPriority)GetValue(PanePriorityProperty); }
|
||||
set { SetValue(PanePriorityProperty, value); }
|
||||
}
|
||||
|
||||
public TwoPaneView() : base()
|
||||
{
|
||||
_content1 = new ContentView();
|
||||
_content2 = new ContentView();
|
||||
|
||||
Children.Add(_content1);
|
||||
Children.Add(_content2);
|
||||
|
||||
this.VerticalOptions = LayoutOptions.Fill;
|
||||
this.HorizontalOptions = LayoutOptions.Fill;
|
||||
ColumnSpacing = 0;
|
||||
RowSpacing = 0;
|
||||
|
||||
_modeStates = new VisualStateGroup()
|
||||
{
|
||||
Name = "ModeStates"
|
||||
};
|
||||
|
||||
_modeStates.States.Add(new VisualState() { Name = "ViewMode_OneOnly" });
|
||||
_modeStates.States.Add(new VisualState() { Name = "ViewMode_TwoOnly" });
|
||||
_modeStates.States.Add(new VisualState() { Name = "ViewMode_LeftRight" });
|
||||
_modeStates.States.Add(new VisualState() { Name = "ViewMode_RightLeft" });
|
||||
_modeStates.States.Add(new VisualState() { Name = "ViewMode_TopBottom" });
|
||||
_modeStates.States.Add(new VisualState() { Name = "ViewMode_BottomTop" });
|
||||
|
||||
VisualStateManager.SetVisualStateGroups(this, new VisualStateGroupList() { _modeStates });
|
||||
|
||||
this.RowDefinitions = new RowDefinitionCollection() { new RowDefinition(), new RowDefinition(), new RowDefinition() };
|
||||
this.ColumnDefinitions = new ColumnDefinitionCollection() { new ColumnDefinition(), new ColumnDefinition(), new ColumnDefinition() };
|
||||
}
|
||||
|
||||
internal override void OnIsPlatformEnabledChanged()
|
||||
{
|
||||
base.OnIsPlatformEnabledChanged();
|
||||
if (IsPlatformEnabled)
|
||||
{
|
||||
TwoPaneViewLayoutGuide.WatchForChanges();
|
||||
TwoPaneViewLayoutGuide.PropertyChanged += OnTwoPaneViewLayoutGuide;
|
||||
}
|
||||
else
|
||||
{
|
||||
TwoPaneViewLayoutGuide.WatchForChanges();
|
||||
TwoPaneViewLayoutGuide.PropertyChanged += OnTwoPaneViewLayoutGuide;
|
||||
}
|
||||
}
|
||||
|
||||
TwoPaneViewLayoutGuide TwoPaneViewLayoutGuide
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_twoPaneViewLayoutGuide == null)
|
||||
{
|
||||
_twoPaneViewLayoutGuide = new TwoPaneViewLayoutGuide(this);
|
||||
}
|
||||
|
||||
return _twoPaneViewLayoutGuide;
|
||||
}
|
||||
}
|
||||
|
||||
void OnTwoPaneViewLayoutGuide(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
UpdateMode();
|
||||
}
|
||||
|
||||
protected override void LayoutChildren(double x, double y, double width, double height)
|
||||
{
|
||||
if (_updatingMode)
|
||||
return;
|
||||
|
||||
if (_hasMeasured)
|
||||
base.LayoutChildren(x, y, width, height);
|
||||
else
|
||||
UpdateMode();
|
||||
}
|
||||
|
||||
void UpdateMode()
|
||||
{
|
||||
_updatingMode = true;
|
||||
try
|
||||
{
|
||||
double controlWidth = this.Width;
|
||||
double controlHeight = this.Height;
|
||||
|
||||
ViewMode newMode = (PanePriority == TwoPaneViewPriority.Pane1) ? ViewMode.Pane1Only : ViewMode.Pane2Only;
|
||||
|
||||
_hasMeasured = true;
|
||||
|
||||
this.TwoPaneViewLayoutGuide.UpdateLayouts();
|
||||
|
||||
if (TwoPaneViewLayoutGuide.Mode != TwoPaneViewMode.SinglePane)
|
||||
{
|
||||
if (TwoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Wide)
|
||||
{
|
||||
// Regions are laid out horizontally
|
||||
if (WideModeConfiguration != TwoPaneViewWideModeConfiguration.SinglePane)
|
||||
{
|
||||
newMode = (WideModeConfiguration == TwoPaneViewWideModeConfiguration.LeftRight) ? ViewMode.LeftRight : ViewMode.RightLeft;
|
||||
}
|
||||
}
|
||||
else if (TwoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Tall)
|
||||
{
|
||||
// Regions are laid out vertically
|
||||
if (TallModeConfiguration != TwoPaneViewTallModeConfiguration.SinglePane)
|
||||
{
|
||||
newMode = (TallModeConfiguration == TwoPaneViewTallModeConfiguration.TopBottom) ? ViewMode.TopBottom : ViewMode.BottomTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// One region
|
||||
if (controlWidth > MinWideModeWidth && WideModeConfiguration != TwoPaneViewWideModeConfiguration.SinglePane)
|
||||
{
|
||||
// Split horizontally
|
||||
newMode = (WideModeConfiguration == TwoPaneViewWideModeConfiguration.LeftRight) ? ViewMode.LeftRight : ViewMode.RightLeft;
|
||||
}
|
||||
else if (controlHeight > MinTallModeHeight && TallModeConfiguration != TwoPaneViewTallModeConfiguration.SinglePane)
|
||||
{
|
||||
// Split vertically
|
||||
newMode = (TallModeConfiguration == TwoPaneViewTallModeConfiguration.TopBottom) ? ViewMode.TopBottom : ViewMode.BottomTop;
|
||||
}
|
||||
}
|
||||
|
||||
// Update row/column sizes (this may need to happen even if the mode doesn't change)
|
||||
UpdateRowsColumns(newMode);
|
||||
|
||||
// Update mode if necessary
|
||||
if (newMode != _currentMode)
|
||||
{
|
||||
_currentMode = newMode;
|
||||
|
||||
TwoPaneViewMode newViewMode = TwoPaneViewMode.SinglePane;
|
||||
|
||||
switch (_currentMode)
|
||||
{
|
||||
case ViewMode.Pane1Only:
|
||||
VisualStateManager.GoToState(this, "ViewMode_OneOnly"); break;
|
||||
case ViewMode.Pane2Only:
|
||||
VisualStateManager.GoToState(this, "ViewMode_TwoOnly"); break;
|
||||
case ViewMode.LeftRight:
|
||||
VisualStateManager.GoToState(this, "ViewMode_LeftRight"); newViewMode = TwoPaneViewMode.Wide; break;
|
||||
case ViewMode.RightLeft:
|
||||
VisualStateManager.GoToState(this, "ViewMode_RightLeft"); newViewMode = TwoPaneViewMode.Wide; break;
|
||||
case ViewMode.TopBottom:
|
||||
VisualStateManager.GoToState(this, "ViewMode_TopBottom"); newViewMode = TwoPaneViewMode.Tall; break;
|
||||
case ViewMode.BottomTop:
|
||||
VisualStateManager.GoToState(this, "ViewMode_BottomTop"); newViewMode = TwoPaneViewMode.Tall; break;
|
||||
}
|
||||
|
||||
if (newViewMode != Mode)
|
||||
{
|
||||
_updatingMode = false;
|
||||
SetValue(ModePropertyKey, newViewMode);
|
||||
}
|
||||
}
|
||||
|
||||
_updatingMode = false;
|
||||
|
||||
if (_processPendingChange)
|
||||
{
|
||||
_processPendingChange = false;
|
||||
UpdateMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
InvalidateLayout();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_updatingMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateRowsColumns(ViewMode newMode)
|
||||
{
|
||||
var _columnLeft = ColumnDefinitions[0];
|
||||
var _columnMiddle = ColumnDefinitions[1];
|
||||
var _columnRight = ColumnDefinitions[2];
|
||||
|
||||
var _rowTop = RowDefinitions[0];
|
||||
var _rowMiddle = RowDefinitions[1];
|
||||
var _rowBottom = RowDefinitions[2];
|
||||
|
||||
_columnMiddle.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
_rowMiddle.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
|
||||
if (newMode == ViewMode.LeftRight || newMode == ViewMode.RightLeft)
|
||||
{
|
||||
_columnLeft.Width = ((newMode == ViewMode.LeftRight) ? Pane1Length : Pane2Length);
|
||||
_columnRight.Width = ((newMode == ViewMode.LeftRight) ? Pane2Length : Pane1Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
_columnLeft.Width = new GridLength(1, GridUnitType.Star);
|
||||
_columnRight.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
|
||||
if (newMode == ViewMode.TopBottom || newMode == ViewMode.BottomTop)
|
||||
{
|
||||
_rowTop.Height = ((newMode == ViewMode.TopBottom) ? Pane1Length : Pane2Length);
|
||||
_rowBottom.Height = ((newMode == ViewMode.TopBottom) ? Pane2Length : Pane1Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rowTop.Height = new GridLength(1, GridUnitType.Star);
|
||||
_rowBottom.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
|
||||
if (TwoPaneViewLayoutGuide.Mode != TwoPaneViewMode.SinglePane && newMode != ViewMode.Pane1Only && newMode != ViewMode.Pane2Only)
|
||||
{
|
||||
Rectangle rc1 = _twoPaneViewLayoutGuide.Pane1;
|
||||
Rectangle rc2 = _twoPaneViewLayoutGuide.Pane2;
|
||||
Rectangle hinge = _twoPaneViewLayoutGuide.Hinge;
|
||||
|
||||
if (TwoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Wide)
|
||||
{
|
||||
_columnMiddle.Width = new GridLength(hinge.Width, GridUnitType.Absolute);
|
||||
_columnLeft.Width = new GridLength(rc1.Width, GridUnitType.Absolute);
|
||||
_columnRight.Width = new GridLength(rc2.Width, GridUnitType.Absolute);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rowMiddle.Height = new GridLength(hinge.Height, GridUnitType.Absolute);
|
||||
_rowTop.Height = new GridLength(rc1.Height, GridUnitType.Absolute);
|
||||
_rowBottom.Height = new GridLength(rc2.Height, GridUnitType.Absolute);
|
||||
}
|
||||
}
|
||||
|
||||
switch (newMode)
|
||||
{
|
||||
case ViewMode.LeftRight:
|
||||
SetRowColumn(_content1, 0, 0);
|
||||
SetRowColumn(_content2, 0, 2);
|
||||
_content2.IsVisible = true;
|
||||
_content2.IsVisible = true;
|
||||
break;
|
||||
case ViewMode.RightLeft:
|
||||
SetRowColumn(_content1, 0, 2);
|
||||
SetRowColumn(_content2, 0, 0);
|
||||
_content2.IsVisible = true;
|
||||
_content2.IsVisible = true;
|
||||
break;
|
||||
case ViewMode.TopBottom:
|
||||
SetRowColumn(_content1, 0, 0);
|
||||
SetRowColumn(_content2, 2, 0);
|
||||
_content2.IsVisible = true;
|
||||
_content2.IsVisible = true;
|
||||
break;
|
||||
case ViewMode.BottomTop:
|
||||
SetRowColumn(_content1, 2, 0);
|
||||
SetRowColumn(_content2, 0, 0);
|
||||
_content2.IsVisible = true;
|
||||
_content2.IsVisible = true;
|
||||
break;
|
||||
case ViewMode.Pane1Only:
|
||||
SetRowColumn(_content1, 0, 0);
|
||||
SetRowColumn(_content2, 0, 2);
|
||||
_content1.IsVisible = true;
|
||||
_content2.IsVisible = false;
|
||||
break;
|
||||
case ViewMode.Pane2Only:
|
||||
SetRowColumn(_content1, 0, 2);
|
||||
SetRowColumn(_content2, 0, 0);
|
||||
_content1.IsVisible = false;
|
||||
_content2.IsVisible = true;
|
||||
break;
|
||||
}
|
||||
|
||||
void SetRowColumn(BindableObject bo, int row, int column)
|
||||
{
|
||||
if (bo == null)
|
||||
return;
|
||||
|
||||
Grid.SetColumn(bo, column);
|
||||
Grid.SetRow(bo, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
internal class TwoPaneViewLayoutGuide : INotifyPropertyChanged
|
||||
{
|
||||
public static TwoPaneViewLayoutGuide Instance => _twoPaneViewLayoutGuide.Value;
|
||||
static Lazy<TwoPaneViewLayoutGuide> _twoPaneViewLayoutGuide = new Lazy<TwoPaneViewLayoutGuide>(() => new TwoPaneViewLayoutGuide());
|
||||
|
||||
IDualScreenService DualScreenService =>
|
||||
DependencyService.Get<IDualScreenService>() ?? NoDualScreenServiceImpl.Instance;
|
||||
|
||||
Rectangle _hinge;
|
||||
Rectangle _leftPage;
|
||||
Rectangle _rightPane;
|
||||
TwoPaneViewMode _mode;
|
||||
Layout _layout;
|
||||
bool _isLandscape;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
List<string> _pendingPropertyChanges = new List<string>();
|
||||
|
||||
TwoPaneViewLayoutGuide()
|
||||
{
|
||||
}
|
||||
|
||||
public TwoPaneViewLayoutGuide(Layout layout)
|
||||
{
|
||||
_layout = layout;
|
||||
}
|
||||
|
||||
public void WatchForChanges()
|
||||
{
|
||||
StopWatchingForChanges();
|
||||
DualScreenService.OnScreenChanged += OnScreenChanged;
|
||||
|
||||
if (_layout != null)
|
||||
{
|
||||
_layout.SizeChanged += OnLayoutChanged;
|
||||
}
|
||||
if (Device.Info is INotifyPropertyChanged npc)
|
||||
{
|
||||
npc.PropertyChanged += OnDeviceInfoChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopWatchingForChanges()
|
||||
{
|
||||
DualScreenService.OnScreenChanged -= OnScreenChanged;
|
||||
|
||||
if (_layout != null)
|
||||
{
|
||||
_layout.SizeChanged -= OnLayoutChanged;
|
||||
}
|
||||
if (Device.Info is INotifyPropertyChanged npc)
|
||||
{
|
||||
npc.PropertyChanged -= OnDeviceInfoChanged;
|
||||
}
|
||||
}
|
||||
|
||||
void OnLayoutChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdateLayouts();
|
||||
}
|
||||
|
||||
void OnScreenChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdateLayouts();
|
||||
}
|
||||
|
||||
void OnDeviceInfoChanged(object sender, PropertyChangedEventArgs args)
|
||||
{
|
||||
if (args.PropertyName == nameof(Device.Info.CurrentOrientation))
|
||||
{
|
||||
UpdateLayouts();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLandscape
|
||||
{
|
||||
get => DualScreenService.IsLandscape;
|
||||
set => SetProperty(ref _isLandscape, value);
|
||||
}
|
||||
|
||||
public TwoPaneViewMode Mode
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetTwoPaneViewMode();
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _mode, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle Pane1
|
||||
{
|
||||
get
|
||||
{
|
||||
return _leftPage;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _leftPage, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle Pane2
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rightPane;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _rightPane, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle Hinge
|
||||
{
|
||||
get
|
||||
{
|
||||
return DualScreenService.GetHinge();
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _hinge, value);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateLayouts()
|
||||
{
|
||||
Rectangle containerArea;
|
||||
if (_layout == null)
|
||||
{
|
||||
containerArea = new Rectangle(Point.Zero, Device.info.ScaledScreenSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
containerArea = _layout.Bounds;
|
||||
}
|
||||
|
||||
if (containerArea.Width <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rectangle _newPane1 = Pane1;
|
||||
Rectangle _newPane2 = Pane2;
|
||||
|
||||
if (!DualScreenService.IsLandscape)
|
||||
{
|
||||
if (DualScreenService.IsSpanned)
|
||||
{
|
||||
var paneWidth = (containerArea.Width - Hinge.Width) / 2;
|
||||
_newPane1 = new Rectangle(0, 0, paneWidth, containerArea.Height);
|
||||
_newPane2 = new Rectangle(paneWidth + Hinge.Width, 0, paneWidth, Pane1.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
_newPane1 = new Rectangle(0, 0, containerArea.Width, containerArea.Height);
|
||||
_newPane2 = Rectangle.Zero;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DualScreenService.IsSpanned)
|
||||
{
|
||||
Point displayedScreenAbsCoordinates = Point.Zero;
|
||||
|
||||
if (_layout != null)
|
||||
displayedScreenAbsCoordinates = DualScreenService.GetLocationOnScreen(_layout) ?? Point.Zero;
|
||||
|
||||
var screenSize = Device.info.ScaledScreenSize;
|
||||
var topStuffHeight = displayedScreenAbsCoordinates.Y;
|
||||
var bottomStuffHeight = screenSize.Height - topStuffHeight - containerArea.Height;
|
||||
var paneWidth = containerArea.Width;
|
||||
var leftPaneHeight = Hinge.Y - topStuffHeight;
|
||||
var rightPaneHeight = screenSize.Height - topStuffHeight - leftPaneHeight - bottomStuffHeight - Hinge.Height;
|
||||
|
||||
_newPane1 = new Rectangle(0, 0, paneWidth, leftPaneHeight);
|
||||
_newPane2 = new Rectangle(0, Hinge.Y + Hinge.Height - topStuffHeight, paneWidth, rightPaneHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
_newPane1 = new Rectangle(0, 0, containerArea.Width, containerArea.Height);
|
||||
_newPane2 = Rectangle.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
if (_newPane2.Height < 0 || _newPane2.Width < 0)
|
||||
_newPane2 = Rectangle.Zero;
|
||||
|
||||
if (_newPane1.Height < 0 || _newPane1.Width < 0)
|
||||
_newPane1 = Rectangle.Zero;
|
||||
|
||||
Pane1 = _newPane1;
|
||||
Pane2 = _newPane2;
|
||||
Mode = GetTwoPaneViewMode();
|
||||
Hinge = DualScreenService.GetHinge();
|
||||
IsLandscape = DualScreenService.IsLandscape;
|
||||
|
||||
var properties = _pendingPropertyChanges.ToList();
|
||||
_pendingPropertyChanges.Clear();
|
||||
|
||||
foreach(var property in properties)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
|
||||
}
|
||||
}
|
||||
|
||||
TwoPaneViewMode GetTwoPaneViewMode()
|
||||
{
|
||||
if (!DualScreenService.IsSpanned)
|
||||
return TwoPaneViewMode.SinglePane;
|
||||
|
||||
if (DualScreenService.IsLandscape)
|
||||
return TwoPaneViewMode.Tall;
|
||||
|
||||
return TwoPaneViewMode.Wide;
|
||||
}
|
||||
|
||||
protected bool SetProperty<T>(ref T backingStore, T value,
|
||||
[CallerMemberName]string propertyName = "",
|
||||
Action onChanged = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(backingStore, value))
|
||||
return false;
|
||||
|
||||
backingStore = value;
|
||||
onChanged?.Invoke();
|
||||
_pendingPropertyChanges.Add(propertyName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Xamarin.Forms.DualScreen</RootNamespace>
|
||||
<AssemblyName>Xamarin.Forms.DualScreen</AssemblyName>
|
||||
<PackageId>Xamarin.Forms.DualScreen</PackageId>
|
||||
<TargetFrameworks>$(TargetFrameworks);$(AndroidTargetFrameworks);netstandard1.0;netstandard2.0</TargetFrameworks>
|
||||
<SkipMicrosoftUIXamlCheckTargetPlatformVersion>true</SkipMicrosoftUIXamlCheckTargetPlatformVersion>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<XamarinAndroidSupportSkipVerifyVersions>true</XamarinAndroidSupportSkipVerifyVersions>
|
||||
<GenerateLibraryLayout Condition=" $(TargetFramework.StartsWith('uap10.0')) ">false</GenerateLibraryLayout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetPlatformVersion) == 'uap10.0.14393'">
|
||||
<DefineConstants>$(DefineConstants);UWP_14393</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetPlatformVersion) == 'uap10.0.16299'">
|
||||
<DefineConstants>$(DefineConstants);UWP_18362</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" $(TargetFramework.StartsWith('uap10.0')) ">
|
||||
<DefineConstants>$(DefineConstants);UWP</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid'))">
|
||||
<DefineConstants>$(DefineConstants);ANDROID</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)'=='Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Use2017)' == 'true'">
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>bin\$(Configuration)\$(TargetFramework)</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="**\*.shared.cs" />
|
||||
<Compile Include="**\*.shared.*.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
|
||||
<Compile Include="**\*.netstandard.cs" />
|
||||
<Compile Include="**\*.netstandard.*.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.0' ">
|
||||
<PackageReference Include="System.ComponentModel" Version="4.3.0" />
|
||||
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('uap10.0')) ">
|
||||
<Compile Include="**\*.uwp.cs" />
|
||||
<Compile Include="**\*.uwp.*.cs" />
|
||||
<ProjectReference Include="..\Xamarin.Forms.Platform.UAP\Xamarin.Forms.Platform.UAP.csproj">
|
||||
</ProjectReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.0.15" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
|
||||
<Compile Include="**\*.android.cs" />
|
||||
<Compile Include="**\*.android.*.cs" />
|
||||
<AndroidResource Include="Resources\xml\*.xml" />
|
||||
<PackageReference Include="Xamarin.DuoSdk" Version="0.0.3.2" />
|
||||
<ProjectReference Include="..\Xamarin.Forms.Platform.Android.FormsViewGroup\Xamarin.Forms.Platform.Android.FormsViewGroup.csproj">
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Platform.Android\Xamarin.Forms.Platform.Android.csproj">
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'MonoAndroid10.0'">
|
||||
<PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.1.0-rc1" />
|
||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0-rc1" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0-rc1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'MonoAndroid90'">
|
||||
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v4" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="28.0.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Condition="'$(CI)' == 'true'" Include="Xamarin.Build.TypeRedirector" Version="0.1.2-preview" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"msbuild-sdks": {
|
||||
"MSBuild.Sdk.Extras": "2.0.54"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,8 @@
|
|||
<Project>
|
||||
<Import Project="../Directory.Build.targets" />
|
||||
<Import Project="../UWP.Build.targets" />
|
||||
<ItemGroup Condition=" '$(OS)' != 'Windows_NT' ">
|
||||
<Compile Remove="**\*.*" />
|
||||
<None Include="**\*.*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -3,14 +3,15 @@ using Android.Animation;
|
|||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Xamarin.Forms.Internals;
|
||||
using GlobalSettings = Android.Provider.Settings.Global;
|
||||
using SystemSettings = Android.Provider.Settings.System;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Android
|
||||
{
|
||||
internal class AndroidTicker : Ticker, IDisposable
|
||||
{
|
||||
ValueAnimator _val;
|
||||
bool _energySaveModeDisabled;
|
||||
readonly bool _animatorEnabled;
|
||||
bool _animatorEnabled;
|
||||
|
||||
public AndroidTicker()
|
||||
{
|
||||
|
@ -19,37 +20,46 @@ namespace Xamarin.Forms.Platform.Android
|
|||
_val.RepeatCount = ValueAnimator.Infinite;
|
||||
_val.Update += OnValOnUpdate;
|
||||
_animatorEnabled = IsAnimatorEnabled();
|
||||
CheckPowerSaveModeStatus();
|
||||
CheckAnimationEnabledStatus();
|
||||
}
|
||||
|
||||
public override bool SystemEnabled => _energySaveModeDisabled && _animatorEnabled;
|
||||
public override bool SystemEnabled => _animatorEnabled;
|
||||
|
||||
internal void CheckPowerSaveModeStatus()
|
||||
internal void CheckAnimationEnabledStatus()
|
||||
{
|
||||
// Android disables animations when it's in power save mode
|
||||
// So we need to keep track of whether we're in that mode and handle animations accordingly
|
||||
// We can't just check ValueAnimator.AreAnimationsEnabled() because there's no event for that, and it's
|
||||
// only supported on API >= 26
|
||||
var animatorEnabled = IsAnimatorEnabled();
|
||||
|
||||
if (!Forms.IsLollipopOrNewer)
|
||||
if (animatorEnabled != _animatorEnabled)
|
||||
{
|
||||
_energySaveModeDisabled = true;
|
||||
return;
|
||||
_animatorEnabled = animatorEnabled;
|
||||
|
||||
// Notify the ticker that this value has changed, so it can manage animations in progress
|
||||
OnSystemEnabledChanged();
|
||||
}
|
||||
|
||||
var powerManager = (PowerManager)Forms.ApplicationContext.GetSystemService(Context.PowerService);
|
||||
|
||||
var powerSaveOn = powerManager.IsPowerSaveMode;
|
||||
|
||||
// If power saver is active, then animations will not run
|
||||
_energySaveModeDisabled = !powerSaveOn;
|
||||
|
||||
// Notify the ticker that this value has changed, so it can manage animations in progress
|
||||
OnSystemEnabledChanged();
|
||||
}
|
||||
|
||||
static bool IsAnimatorEnabled()
|
||||
{
|
||||
if (Forms.IsOreoOrNewer)
|
||||
{
|
||||
// For more recent API levels, we can just check this method and be done with it
|
||||
return ValueAnimator.AreAnimatorsEnabled();
|
||||
}
|
||||
|
||||
if (Forms.IsLollipopOrNewer)
|
||||
{
|
||||
// For API levels which support power saving but not AreAnimatorsEnabled, we can check the
|
||||
// power save mode; for these API levels, power saving == ON will mean that animations are disabled
|
||||
var powerManager = (PowerManager)Forms.ApplicationContext.GetSystemService(Context.PowerService);
|
||||
if (powerManager.IsPowerSaveMode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not in power save mode (or don't support it), we still need to check the AnimatorDurationScale;
|
||||
// animations might be disabled by developer mode
|
||||
|
||||
var resolver = global::Android.App.Application.Context?.ContentResolver;
|
||||
if (resolver == null)
|
||||
{
|
||||
|
@ -58,14 +68,14 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
float animationScale;
|
||||
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr1)
|
||||
if (Forms.IsJellyBeanMr1OrNewer)
|
||||
{
|
||||
animationScale = global::Android.Provider.Settings.Global.GetFloat(resolver, global::Android.Provider.Settings.Global.AnimatorDurationScale, 1);
|
||||
animationScale = GlobalSettings.GetFloat(resolver, GlobalSettings.AnimatorDurationScale, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
#pragma warning disable 0618
|
||||
animationScale = global::Android.Provider.Settings.System.GetFloat(resolver, global::Android.Provider.Settings.System.AnimatorDurationScale, 1);
|
||||
animationScale = SystemSettings.GetFloat(resolver, SystemSettings.AnimatorDurationScale, 1);
|
||||
#pragma warning restore 0618
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ using Xamarin.Forms.Internals;
|
|||
using Xamarin.Forms.Platform.Android;
|
||||
using Resource = Android.Resource;
|
||||
using Trace = System.Diagnostics.Trace;
|
||||
using ALayoutDirection = Android.Views.LayoutDirection;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
|
@ -36,9 +35,9 @@ namespace Xamarin.Forms
|
|||
public InitializationOptions(Context activity, Bundle bundle, Assembly resourceAssembly)
|
||||
{
|
||||
this = default(InitializationOptions);
|
||||
this.Activity = activity;
|
||||
this.Bundle = bundle;
|
||||
this.ResourceAssembly = resourceAssembly;
|
||||
Activity = activity;
|
||||
Bundle = bundle;
|
||||
ResourceAssembly = resourceAssembly;
|
||||
}
|
||||
public Context Activity;
|
||||
public Bundle Bundle;
|
||||
|
@ -50,7 +49,6 @@ namespace Xamarin.Forms
|
|||
|
||||
public static class Forms
|
||||
{
|
||||
|
||||
const int TabletCrossover = 600;
|
||||
|
||||
static BuildVersionCodes? s_sdkInt;
|
||||
|
@ -58,6 +56,8 @@ namespace Xamarin.Forms
|
|||
static bool? s_is29OrNewer;
|
||||
static bool? s_isMarshmallowOrNewer;
|
||||
static bool? s_isNougatOrNewer;
|
||||
static bool? s_isOreoOrNewer;
|
||||
static bool? s_isJellyBeanMr1OrNewer;
|
||||
|
||||
[Obsolete("Context is obsolete as of version 2.5. Please use a local context instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
|
@ -73,8 +73,10 @@ namespace Xamarin.Forms
|
|||
static Color _ColorButtonNormal = Color.Default;
|
||||
public static Color ColorButtonNormalOverride { get; set; }
|
||||
|
||||
internal static BuildVersionCodes SdkInt {
|
||||
get {
|
||||
internal static BuildVersionCodes SdkInt
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_sdkInt.HasValue)
|
||||
s_sdkInt = Build.VERSION.SdkInt;
|
||||
return (BuildVersionCodes)s_sdkInt;
|
||||
|
@ -90,13 +92,23 @@ namespace Xamarin.Forms
|
|||
return s_is29OrNewer.Value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsJellyBeanMr1OrNewer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_isJellyBeanMr1OrNewer.HasValue)
|
||||
s_isJellyBeanMr1OrNewer = SdkInt >= BuildVersionCodes.JellyBeanMr1;
|
||||
return s_isJellyBeanMr1OrNewer.Value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsLollipopOrNewer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_isLollipopOrNewer.HasValue)
|
||||
s_isLollipopOrNewer = (int)SdkInt >= 21;
|
||||
s_isLollipopOrNewer = SdkInt >= BuildVersionCodes.Lollipop;
|
||||
return s_isLollipopOrNewer.Value;
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +118,7 @@ namespace Xamarin.Forms
|
|||
get
|
||||
{
|
||||
if (!s_isMarshmallowOrNewer.HasValue)
|
||||
s_isMarshmallowOrNewer = (int)SdkInt >= 23;
|
||||
s_isMarshmallowOrNewer = SdkInt >= BuildVersionCodes.M;
|
||||
return s_isMarshmallowOrNewer.Value;
|
||||
}
|
||||
}
|
||||
|
@ -116,11 +128,21 @@ namespace Xamarin.Forms
|
|||
get
|
||||
{
|
||||
if (!s_isNougatOrNewer.HasValue)
|
||||
s_isNougatOrNewer = (int)Build.VERSION.SdkInt >= 24;
|
||||
s_isNougatOrNewer = SdkInt >= BuildVersionCodes.N;
|
||||
return s_isNougatOrNewer.Value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsOreoOrNewer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_isOreoOrNewer.HasValue)
|
||||
s_isOreoOrNewer = SdkInt >= BuildVersionCodes.O;
|
||||
return s_isOreoOrNewer.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static float GetFontSizeNormal(Context context)
|
||||
{
|
||||
float size = 50;
|
||||
|
|
|
@ -185,6 +185,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
PowerManager.ActionPowerSaveModeChanged));
|
||||
|
||||
_powerSaveReceiverRegistered = true;
|
||||
_powerSaveModeBroadcastReceiver.CheckAnimationEnabledStatus();
|
||||
}
|
||||
|
||||
OnStateChanged();
|
||||
|
|
|
@ -8,7 +8,12 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
public override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
((AndroidTicker)Ticker.Default).CheckPowerSaveModeStatus();
|
||||
CheckAnimationEnabledStatus();
|
||||
}
|
||||
|
||||
public void CheckAnimationEnabledStatus()
|
||||
{
|
||||
((AndroidTicker)Ticker.Default).CheckAnimationEnabledStatus();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
const int SwipeThresholdMargin = 0;
|
||||
const int SwipeItemWidth = 100;
|
||||
const long SwipeAnimationDuration = 200;
|
||||
const double SwipeMinimumDelta = 10;
|
||||
const float SwipeMinimumDelta = 10f;
|
||||
|
||||
readonly Context _context;
|
||||
GestureDetector _detector;
|
||||
|
@ -220,6 +220,12 @@ namespace Xamarin.Forms.Platform.Android
|
|||
_actionView.Dispose();
|
||||
_actionView = null;
|
||||
}
|
||||
|
||||
if (_initialPoint != null)
|
||||
{
|
||||
_initialPoint.Dispose();
|
||||
_initialPoint = null;
|
||||
}
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
|
@ -231,23 +237,42 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
base.OnTouchEvent(e);
|
||||
|
||||
var density = Resources.DisplayMetrics.Density;
|
||||
float x = Math.Abs((_downX - e.GetX()) / density);
|
||||
float y = Math.Abs((_downY - e.GetY()) / density);
|
||||
float x = Math.Abs((_downX - e.GetX()) / _density);
|
||||
float y = Math.Abs((_downY - e.GetY()) / _density);
|
||||
|
||||
if (e.Action != MotionEventActions.Move | (x > SwipeMinimumDelta || y > SwipeMinimumDelta))
|
||||
{
|
||||
_detector.OnTouchEvent(e);
|
||||
}
|
||||
|
||||
|
||||
ProcessSwipingInteractions(e);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnInterceptTouchEvent(MotionEvent ev)
|
||||
public override bool OnInterceptTouchEvent(MotionEvent e)
|
||||
{
|
||||
return true;
|
||||
switch (e.Action)
|
||||
{
|
||||
case MotionEventActions.Down:
|
||||
_downX = e.RawX;
|
||||
_downY = e.RawY;
|
||||
_initialPoint = new APointF(e.GetX() / _density, e.GetY() / _density);
|
||||
break;
|
||||
case MotionEventActions.Move:
|
||||
float x = Math.Abs((_downX - e.GetX()) / _density);
|
||||
float y = Math.Abs((_downY - e.GetY()) / _density);
|
||||
|
||||
if (x > SwipeMinimumDelta || y > SwipeMinimumDelta)
|
||||
ProcessSwipingInteractions(e);
|
||||
break;
|
||||
case MotionEventActions.Cancel:
|
||||
case MotionEventActions.Up:
|
||||
ProcessSwipingInteractions(e);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnDown(MotionEvent e)
|
||||
|
@ -457,7 +482,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
_isTouchDown = false;
|
||||
|
||||
if(!TouchInsideContent(point))
|
||||
if (!TouchInsideContent(point))
|
||||
ProcessTouchSwipeItems(point);
|
||||
|
||||
if (!_isSwiping)
|
||||
|
@ -466,7 +491,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
_isSwiping = false;
|
||||
|
||||
RaiseSwipeEnded();
|
||||
|
||||
|
||||
if (!ValidateSwipeDirection())
|
||||
return false;
|
||||
|
||||
|
@ -559,7 +584,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
_actionView.LayoutParameters = layoutParams;
|
||||
|
||||
_actionView.Orientation = LinearLayoutCompat.Horizontal;
|
||||
|
||||
|
||||
var swipeItems = new List<AView>();
|
||||
|
||||
foreach (var item in items)
|
||||
|
@ -617,7 +642,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
child.Layout(previousWidth, 0, (i + 1) * swipeItemWidth, swipeItemHeight);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
i++;
|
||||
previousWidth += swipeItemWidth;
|
||||
}
|
||||
|
@ -687,7 +712,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
return luminosity < 0.75 ? Color.White : Color.Black;
|
||||
}
|
||||
|
||||
|
||||
void DisposeSwipeItems()
|
||||
{
|
||||
if (_actionView != null)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<Project>
|
||||
<Import Project="../Directory.Build.targets" />
|
||||
<Import Project="../UWP.Build.targets" />
|
||||
<ItemGroup Condition=" '$(OS)' != 'Windows_NT' ">
|
||||
<Compile Remove="**\*.*" />
|
||||
<None Include="**\*.*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,7 +1,4 @@
|
|||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
<PropertyGroup Condition="$(TargetFramework) == 'uap10.0.14393'">
|
||||
<DefineConstants>$(DefineConstants);UWP_14393</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Xamarin.Forms.Platform.UAP</AssemblyName>
|
||||
<RootNamespace>Xamarin.Forms.Platform.UAP</RootNamespace>
|
||||
|
|
|
@ -206,6 +206,41 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override UIView HitTest(CGPoint point, UIEvent uievent)
|
||||
{
|
||||
if (!UserInteractionEnabled || Hidden)
|
||||
return null;
|
||||
|
||||
foreach (var subview in Subviews)
|
||||
{
|
||||
var view = HitTest(subview, point, uievent);
|
||||
|
||||
if (view != null)
|
||||
return view;
|
||||
}
|
||||
|
||||
return base.HitTest(point, uievent);
|
||||
}
|
||||
|
||||
UIView HitTest(UIView view, CGPoint point, UIEvent uievent)
|
||||
{
|
||||
if (view.Subviews == null)
|
||||
return null;
|
||||
|
||||
foreach (var subview in view.Subviews)
|
||||
{
|
||||
CGPoint subPoint = subview.ConvertPointFromView(point, this);
|
||||
UIView result = subview.HitTest(subPoint, uievent);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void TouchesBegan(NSSet touches, UIEvent evt)
|
||||
{
|
||||
var navigationController = GetUINavigationController(GetViewController());
|
||||
|
|
|
@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Xamarin.Forms.Xaml", "Xamar
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.iOS", "Xamarin.Forms.Platform.iOS\Xamarin.Forms.Platform.iOS.csproj", "{271193C1-6E7C-429C-A36D-3F1BE5267231}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.Android", "Xamarin.Forms.Platform.Android\Xamarin.Forms.Platform.Android.csproj", "{0E16E70A-D6DD-4323-AD5D-363ABFF42D6A}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Platform.Android", "Xamarin.Forms.Platform.Android\Xamarin.Forms.Platform.Android.csproj", "{0E16E70A-D6DD-4323-AD5D-363ABFF42D6A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Core.UnitTests", "Xamarin.Forms.Core.UnitTests\Xamarin.Forms.Core.UnitTests.csproj", "{00259593-A283-47A5-ACB7-9C3819B16364}"
|
||||
EndProject
|
||||
|
@ -30,6 +30,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuspec", ".nuspec", "{7E12
|
|||
.nuspec\Xamarin.Forms.Debug.targets = .nuspec\Xamarin.Forms.Debug.targets
|
||||
.nuspec\Xamarin.Forms.DefaultItems.props = .nuspec\Xamarin.Forms.DefaultItems.props
|
||||
.nuspec\Xamarin.Forms.DefaultItems.targets = .nuspec\Xamarin.Forms.DefaultItems.targets
|
||||
.nuspec\Xamarin.Forms.DualScreen.nuspec = .nuspec\Xamarin.Forms.DualScreen.nuspec
|
||||
.nuspec\Xamarin.Forms.Maps.GTK.nuspec = .nuspec\Xamarin.Forms.Maps.GTK.nuspec
|
||||
.nuspec\Xamarin.Forms.Maps.nuspec = .nuspec\Xamarin.Forms.Maps.nuspec
|
||||
.nuspec\Xamarin.Forms.Maps.WPF.nuspec = .nuspec\Xamarin.Forms.Maps.WPF.nuspec
|
||||
|
@ -61,7 +62,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Maps", "Xamar
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Maps.iOS", "Xamarin.Forms.Maps.iOS\Xamarin.Forms.Maps.iOS.csproj", "{ABA078C4-F9BB-4924-8B2B-10FE0D2F5491}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Maps.Android", "Xamarin.Forms.Maps.Android\Xamarin.Forms.Maps.Android.csproj", "{BD50B39A-EBC5-408F-9C5E-923A8EBAE473}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Maps.Android", "Xamarin.Forms.Maps.Android\Xamarin.Forms.Maps.Android.csproj", "{BD50B39A-EBC5-408F-9C5E-923A8EBAE473}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UITests", "UITests", "{D4D57221-71D6-4031-A6F4-EC66AF0929D9}"
|
||||
EndProject
|
||||
|
@ -79,7 +80,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.ControlGaller
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Forwarders", "Forwarders", "{5A2DADBC-9510-4DD1-BE58-01501F2DF65D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.Android (Forwarders)", "Stubs\Xamarin.Forms.Platform.Android\Xamarin.Forms.Platform.Android (Forwarders).csproj", "{6E53FEB1-1100-46AE-8013-17BBA35CC197}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Platform.Android (Forwarders)", "Stubs\Xamarin.Forms.Platform.Android\Xamarin.Forms.Platform.Android (Forwarders).csproj", "{6E53FEB1-1100-46AE-8013-17BBA35CC197}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.iOS (Forwarders)", "Stubs\Xamarin.Forms.Platform.iOS\Xamarin.Forms.Platform.iOS (Forwarders).csproj", "{39B3457F-01D8-43D0-8E84-D8C4F73CF48D}"
|
||||
EndProject
|
||||
|
@ -89,9 +90,9 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Xamarin.Forms.Controls.Issu
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.ControlGallery.WindowsUniversal", "Xamarin.Forms.ControlGallery.WindowsUniversal\Xamarin.Forms.ControlGallery.WindowsUniversal.csproj", "{AC257966-9368-478A-9DF4-F0D28E320FE3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.UAP", "Xamarin.Forms.Platform.UAP\Xamarin.Forms.Platform.UAP.csproj", "{00D8D049-FFAA-4759-8FC9-1ECA30777F72}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Platform.UAP", "Xamarin.Forms.Platform.UAP\Xamarin.Forms.Platform.UAP.csproj", "{00D8D049-FFAA-4759-8FC9-1ECA30777F72}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Maps.UWP", "Xamarin.Forms.Maps.UWP\Xamarin.Forms.Maps.UWP.csproj", "{04D89A60-78EF-4A32-AE17-87E47E0233A5}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Maps.UWP", "Xamarin.Forms.Maps.UWP\Xamarin.Forms.Maps.UWP.csproj", "{04D89A60-78EF-4A32-AE17-87E47E0233A5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Core.Windows.UITests", "Xamarin.Forms.Core.Windows.UITests\Xamarin.Forms.Core.Windows.UITests.csproj", "{0A39A74B-6F7A-4D41-84F2-B0CCDCE899DF}"
|
||||
EndProject
|
||||
|
@ -110,7 +111,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PagesGallery.Droid", "Pages
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PagesGallery.UWP", "PagesGallery\PagesGallery.UWP\PagesGallery.UWP.csproj", "{95FEB8D4-D57E-4B96-A8D8-59D241C0501B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.Android.AppLinks", "Xamarin.Forms.Platform.Android.AppLinks\Xamarin.Forms.Platform.Android.AppLinks.csproj", "{42DB052E-0909-45D2-8240-187F99F393FB}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Platform.Android.AppLinks", "Xamarin.Forms.Platform.Android.AppLinks\Xamarin.Forms.Platform.Android.AppLinks.csproj", "{42DB052E-0909-45D2-8240-187F99F393FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Pages.Azure", "Xamarin.Forms.Pages.Azure\Xamarin.Forms.Pages.Azure.csproj", "{C9696465-7657-4843-872E-3C01891C4A9B}"
|
||||
EndProject
|
||||
|
@ -182,10 +183,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Xamarin.Forms.Material", "X
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Material.iOS", "Xamarin.Forms.Material.iOS\Xamarin.Forms.Material.iOS.csproj", "{8A75B1DC-CEED-4B1B-8675-A7DFFD1E6DE4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Material.Android", "Xamarin.Forms.Material.Android\Xamarin.Forms.Material.Android.csproj", "{E1586CE6-8EAC-4388-A15A-1AABF108B5F8}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Material.Android", "Xamarin.Forms.Material.Android\Xamarin.Forms.Material.Android.csproj", "{E1586CE6-8EAC-4388-A15A-1AABF108B5F8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.Material.Tizen", "Xamarin.Forms.Material.Tizen\Xamarin.Forms.Material.Tizen.csproj", "{84B8819E-9123-43CE-A788-8CB05ABA32DA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Xamarin.Forms.DualScreen", "Xamarin.Forms.DualScreen", "{C97E54D4-DBC5-424E-B63F-4CF2F3EE8D13}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Forms.DualScreen", "Xamarin.Forms.DualScreen\Xamarin.Forms.DualScreen.csproj", "{FB4A866A-5721-4545-9E5D-B7F7D59875A4}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{0E16E70A-D6DD-4323-AD5D-363ABFF42D6A} = {0E16E70A-D6DD-4323-AD5D-363ABFF42D6A}
|
||||
{00D8D049-FFAA-4759-8FC9-1ECA30777F72} = {00D8D049-FFAA-4759-8FC9-1ECA30777F72}
|
||||
{3B72465B-ACAE-43AE-9327-10F372FE5F80} = {3B72465B-ACAE-43AE-9327-10F372FE5F80}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.DualScreen.UnitTests", "Xamarin.Forms.DualScreen.UnitTests\Xamarin.Forms.DualScreen.UnitTests.csproj", "{00D50743-7821-4293-92F2-7C614C256BD6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Xamarin.Forms.Controls.Issues.Shared.projitems*{0a39a74b-6f7a-4d41-84f2-b0ccdce899df}*SharedItemsImports = 4
|
||||
|
@ -1573,6 +1585,54 @@ Global
|
|||
{84B8819E-9123-43CE-A788-8CB05ABA32DA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{84B8819E-9123-43CE-A788-8CB05ABA32DA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{84B8819E-9123-43CE-A788-8CB05ABA32DA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1640,6 +1700,8 @@ Global
|
|||
{8A75B1DC-CEED-4B1B-8675-A7DFFD1E6DE4} = {F6F6A11F-CA66-4C7E-AB84-CD21D817C2CD}
|
||||
{E1586CE6-8EAC-4388-A15A-1AABF108B5F8} = {F6F6A11F-CA66-4C7E-AB84-CD21D817C2CD}
|
||||
{84B8819E-9123-43CE-A788-8CB05ABA32DA} = {F6F6A11F-CA66-4C7E-AB84-CD21D817C2CD}
|
||||
{FB4A866A-5721-4545-9E5D-B7F7D59875A4} = {C97E54D4-DBC5-424E-B63F-4CF2F3EE8D13}
|
||||
{00D50743-7821-4293-92F2-7C614C256BD6} = {C97E54D4-DBC5-424E-B63F-4CF2F3EE8D13}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {650AE971-2F29-46A8-822C-FB4FCDC6A9A0}
|
||||
|
|
|
@ -91,6 +91,7 @@ jobs:
|
|||
inputs:
|
||||
testAssemblyVer2: |
|
||||
**/bin/$(BuildConfiguration)/Xamarin.Forms.Core.UnitTests.dll
|
||||
**/bin/$(BuildConfiguration)/**/Xamarin.Forms.DualScreen.UnitTests.dll
|
||||
**/bin/$(BuildConfiguration)/Xamarin.Forms.Pages.UnitTests.dll
|
||||
**/bin/$(BuildConfiguration)/**/Xamarin.Forms.Xaml.UnitTests.dll
|
||||
searchFolder: ${{ parameters.nunitTestFolder }}
|
||||
|
@ -169,6 +170,10 @@ jobs:
|
|||
Microsoft.XamlStandard/bin/$(BuildConfiguration)/**/*.dll
|
||||
Microsoft.XamlStandard/bin/$(BuildConfiguration)/**/*.mdb
|
||||
Microsoft.XamlStandard/bin/$(BuildConfiguration)/**/*.pdb
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.dll
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.pdb
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.mdb
|
||||
Xamarin.Forms.DualScreen.UnitTests/bin/$(BuildConfiguration)/**/*.dll
|
||||
**/*.binlog
|
||||
|
||||
TargetFolder: ${{ parameters.artifactsTargetFolder }}
|
||||
|
@ -188,6 +193,10 @@ jobs:
|
|||
Xamarin.Forms.Maps.UWP/bin/$(BuildConfiguration)/**/*.pdb
|
||||
Xamarin.Forms.Maps.UWP/bin/$(BuildConfiguration)/**/*.pri
|
||||
Xamarin.Forms.Maps.UWP/bin/$(BuildConfiguration)/**/*.xbf
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.dll
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.pdb
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.mdb
|
||||
Xamarin.Forms.DualScreen.UnitTests/bin/$(BuildConfiguration)/**/*.dll
|
||||
|
||||
TargetFolder: ${{ parameters.artifactsTargetFolder }}
|
||||
|
||||
|
@ -209,6 +218,10 @@ jobs:
|
|||
Xamarin.Forms.Platform.Android.AppLinks/bin/$(BuildConfiguration)/**/*.dll
|
||||
Xamarin.Forms.Platform.Android.AppLinks/bin/$(BuildConfiguration)/**/*.pdb
|
||||
Xamarin.Forms.Platform.Android.AppLinks/bin/$(BuildConfiguration)/**/*.mdb
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.dll
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.pdb
|
||||
Xamarin.Forms.DualScreen/bin/$(BuildConfiguration)/**/*.mdb
|
||||
Xamarin.Forms.DualScreen.UnitTests/bin/$(BuildConfiguration)/**/*.dll
|
||||
**/*.binlog
|
||||
|
||||
TargetFolder: ${{ parameters.artifactsTargetFolder }}
|
||||
|
|
Загрузка…
Ссылка в новой задаче