From 501892ec7551444a8f2e8f16a5fd455ca8d04b9d Mon Sep 17 00:00:00 2001 From: James Montemagno Date: Thu, 16 Aug 2018 08:10:25 -0700 Subject: [PATCH] GH-453 Check for changes in device metrics before sending Event. (#455) * GH-453 Check for changes in device metrics before sending Event. * Implement and test new equality for ScreenMetrics * Implement IEquatable * Make Screen Metrics Readonly :) add more tests * Update docs * Condense tests into single Theory This tests equals/not equals in one test and allows us to add more inline data cases. * Additional optimizations * More Changes for tests * Optimize code even further and fix up tests --- Tests/DeviceDisplay_Tests.cs | 63 +++++++ .../DeviceDisplay/DeviceDisplay.android.cs | 14 +- .../DeviceDisplay/DeviceDisplay.ios.cs | 14 +- .../DeviceDisplay/DeviceDisplay.shared.cs | 52 ++++-- .../DeviceDisplay/DeviceDisplay.uwp.cs | 14 +- Xamarin.Essentials/Xamarin.Essentials.csproj | 5 +- .../xamarin-essentials-android.xml | 5 + .../xamarin-essentials-ios.xml | 5 + .../xamarin-essentials-uwp.xml | 5 + .../en/FrameworksIndex/xamarin-essentials.xml | 5 + docs/en/Xamarin.Essentials/ScreenMetrics.xml | 173 ++++++++++++++++-- 11 files changed, 301 insertions(+), 54 deletions(-) create mode 100644 Tests/DeviceDisplay_Tests.cs diff --git a/Tests/DeviceDisplay_Tests.cs b/Tests/DeviceDisplay_Tests.cs new file mode 100644 index 0000000..3b31730 --- /dev/null +++ b/Tests/DeviceDisplay_Tests.cs @@ -0,0 +1,63 @@ +using Xamarin.Essentials; +using Xunit; + +namespace Tests +{ + public class DeviceDisplay_Tests + { + [Theory] + [InlineData(0.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, 0.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, true)] + [InlineData(1.1, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, 1.1, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, true)] + [InlineData(0.0, 0.0, 0.0, ScreenOrientation.Portrait, ScreenRotation.Rotation0, 0.0, 0.0, 0.0, ScreenOrientation.Portrait, ScreenRotation.Rotation0, true)] + [InlineData(1.1, 0.0, 2.2, ScreenOrientation.Landscape, ScreenRotation.Rotation180, 1.1, 0.0, 2.2, ScreenOrientation.Landscape, ScreenRotation.Rotation180, true)] + [InlineData(1.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, 0.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, false)] + [InlineData(0.0, 1.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, 0.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, false)] + [InlineData(0.0, 0.0, 1.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, 0.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, false)] + [InlineData(0.0, 0.0, 0.0, ScreenOrientation.Portrait, ScreenRotation.Rotation0, 0.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, false)] + [InlineData(1.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation180, 0.0, 0.0, 0.0, ScreenOrientation.Landscape, ScreenRotation.Rotation0, false)] + public void DeviceDisplay_Comparison( + double width1, + double height1, + double density1, + ScreenOrientation orientation1, + ScreenRotation rotation1, + double width2, + double height2, + double density2, + ScreenOrientation orientation2, + ScreenRotation rotation2, + bool equals) + { + var device1 = new ScreenMetrics( + width: width1, + height: height1, + density: density1, + orientation: orientation1, + rotation: rotation1); + + var device2 = new ScreenMetrics( + width: width2, + height: height2, + density: density2, + orientation: orientation2, + rotation: rotation2); + + if (equals) + { + Assert.True(device1.Equals(device2)); + Assert.True(device1 == device2); + Assert.False(device1 != device2); + Assert.Equal(device1, device2); + Assert.Equal(device1.GetHashCode(), device2.GetHashCode()); + } + else + { + Assert.False(device1.Equals(device2)); + Assert.True(device1 != device2); + Assert.False(device1 == device2); + Assert.NotEqual(device1, device2); + Assert.NotEqual(device1.GetHashCode(), device2.GetHashCode()); + } + } + } +} diff --git a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.android.cs b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.android.cs index 265af6e..fd1fb14 100644 --- a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.android.cs +++ b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.android.cs @@ -17,14 +17,12 @@ namespace Xamarin.Essentials { var displayMetrics = Platform.AppContext.Resources?.DisplayMetrics; - return new ScreenMetrics - { - Orientation = CalculateOrientation(), - Rotation = CalculateRotation(), - Width = displayMetrics?.WidthPixels ?? 0, - Height = displayMetrics?.HeightPixels ?? 0, - Density = displayMetrics?.Density ?? 0 - }; + return new ScreenMetrics( + width: displayMetrics?.WidthPixels ?? 0, + height: displayMetrics?.HeightPixels ?? 0, + density: displayMetrics?.Density ?? 0, + orientation: CalculateOrientation(), + rotation: CalculateRotation()); } static void StartScreenMetricsListeners() diff --git a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.ios.cs b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.ios.cs index 2c7720b..30d0eb1 100644 --- a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.ios.cs +++ b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.ios.cs @@ -13,14 +13,12 @@ namespace Xamarin.Essentials var bounds = UIScreen.MainScreen.Bounds; var scale = UIScreen.MainScreen.Scale; - return new ScreenMetrics - { - Width = bounds.Width * scale, - Height = bounds.Height * scale, - Density = scale, - Orientation = CalculateOrientation(), - Rotation = CalculateRotation() - }; + return new ScreenMetrics( + width: bounds.Width * scale, + height: bounds.Height * scale, + density: scale, + orientation: CalculateOrientation(), + rotation: CalculateRotation()); } static void StartScreenMetricsListeners() diff --git a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.shared.cs b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.shared.cs index a3ee007..697d1ff 100644 --- a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.shared.cs +++ b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.shared.cs @@ -6,8 +6,13 @@ namespace Xamarin.Essentials { static event EventHandler ScreenMetricsChangedInternal; + static ScreenMetrics currentMetrics; + public static ScreenMetrics ScreenMetrics => GetScreenMetrics(); + static void SetCurrent(ScreenMetrics metrics) => + currentMetrics = new ScreenMetrics(metrics.Width, metrics.Height, metrics.Density, metrics.Orientation, metrics.Rotation); + public static event EventHandler ScreenMetricsChanged { add @@ -17,7 +22,10 @@ namespace Xamarin.Essentials ScreenMetricsChangedInternal += value; if (!wasRunning && ScreenMetricsChangedInternal != null) + { + SetCurrent(GetScreenMetrics()); StartScreenMetricsListeners(); + } } remove @@ -35,21 +43,25 @@ namespace Xamarin.Essentials => OnScreenMetricsChanged(new ScreenMetricsChangedEventArgs(metrics)); static void OnScreenMetricsChanged(ScreenMetricsChangedEventArgs e) - => ScreenMetricsChangedInternal?.Invoke(null, e); + { + if (!currentMetrics.Equals(e.Metrics)) + { + SetCurrent(e.Metrics); + ScreenMetricsChangedInternal?.Invoke(null, e); + } + } } public class ScreenMetricsChangedEventArgs : EventArgs { - public ScreenMetricsChangedEventArgs(ScreenMetrics metrics) - { + public ScreenMetricsChangedEventArgs(ScreenMetrics metrics) => Metrics = metrics; - } public ScreenMetrics Metrics { get; } } [Preserve(AllMembers = true)] - public struct ScreenMetrics + public readonly struct ScreenMetrics : IEquatable { internal ScreenMetrics(double width, double height, double density, ScreenOrientation orientation, ScreenRotation rotation) { @@ -60,21 +72,39 @@ namespace Xamarin.Essentials Rotation = rotation; } - public double Width { get; set; } + public double Width { get; } - public double Height { get; set; } + public double Height { get; } - public double Density { get; set; } + public double Density { get; } - public ScreenOrientation Orientation { get; set; } + public ScreenOrientation Orientation { get; } - public ScreenRotation Rotation { get; set; } + public ScreenRotation Rotation { get; } + + public static bool operator ==(ScreenMetrics left, ScreenMetrics right) => + Equals(left, right); + + public static bool operator !=(ScreenMetrics left, ScreenMetrics right) => + !Equals(left, right); + + public override bool Equals(object obj) => + (obj is ScreenMetrics metrics) && Equals(metrics); + + public bool Equals(ScreenMetrics other) => + Width.Equals(other.Width) && + Height.Equals(other.Height) && + Density.Equals(other.Density) && + Orientation.Equals(other.Orientation) && + Rotation.Equals(other.Rotation); + + public override int GetHashCode() => + (Height, Width, Density, Orientation, Rotation).GetHashCode(); } public enum ScreenOrientation { Unknown, - Portrait, Landscape } diff --git a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.uwp.cs b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.uwp.cs index 94014a4..ab51daa 100644 --- a/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.uwp.cs +++ b/Xamarin.Essentials/DeviceDisplay/DeviceDisplay.uwp.cs @@ -16,14 +16,12 @@ namespace Xamarin.Essentials var w = di.ScreenWidthInRawPixels; var h = di.ScreenHeightInRawPixels; - return new ScreenMetrics - { - Width = perpendicular ? h : w, - Height = perpendicular ? w : h, - Density = di.LogicalDpi / 96.0, - Orientation = CalculateOrientation(di), - Rotation = rotation - }; + return new ScreenMetrics( + width: perpendicular ? h : w, + height: perpendicular ? w : h, + density: di.LogicalDpi / 96.0, + orientation: CalculateOrientation(di), + rotation: rotation); } static void StartScreenMetricsListeners() diff --git a/Xamarin.Essentials/Xamarin.Essentials.csproj b/Xamarin.Essentials/Xamarin.Essentials.csproj index 9d8cf56..925c712 100644 --- a/Xamarin.Essentials/Xamarin.Essentials.csproj +++ b/Xamarin.Essentials/Xamarin.Essentials.csproj @@ -46,11 +46,14 @@ - + + + + diff --git a/docs/en/FrameworksIndex/xamarin-essentials-android.xml b/docs/en/FrameworksIndex/xamarin-essentials-android.xml index ba3fa73..bfbe0cd 100644 --- a/docs/en/FrameworksIndex/xamarin-essentials-android.xml +++ b/docs/en/FrameworksIndex/xamarin-essentials-android.xml @@ -424,6 +424,11 @@ + + + + + diff --git a/docs/en/FrameworksIndex/xamarin-essentials-ios.xml b/docs/en/FrameworksIndex/xamarin-essentials-ios.xml index 32b373d..2e5e898 100644 --- a/docs/en/FrameworksIndex/xamarin-essentials-ios.xml +++ b/docs/en/FrameworksIndex/xamarin-essentials-ios.xml @@ -421,6 +421,11 @@ + + + + + diff --git a/docs/en/FrameworksIndex/xamarin-essentials-uwp.xml b/docs/en/FrameworksIndex/xamarin-essentials-uwp.xml index 7d06d3f..6f00ece 100644 --- a/docs/en/FrameworksIndex/xamarin-essentials-uwp.xml +++ b/docs/en/FrameworksIndex/xamarin-essentials-uwp.xml @@ -420,6 +420,11 @@ + + + + + diff --git a/docs/en/FrameworksIndex/xamarin-essentials.xml b/docs/en/FrameworksIndex/xamarin-essentials.xml index e723bd3..76d1a29 100644 --- a/docs/en/FrameworksIndex/xamarin-essentials.xml +++ b/docs/en/FrameworksIndex/xamarin-essentials.xml @@ -420,6 +420,11 @@ + + + + + diff --git a/docs/en/Xamarin.Essentials/ScreenMetrics.xml b/docs/en/Xamarin.Essentials/ScreenMetrics.xml index e4163a6..4c7787c 100644 --- a/docs/en/Xamarin.Essentials/ScreenMetrics.xml +++ b/docs/en/Xamarin.Essentials/ScreenMetrics.xml @@ -1,6 +1,6 @@ - - + + Xamarin.Essentials @@ -9,16 +9,25 @@ System.ValueType - + + + System.IEquatable<Xamarin.Essentials.ScreenMetrics> + + + + + System.Runtime.CompilerServices.IsReadOnly + + This type represents the properties of the current screen. - + - + Property @@ -30,15 +39,85 @@ System.Double - Gets or sets a value representing the current screen density. + Gets a value representing the current screen density. - + This value is a multiple value, such that a retina display would be 2.0 or 3.0. On Windows, the scaling percent of 200% means that the density will be 2.0. + + + + + Method + + Xamarin.Essentials + 1.0.0.0 + + + System.Boolean + + + + + + Object to compare + If equal to another object + If equal + + + + + + + + + + Method + + M:System.IEquatable`1.Equals(`0) + + + Xamarin.Essentials + 1.0.0.0 + + + System.Boolean + + + + + + The other ScreenMetrics + If equal to another ScreenMetrics + If equal + To be added. + + + + + + + Method + + Xamarin.Essentials + 1.0.0.0 + + + System.Int32 + + + + Get the hash code for object. + The hash code + + + + + - + Property @@ -50,15 +129,67 @@ System.Double - Gets or sets the height of the screen for the current orientation. + Gets the height of the screen for the current orientation. This value is the height after rotation, so a landscape iPhone will have a smaller height than width. + + + + + Method + + Xamarin.Essentials + 1.0.0.0 + + + System.Boolean + + + + + + + Left to compare + Right to compare + Equality operator for equals + If equal + + + + + + + + + + Method + + Xamarin.Essentials + 1.0.0.0 + + + System.Boolean + + + + + + + Left to check + Right to check + Inequality check + If not equal + + + + + - + Property @@ -70,13 +201,15 @@ Xamarin.Essentials.ScreenOrientation - Gets or sets the orientation of the device. - + Gets the orientation of the device. + + + If the device is a square device, then the orientation will be portrait. - + Property @@ -88,13 +221,15 @@ Xamarin.Essentials.ScreenRotation - Gets or sets the current rotation from the designed orientation. - + Gets the current rotation from the designed orientation. + + + This represents the degrees that the device has been rotated. For example, if this is a phone (which is designed for portrait) that has been rotated to landscape with the main buttoms to the right, then the device has been rotated to the left by 90 degrees. - + Property @@ -106,8 +241,10 @@ System.Double - Gets or sets the width of the screen in the current orientation. - + Gets the width of the screen in the current orientation. + + + This value is the width after rotation, so a landscape Android phone will have a larget width than height.