Dualscreen updates to new apis and add hinge angle for UWP (#10244)

* Dual Screen sdk updates
- update to newer windows sdk apis
- Add Hinge Angle Events

* - remove reflection call
This commit is contained in:
Shane Neuville 2020-04-13 04:33:32 -06:00 коммит произвёл GitHub
Родитель 8a458aebef
Коммит 723c4bc2f1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 443 добавлений и 188 удалений

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

@ -1,11 +1,27 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup Condition=" '$(UwpMinTargetFrameworks)' != '' ">
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">uap10.0.14393;uap10.0.16299;</TargetFrameworks> <TargetFrameworks>$(UwpMinTargetFrameworks)</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=" '$(UwpMinTargetFrameworks)' == '' ">
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">uap10.0.14393;uap10.0.16299</TargetFrameworks>
<!--<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">uap10.0.14393;uap10.0.16299;uap10.0.18362</TargetFrameworks>-->
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<TargetPlatformVersion Condition="'$(TargetFramework)' == 'uap10.0.14393'">10.0.16299.0</TargetPlatformVersion> <TargetPlatformVersion Condition="'$(TargetFramework)' == 'uap10.0.14393'">10.0.16299.0</TargetPlatformVersion>
<TargetPlatformVersion Condition="'$(TargetFramework)' == 'uap10.0.16299'">10.0.18362.0</TargetPlatformVersion> <TargetPlatformVersion Condition="'$(TargetFramework)' == 'uap10.0.16299'">10.0.18362.0</TargetPlatformVersion>
<TargetPlatformVersion Condition="'$(TargetFramework)' == 'uap10.0.18362'">10.0.19559.0</TargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0.14393'"> <PropertyGroup Condition=" $(TargetFramework.StartsWith('uap10.0')) ">
<DefineConstants>$(DefineConstants);UWP</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetPlatformVersion) == '10.0.16299.0'">
<DefineConstants>$(DefineConstants);UWP_14393</DefineConstants> <DefineConstants>$(DefineConstants);UWP_14393</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="$(TargetPlatformVersion) == '10.0.18362.0'">
<DefineConstants>$(DefineConstants);UWP_18362</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" $(TargetPlatformVersion.StartsWith('10.0.19')) ">
<DefineConstants>$(DefineConstants);UWP_18362;UWP_19000</DefineConstants>
</PropertyGroup>
</Project> </Project>

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

@ -2,11 +2,16 @@
<ItemGroup Condition="'$(TargetFramework)' == 'uap10.0.14393' AND '$(OS)' == 'Windows_NT' "> <ItemGroup Condition="'$(TargetFramework)' == 'uap10.0.14393' AND '$(OS)' == 'Windows_NT' ">
<PackageReference Include="NETStandard.Library" Version="2.0.1" /> <PackageReference Include="NETStandard.Library" Version="2.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('uap10.0')) AND '$(OS)' == 'Windows_NT' "> <ItemGroup Condition="$(TargetFramework.StartsWith('uap10.0')) AND '$(OS)' == 'Windows_NT' AND $(TargetPlatformMinVersion) &lt;= '10.0.16299.0' ">
<SDKReference Include="WindowsMobile, Version=10.0.18362.0"> <SDKReference Include="WindowsMobile, Version=10.0.18362.0">
<Name>Windows Mobile Extensions for the UWP</Name> <Name>Windows Mobile Extensions for the UWP</Name>
</SDKReference> </SDKReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('uap10.0')) AND '$(OS)' == 'Windows_NT' AND $(TargetPlatformMinVersion) &gt;= '10.0.18362.0' ">
<SDKReference Include="WindowsMobile, Version=10.0.19559.0">
<Name>Windows Mobile Extensions for the UWP</Name>
</SDKReference>
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('uap10.0')) AND '$(OS)' == 'Windows_NT' "> <ItemGroup Condition="$(TargetFramework.StartsWith('uap10.0')) AND '$(OS)' == 'Windows_NT' ">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.0.15" /> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.0.15" />
</ItemGroup> </ItemGroup>

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

