TPV Initial Project Setup (#9418)
* TPV Initial Setup * Update Xamarin.Forms.ControlGallery.WindowsUniversal/Xamarin.Forms.ControlGallery.WindowsUniversal.csproj * - project fixes * - partial * code cleanup * - nuspec updates * - uitest fixes for mac * - project fixes * netcore app tests * - tests * mac fixes * - yaml search filter * - yaml * - yaml * - pane priority fixes * - tall and wide mode config tests * - add files back in * - spacing fixes
This commit is contained in:
Родитель
3fa35e99c6
Коммит
cfcf8169e8
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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 }}
|
||||
|
|
Загрузка…
Ссылка в новой задаче