This commit is contained in:
Samantha Houts 2020-02-04 15:08:04 -08:00
Родитель f0087cbf5a 50af9f7523
Коммит 85bd80e5c3
45 изменённых файлов: 2379 добавлений и 81 удалений

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

@ -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>

Двоичные данные
Visual Studio 2019/Visualizers/attribcache140.bin Normal file

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

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

@ -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 }}