@ -56,7 +56,6 @@ using Android.Support.V4.Content;
[assembly: ExportRenderer(typeof(ShellGestures.TouchTestView), typeof(ShellGesturesTouchTestViewRenderer))] [assembly: ExportRenderer(typeof(ShellGestures.TouchTestView), typeof(ShellGesturesTouchTestViewRenderer))]
[assembly: ExportRenderer(typeof(Issue7249Switch), typeof(Issue7249SwitchRenderer))] [assembly: ExportRenderer(typeof(Issue7249Switch), typeof(Issue7249SwitchRenderer))]
[assembly: ExportRenderer(typeof(Issue9360.Issue9360NavigationPage), typeof(Issue9360NavigationPageRenderer))] [assembly: ExportRenderer(typeof(Issue9360.Issue9360NavigationPage), typeof(Issue9360NavigationPageRenderer))]
[assembly: ExportRenderer(typeof(Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.HingeAngleLabel), typeof(HingeAngleLabelRenderer))]
[assembly: ExportRenderer(typeof(Xamarin.Forms.Controls.Tests.TestClasses.CustomButton), typeof(CustomButtonRenderer))] [assembly: ExportRenderer(typeof(Xamarin.Forms.Controls.Tests.TestClasses.CustomButton), typeof(CustomButtonRenderer))]
[assembly: ExportRenderer(typeof(Issue8801.PopupStackLayout), typeof(CustomStackLayoutRenderer))] [assembly: ExportRenderer(typeof(Issue8801.PopupStackLayout), typeof(CustomStackLayoutRenderer))]
@ -67,56 +66,6 @@ using Android.Support.V4.Content;
#endif #endif
namespace Xamarin.Forms.ControlGallery.Android namespace Xamarin.Forms.ControlGallery.Android
{ {
public class HingeAngleLabelRenderer : Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer
{
System.Timers.Timer _hingeTimer;
public HingeAngleLabelRenderer(Context context) : base(context)
{
}
async void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (_hingeTimer == null)
return;
_hingeTimer.Stop();
var hingeAngle = await DualScreen.DualScreenInfo.Current.GetHingeAngleAsync();
Device.BeginInvokeOnMainThread(() =>
{
if (_hingeTimer != null)
Element.Text = hingeAngle.ToString();
});
if(_hingeTimer != null)
_hingeTimer.Start();
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if(_hingeTimer == null)
{
_hingeTimer = new System.Timers.Timer(100);
_hingeTimer.Elapsed += OnTimerElapsed;
_hingeTimer.Start();
}
}
protected override void Dispose(bool disposing)
{
if (_hingeTimer != null)
{
_hingeTimer.Elapsed -= OnTimerElapsed;
_hingeTimer.Stop();
_hingeTimer = null;
}
base.Dispose(disposing);
}
}
public class CustomStackLayoutRenderer : VisualElementRenderer<StackLayout> public class CustomStackLayoutRenderer : VisualElementRenderer<StackLayout>
{ {
public CustomStackLayoutRenderer(Context context) : base(context) public CustomStackLayoutRenderer(Context context) : base(context)

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

@ -33,8 +33,8 @@
</Picker> </Picker>
<Picker Title="WideModeConfiguration" x:Name="WideModeConfiguration" SelectedIndex="1"> <Picker Title="WideModeConfiguration" x:Name="WideModeConfiguration" SelectedIndex="1">
</Picker> </Picker>
<Label Text="Hinge Angle (only works on Android):"></Label> <Label Text="Hinge Angle"></Label>
<twoPaneGallery:HingeAngleLabel /> <Label x:Name="lblHingeAngle" />
</StackLayout> </StackLayout>
</local:TwoPaneView.Pane1> </local:TwoPaneView.Pane1>
<local:TwoPaneView.Pane2> <local:TwoPaneView.Pane2>

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

@ -33,11 +33,21 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
protected override void OnAppearing() protected override void OnAppearing()
{ {
base.OnAppearing();
PanePriority.SelectedIndex = 0; PanePriority.SelectedIndex = 0;
TallModeConfiguration.SelectedIndex = 1; TallModeConfiguration.SelectedIndex = 1;
WideModeConfiguration.SelectedIndex = 1; WideModeConfiguration.SelectedIndex = 1;
DualScreen.DualScreenInfo.Current.HingeAngleChanged += OnHingeAngleChanged;
}
protected override void OnDisappearing()
{
DualScreen.DualScreenInfo.Current.HingeAngleChanged -= OnHingeAngleChanged;
}
void OnHingeAngleChanged(object sender, DualScreen.HingeAngleChangedEventArgs e)
{
lblHingeAngle.Text = e.HingeAngleInDegrees.ToString();
} }
void Setup(double width, double height) void Setup(double width, double height)
@ -61,6 +71,4 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
Pane2Length.Value = 0.5; Pane2Length.Value = 0.5;
} }
} }
public class HingeAngleLabel : Label { }
} }

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

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Windows.Foundation.Metadata; using Windows.Foundation.Metadata;
using Windows.UI.ViewManagement; using Windows.UI.ViewManagement;
#if !UWP_14393 #if UWP_18362
using Windows.UI.WindowManagement; using Windows.UI.WindowManagement;
#endif #endif
@ -18,7 +18,7 @@ namespace Xamarin.Forms.DualScreen
public static class DualScreenHelper public static class DualScreenHelper
{ {
#if !UWP_14393 #if UWP_18362
public static bool HasCompactModeSupport() public static bool HasCompactModeSupport()
{ {

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

@ -3,12 +3,38 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Device.Display;
namespace Xamarin.Forms.DualScreen namespace Xamarin.Forms.DualScreen
{ {
public partial class DualScreenInfo : INotifyPropertyChanged public partial class DualScreenInfo : INotifyPropertyChanged
{ {
static object hingeAngleLock = new object();
public Task<int> GetHingeAngleAsync() => DualScreenService.GetHingeAngleAsync(); public Task<int> GetHingeAngleAsync() => DualScreenService.GetHingeAngleAsync();
void ProcessHingeAngleSubscriberCount(int newCount)
{
lock (hingeAngleLock)
{
if (newCount == 1)
{
DualScreen.DualScreenService.DualScreenServiceImpl.HingeAngleChanged += OnHingeAngleChanged;
}
else if (newCount == 0)
{
DualScreen.DualScreenService.DualScreenServiceImpl.HingeAngleChanged -= OnHingeAngleChanged;
}
}
}
void OnHingeAngleChanged(object sender, HingeSensor.HingeSensorChangedEventArgs e)
{
Device.BeginInvokeOnMainThread(() =>
{
_hingeAngleChanged?.Invoke(this, new HingeAngleChangedEventArgs(e.HingeAngle));
});
}
} }
} }

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

@ -0,0 +1,16 @@
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 partial class DualScreenInfo : INotifyPropertyChanged
{
public Task<int> GetHingeAngleAsync() => DualScreenService.GetHingeAngleAsync();
void ProcessHingeAngleSubscriberCount(int newCount) { }
}
}

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

@ -3,24 +3,37 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Xamarin.Forms.DualScreen namespace Xamarin.Forms.DualScreen
{ {
public class HingeAngleChangedEventArgs : EventArgs
{
public HingeAngleChangedEventArgs(double hingeAngleInDegrees)
{
HingeAngleInDegrees = hingeAngleInDegrees;
}
public double HingeAngleInDegrees { get; }
}
public partial class DualScreenInfo : INotifyPropertyChanged public partial class DualScreenInfo : INotifyPropertyChanged
{ {
static Lazy<DualScreenInfo> _dualScreenInfo { get; } = new Lazy<DualScreenInfo>(OnCreate);
public event PropertyChangedEventHandler PropertyChanged;
Rectangle[] _spanningBounds; Rectangle[] _spanningBounds;
Rectangle _hingeBounds; Rectangle _hingeBounds;
bool _isLandscape; bool _isLandscape;
TwoPaneViewMode _spanMode; TwoPaneViewMode _spanMode;
TwoPaneViewLayoutGuide _twoPaneViewLayoutGuide; TwoPaneViewLayoutGuide _twoPaneViewLayoutGuide;
IDualScreenService _dualScreenService; IDualScreenService _dualScreenService;
public static DualScreenInfo Current => _dualScreenInfo.Value;
IDualScreenService DualScreenService => IDualScreenService DualScreenService =>
_dualScreenService ?? DependencyService.Get<IDualScreenService>() ?? NoDualScreenServiceImpl.Instance; _dualScreenService ?? DependencyService.Get<IDualScreenService>() ?? NoDualScreenServiceImpl.Instance;
static Lazy<DualScreenInfo> _dualScreenInfo { get; } = new Lazy<DualScreenInfo>(OnCreate);
public static DualScreenInfo Current => _dualScreenInfo.Value;
public event PropertyChangedEventHandler PropertyChanged;
public DualScreenInfo(VisualElement layout) : this(layout, null) public DualScreenInfo(VisualElement layout) : this(layout, null)
{ {
} }
@ -35,10 +48,28 @@ namespace Xamarin.Forms.DualScreen
else else
{ {
_twoPaneViewLayoutGuide = new TwoPaneViewLayoutGuide(layout, dualScreenService); _twoPaneViewLayoutGuide = new TwoPaneViewLayoutGuide(layout, dualScreenService);
_twoPaneViewLayoutGuide.PropertyChanged += OnTwoPaneViewLayoutGuideChanged; _twoPaneViewLayoutGuide.PropertyChanged += OnTwoPaneViewLayoutGuideChanged;
} }
} }
EventHandler<HingeAngleChangedEventArgs> _hingeAngleChanged;
int subscriberCount = 0;
public event EventHandler<HingeAngleChangedEventArgs> HingeAngleChanged
{
add
{
ProcessHingeAngleSubscriberCount(Interlocked.Increment(ref subscriberCount));
_hingeAngleChanged += value;
}
remove
{
ProcessHingeAngleSubscriberCount(Interlocked.Decrement(ref subscriberCount));
_hingeAngleChanged -= value;
}
}
public Rectangle[] SpanningBounds public Rectangle[] SpanningBounds
{ {
get => GetSpanningBounds(); get => GetSpanningBounds();

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

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Sensors;
namespace Xamarin.Forms.DualScreen
{
public partial class DualScreenInfo : INotifyPropertyChanged
{
static object hingeAngleLock = new object();
public Task<int> GetHingeAngleAsync() => DualScreenService.GetHingeAngleAsync();
#if UWP_18362
Windows.Devices.Sensors.HingeAngleSensor _angleSensor;
#endif
#if UWP_18362
async void ProcessHingeAngleSubscriberCount(int newCount)
{
try
{
if (_angleSensor == null)
_angleSensor = await Windows.Devices.Sensors.HingeAngleSensor.GetDefaultAsync();
if (_angleSensor == null)
return;
lock (hingeAngleLock)
{
if (newCount == 1)
{
_angleSensor.ReadingChanged += OnReadingChanged;
}
else if(newCount == 0)
{
_angleSensor.ReadingChanged -= OnReadingChanged;
}
}
}
catch(Exception e)
{
Internals.Log.Warning(nameof(DualScreenInfo), $"Failed to retrieve Hinge Angle Sensor {e}");
}
void OnReadingChanged(HingeAngleSensor sender, HingeAngleSensorReadingChangedEventArgs args)
{
Device.BeginInvokeOnMainThread(() =>
{
_hingeAngleChanged?.Invoke(this, new HingeAngleChangedEventArgs(args.Reading.AngleInDegrees));
});
}
}
#else
void ProcessHingeAngleSubscriberCount(int newCount)
{
}
#endif
}
}

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

@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Android.App; using Android.App;
using Android.Util; using Android.Util;
@ -28,11 +29,10 @@ namespace Xamarin.Forms.DualScreen
internal class DualScreenServiceImpl : IDualScreenService, Platform.Android.DualScreen.IDualScreenService internal class DualScreenServiceImpl : IDualScreenService, Platform.Android.DualScreen.IDualScreenService
{ {
public event EventHandler OnScreenChanged;
ScreenHelper _helper; ScreenHelper _helper;
bool _isDuo = false; bool _isDuo = false;
bool IsDuo => (_helper == null || _HingeService == null || _mainActivity == null || _hingeSensor == null) ? false : _isDuo; bool IsDuo => (_helper == null || _HingeService == null || _mainActivity == null || _singleUseHingeSensor == null) ? false : _isDuo;
HingeSensor _hingeSensor; HingeSensor _singleUseHingeSensor;
static Activity _mainActivity; static Activity _mainActivity;
static DualScreenServiceImpl _HingeService; static DualScreenServiceImpl _HingeService;
bool _isLandscape; bool _isLandscape;
@ -40,6 +40,11 @@ namespace Xamarin.Forms.DualScreen
object _hingeAngleLock = new object(); object _hingeAngleLock = new object();
TaskCompletionSource<int> _gettingHingeAngle; TaskCompletionSource<int> _gettingHingeAngle;
internal static Activity MainActivity => _mainActivity;
static HingeSensor DefaultHingeSensor;
public event EventHandler OnScreenChanged;
[Internals.Preserve(Conditional = true)] [Internals.Preserve(Conditional = true)]
public DualScreenServiceImpl() public DualScreenServiceImpl()
{ {
@ -77,51 +82,19 @@ namespace Xamarin.Forms.DualScreen
if (!isDuo) if (!isDuo)
{ {
_HingeService._helper = null; _HingeService._helper = null;
_HingeService._hingeSensor = null; _HingeService.SetupHingeSensors(null);
return; return;
} }
_HingeService._helper = screenHelper; _HingeService._helper = screenHelper;
_HingeService._hingeSensor = new HingeSensor(_mainActivity); _HingeService.SetupHingeSensors(_mainActivity);
if (_mainActivity is IDeviceInfoProvider newDeviceInfoProvider) if (_mainActivity is IDeviceInfoProvider newDeviceInfoProvider)
{ {
newDeviceInfoProvider.ConfigurationChanged += _HingeService.ConfigurationChanged; newDeviceInfoProvider.ConfigurationChanged += _HingeService.ConfigurationChanged;
} }
} }
void ConfigurationChanged(object sender, EventArgs e)
{
if(IsDuo)
_helper?.Update();
bool screenChanged = false;
if (_isLandscape != IsLandscape)
{
_isLandscape = IsLandscape;
screenChanged = true;
}
if (_mainActivity != null)
{
using (DisplayMetrics display = _mainActivity.Resources.DisplayMetrics)
{
var scalingFactor = display.Density;
_pixelScreenSize = new Size(display.WidthPixels, display.HeightPixels);
var newSize = new Size(_pixelScreenSize.Width / scalingFactor, _pixelScreenSize.Height / scalingFactor);
if (newSize != ScaledScreenSize)
{
ScaledScreenSize = newSize;
screenChanged = true;
}
}
}
if(screenChanged)
OnScreenChanged?.Invoke(this, e);
}
public Size ScaledScreenSize public Size ScaledScreenSize
{ {
get; get;
@ -131,43 +104,6 @@ namespace Xamarin.Forms.DualScreen
public bool IsSpanned public bool IsSpanned
=> IsDuo && (_helper?.IsDualMode ?? false); => IsDuo && (_helper?.IsDualMode ?? false);
void StartListeningForHingeChanges()
{
if (!IsDuo)
return;
_hingeSensor.OnSensorChanged += OnSensorChanged;
_hingeSensor.StartListening();
}
void StopListeningForHingeChanges()
{
if (!IsDuo)
return;
_hingeSensor.OnSensorChanged -= OnSensorChanged;
_hingeSensor.StopListening();
}
void OnSensorChanged(object sender, HingeSensor.HingeSensorChangedEventArgs e)
{
SetHingeAngle(e.HingeAngle);
}
void SetHingeAngle(int hingeAngle)
{
TaskCompletionSource<int> toSet = null;
lock (_hingeAngleLock)
{
StopListeningForHingeChanges();
toSet = _gettingHingeAngle;
_gettingHingeAngle = null;
}
if (toSet != null)
toSet.SetResult(hingeAngle);
}
public Task<int> GetHingeAngleAsync() public Task<int> GetHingeAngleAsync()
{ {
if (!IsDuo) if (!IsDuo)
@ -293,6 +229,145 @@ namespace Xamarin.Forms.DualScreen
} }
} }
} }
static EventHandler<HingeSensor.HingeSensorChangedEventArgs> _hingeAngleChanged;
static int subscriberCount;
static object hingeAngleLock = new object();
public static event EventHandler<HingeSensor.HingeSensorChangedEventArgs> HingeAngleChanged
{
add
{
if (DefaultHingeSensor == null)
return;
ProcessHingeAngleSubscriberCount(Interlocked.Increment(ref subscriberCount));
_hingeAngleChanged += value;
}
remove
{
if (DefaultHingeSensor == null)
return;
ProcessHingeAngleSubscriberCount(Interlocked.Decrement(ref subscriberCount));
_hingeAngleChanged -= value;
}
}
static void ProcessHingeAngleSubscriberCount(int subscriberCount)
{
var sensor = DefaultHingeSensor;
if (sensor == null)
return;
lock(hingeAngleLock)
{
if (subscriberCount == 1)
{
sensor.StartListening();
}
else if (subscriberCount == 0)
{
sensor.StopListening();
}
}
}
void DefaultHingeSensorOnSensorChanged(object sender, HingeSensor.HingeSensorChangedEventArgs e)
{
_hingeAngleChanged?.Invoke(this, e);
}
void SetupHingeSensors(global::Android.Content.Context context)
{
if (context == null)
{
if (DefaultHingeSensor != null)
DefaultHingeSensor.OnSensorChanged -= DefaultHingeSensorOnSensorChanged;
_singleUseHingeSensor = null;
DefaultHingeSensor = null;
}
else
{
_singleUseHingeSensor = new HingeSensor(context);
DefaultHingeSensor = new HingeSensor(context);
DefaultHingeSensor.OnSensorChanged += DefaultHingeSensorOnSensorChanged;
}
}
void ConfigurationChanged(object sender, EventArgs e)
{
if (IsDuo)
_helper?.Update();
bool screenChanged = false;
if (_isLandscape != IsLandscape)
{
_isLandscape = IsLandscape;
screenChanged = true;
}
if (_mainActivity != null)
{
using (DisplayMetrics display = _mainActivity.Resources.DisplayMetrics)
{
var scalingFactor = display.Density;
_pixelScreenSize = new Size(display.WidthPixels, display.HeightPixels);
var newSize = new Size(_pixelScreenSize.Width / scalingFactor, _pixelScreenSize.Height / scalingFactor);
if (newSize != ScaledScreenSize)
{
ScaledScreenSize = newSize;
screenChanged = true;
}
}
}
if (screenChanged)
OnScreenChanged?.Invoke(this, e);
}
void StartListeningForHingeChanges()
{
if (!IsDuo)
return;
_singleUseHingeSensor.OnSensorChanged += OnSensorChanged;
_singleUseHingeSensor.StartListening();
}
void StopListeningForHingeChanges()
{
if (!IsDuo)
return;
_singleUseHingeSensor.OnSensorChanged -= OnSensorChanged;
_singleUseHingeSensor.StopListening();
}
void OnSensorChanged(object sender, HingeSensor.HingeSensorChangedEventArgs e)
{
SetHingeAngle(e.HingeAngle);
}
void SetHingeAngle(int hingeAngle)
{
TaskCompletionSource<int> toSet = null;
lock (_hingeAngleLock)
{
StopListeningForHingeChanges();
toSet = _gettingHingeAngle;
_gettingHingeAngle = null;
}
if (toSet != null)
toSet.SetResult(hingeAngle);
}
} }
} }
} }

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

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Foundation.Metadata; using Windows.Foundation.Metadata;
using Windows.Graphics.Display; using Windows.Graphics.Display;
@ -25,32 +27,50 @@ namespace Xamarin.Forms.DualScreen
public DualScreenService() public DualScreenService()
{ {
if(Window.Current != null) if (Window.Current != null)
{
Window.Current.SizeChanged += OnCurrentSizeChanged; Window.Current.SizeChanged += OnCurrentSizeChanged;
}
} }
public Task<int> GetHingeAngleAsync() => Task.FromResult(0); public async Task<int> GetHingeAngleAsync()
{
if (!ApiInformation.IsMethodPresent("Windows.Devices.Sensors.HingeAngleSensor", "GetDefaultAsync"))
{
return await NoDualScreenServiceImpl.Instance.GetHingeAngleAsync();
}
#if UWP_18362
var sensor = await Windows.Devices.Sensors.HingeAngleSensor.GetDefaultAsync();
if (sensor == null)
return await NoDualScreenServiceImpl.Instance.GetHingeAngleAsync();
var currentReading = await sensor.GetCurrentReadingAsync();
return (int)currentReading.AngleInDegrees;
#else
return await NoDualScreenServiceImpl.Instance.GetHingeAngleAsync();
#endif
}
void OnCurrentSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e) void OnCurrentSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{ {
OnScreenChanged?.Invoke(this, EventArgs.Empty); OnScreenChanged?.Invoke(this, EventArgs.Empty);
} }
bool IsDualScreenDevice => ApiInformation.IsMethodPresent("Windows.UI.ViewManagement.ApplicationView", "GetSpanningRects");
public bool IsSpanned public bool IsSpanned
{ {
get get
{ {
if (!IsDualScreenDevice) var viewMode = (int)ApplicationView.GetForCurrentView().ViewMode;
return false;
var visibleBounds = Window.Current.Bounds; switch (viewMode)
{
if (visibleBounds.Height > 1200 || visibleBounds.Width > 1200) case 2:
return true; return true;
default:
return false; return false;
}
} }
} }
@ -59,13 +79,33 @@ namespace Xamarin.Forms.DualScreen
public bool IsLandscape public bool IsLandscape
{ {
get get
{ {
if (IsSpanned) if (!IsSpanned)
return ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Portrait; return ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Landscape;
else
return ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Landscape; #if UWP_18362
} var displayRegions = ApplicationView.GetForCurrentView().GetDisplayRegions();
} if (displayRegions.Count == 2)
{
// We are split in two panes. Layout accordingly
if (displayRegions[0].WorkAreaOffset.X != displayRegions[1].WorkAreaOffset.X)
{
return false;
}
else if (displayRegions[0].WorkAreaOffset.Y != displayRegions[1].WorkAreaOffset.Y)
{
return true;
}
else
{
return ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Landscape;
}
}
#endif
return ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Landscape;
}
}
public Size ScaledScreenSize public Size ScaledScreenSize
{ {
@ -78,21 +118,56 @@ namespace Xamarin.Forms.DualScreen
public Rectangle GetHinge() public Rectangle GetHinge()
{ {
if (!IsDualScreenDevice) if (!ApiInformation.IsMethodPresent("Windows.UI.ViewManagement.ApplicationView", "GetSpanningRects"))
return Rectangle.Zero; return Rectangle.Zero;
var screen = DisplayInformation.GetForCurrentView(); if (!IsSpanned)
return Rectangle.Zero;
if (IsLandscape) var screen = DisplayInformation.GetForCurrentView();
{
if (IsSpanned)
return new Rectangle(0, 664 + 24, ScaledPixels(screen.ScreenWidthInRawPixels), 0); #if UWP_18362
else var applicationView = ApplicationView.GetForCurrentView();
return new Rectangle(0, 664, ScaledPixels(screen.ScreenWidthInRawPixels), 0); List<Windows.Foundation.Rect> spanningRects = null;
}
else #if UWP_19000
return new Rectangle(720, 0, 0, ScaledPixels(screen.ScreenHeightInRawPixels)); spanningRects = applicationView.GetSpanningRects().ToList();
} #endif
if (spanningRects?.Count == 2)
{
if(!IsLandscape)
{
var x = spanningRects[0].Width;
var hingeWidth = spanningRects[1].X - x;
return new Rectangle(x, 0, hingeWidth, ScaledPixels(screen.ScreenHeightInRawPixels));
}
else
{
var y = spanningRects[0].Height;
var hingeHeight = spanningRects[1].Y - y;
return new Rectangle(0, y, ScaledPixels(screen.ScreenWidthInRawPixels), hingeHeight);
}
}
#endif
// fall back to hard coded
Rectangle returnValue = Rectangle.Zero;
if (IsLandscape)
{
if (IsSpanned)
returnValue = new Rectangle(0, 664 + 24, ScaledPixels(screen.ScreenWidthInRawPixels), 0);
else
returnValue = new Rectangle(0, 664, ScaledPixels(screen.ScreenWidthInRawPixels), 0);
}
else
returnValue = new Rectangle(720, 0, 0, ScaledPixels(screen.ScreenHeightInRawPixels));
return returnValue;
}
double ScaledPixels(double n) double ScaledPixels(double n)
=> n / DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel; => n / DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel;

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

@ -13,15 +13,6 @@
<XamarinAndroidSupportSkipVerifyVersions>true</XamarinAndroidSupportSkipVerifyVersions> <XamarinAndroidSupportSkipVerifyVersions>true</XamarinAndroidSupportSkipVerifyVersions>
<GenerateLibraryLayout Condition=" $(TargetFramework.StartsWith('uap10.0')) ">false</GenerateLibraryLayout> <GenerateLibraryLayout Condition=" $(TargetFramework.StartsWith('uap10.0')) ">false</GenerateLibraryLayout>
</PropertyGroup> </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'))"> <PropertyGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid'))">
<DefineConstants>$(DefineConstants);ANDROID</DefineConstants> <DefineConstants>$(DefineConstants);ANDROID</DefineConstants>
</PropertyGroup> </PropertyGroup>

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

@ -94,13 +94,13 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$(TargetFramework) == 'uap10.0.16299'"> <ItemGroup Condition=" '$(OS)' == 'Windows_NT' AND $(TargetFramework.StartsWith('uap10.0')) AND $(TargetPlatformMinVersion) &gt;= '10.0.16299.0'">
<Page Include="Shell\ShellStyles.xaml"> <Page Include="Shell\ShellStyles.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$(TargetFramework) == 'uap10.0.14393'"> <ItemGroup Condition="'$(OS)' == 'Windows_NT' AND $(TargetFramework.StartsWith('uap10.0')) AND $(TargetPlatformMinVersion) &lt; '10.0.16299.0'">
<Page Remove="Shell\ShellStyles.xaml"> <Page Remove="Shell\ShellStyles.xaml">
</Page> </Page>
</ItemGroup> </ItemGroup>
@ -114,11 +114,11 @@
<ProjectReference Include="..\Xamarin.Forms.Xaml\Xamarin.Forms.Xaml.csproj"> <ProjectReference Include="..\Xamarin.Forms.Xaml\Xamarin.Forms.Xaml.csproj">
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(OS)' == 'Windows_NT' "> <ItemGroup Condition=" '$(OS)' == 'Windows_NT' AND $(TargetFramework.StartsWith('uap10.0')) ">
<PackageReference Include="Microsoft.UI.Xaml" Condition="$(TargetFramework) == 'uap10.0.14393'"> <PackageReference Include="Microsoft.UI.Xaml" Condition="$(TargetPlatformMinVersion) &lt; '10.0.16299.0'">
<Version>2.1.190606001</Version> <Version>2.1.190606001</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.UI.Xaml" Condition="$(TargetFramework) == 'uap10.0.16299'"> <PackageReference Include="Microsoft.UI.Xaml" Condition="$(TargetPlatformMinVersion) &gt;= '10.0.16299.0'">
<Version>2.3.191211002</Version> <Version>2.3.191211002</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Win2D.uwp"> <PackageReference Include="Win2D.uwp">