Last active
September 6, 2018 10:37
-
-
Save rotorgames/3e5ffa1d32a38fffeabfda742b0535e0 to your computer and use it in GitHub Desktop.
orientation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.ComponentModel; | |
| using CoreFoundation; | |
| using Foundation; | |
| using Rg.Forms.Enumerables.Device; | |
| using Rg.Forms.Events.Device; | |
| using Rg.Forms.Extensions.Device; | |
| using Rg.Forms.iOS.Extensions.Device; | |
| using Rg.Forms.iOS.Renderers.Pages; | |
| using Rg.Forms.Interfaces.Contracts.Services.Device; | |
| using Rg.Forms.Services.Device; | |
| using Rg.Forms.Views.Pages.OrientationPage; | |
| using UIKit; | |
| using Xamarin.Forms; | |
| using Xamarin.Forms.Platform.iOS; | |
| [assembly: ExportRenderer(typeof(RgOrientationPage), typeof(RgOrientationPageRenderer))] | |
| namespace Rg.Forms.iOS.Renderers.Pages | |
| { | |
| public class RgOrientationPageRenderer : PageRenderer | |
| { | |
| public static UIInterfaceOrientationMask SupportedInterfaceOrientations { get; private set; } | |
| #region Private | |
| private readonly IDeviceOrientationService _deviceOrientationService; | |
| private UIInterfaceOrientationMask _supportedInterfaceOrientations; | |
| private ScreenOrientation _lastDeviceOrientation; | |
| private NSObject _applicationEnterBackgroundObserver; | |
| private NSObject _applicationEnterForegroundObserver; | |
| private RgOrientationPage _Element => (RgOrientationPage)Element; | |
| private RequestScreenOrientation RequestOrientation => _Element.RequestOrientation; | |
| #endregion | |
| #region Main Methods | |
| public RgOrientationPageRenderer() | |
| { | |
| _deviceOrientationService = DeviceOrientationService.CreateInstance(); | |
| _deviceOrientationService.UpdatingDelay = 200; | |
| _deviceOrientationService.OrientationChanged += OnSensorOrientationChanged; | |
| } | |
| protected override void Dispose(bool disposing) | |
| { | |
| base.Dispose(disposing); | |
| OnPaused(); | |
| _deviceOrientationService.OrientationChanged -= OnSensorOrientationChanged; | |
| _deviceOrientationService.Dispose(); | |
| } | |
| protected override void OnElementChanged(VisualElementChangedEventArgs e) | |
| { | |
| base.OnElementChanged(e); | |
| if (e.OldElement != null) | |
| { | |
| e.OldElement.PropertyChanged -= OnElementPropertyChanged; | |
| } | |
| if (e.NewElement != null) | |
| { | |
| e.NewElement.PropertyChanged += OnElementPropertyChanged; | |
| UpdateOrientationListeners(); | |
| UpdateOrientation(true); | |
| } | |
| } | |
| private void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) | |
| { | |
| switch (e.PropertyName) | |
| { | |
| case nameof(_Element.RequestOrientation): | |
| UpdateOrientation(true); | |
| break; | |
| case nameof(_Element.UseDeviceSensorOrientation): | |
| UpdateOrientationListeners(); | |
| UpdateOrientation(true); | |
| break; | |
| } | |
| } | |
| public override void ViewWillAppear(bool animated) | |
| { | |
| base.ViewWillAppear(animated); | |
| _applicationEnterForegroundObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, OnApplicationEnterForeground); | |
| _applicationEnterBackgroundObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.WillResignActiveNotification, OnApplicationEnterBackground); | |
| } | |
| public override void ViewWillDisappear(bool animated) | |
| { | |
| base.ViewWillDisappear(animated); | |
| NSNotificationCenter.DefaultCenter.RemoveObserver(_applicationEnterForegroundObserver); | |
| NSNotificationCenter.DefaultCenter.RemoveObserver(_applicationEnterBackgroundObserver); | |
| } | |
| public override void ViewDidAppear(bool animated) | |
| { | |
| base.ViewDidAppear(animated); | |
| OnResumed(); | |
| } | |
| public override void ViewDidDisappear(bool animated) | |
| { | |
| base.ViewDidDisappear(animated); | |
| OnPaused(); | |
| } | |
| #endregion | |
| #region Lifecycle Methods | |
| private void OnApplicationEnterForeground(NSNotification obj) | |
| { | |
| OnResumed(); | |
| } | |
| private void OnApplicationEnterBackground(NSNotification obj) | |
| { | |
| OnPaused(); | |
| } | |
| /// <summary> | |
| /// <para xml:lang="en"> | |
| /// Invoked when application enters in background, display is blocked or when page disappears | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Вызывается когда приложение уходит в фоновый режим, дисплей блокируется или когда страница становится невидимой | |
| /// </para> | |
| /// </summary> | |
| protected virtual void OnResumed() | |
| { | |
| UpdateOrientationListeners(); | |
| UpdateOrientation(true); | |
| } | |
| /// <summary> | |
| /// <para xml:lang="en"> | |
| /// Invoked when application enters in foreground, display is unlocked or when page appearing | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Вызывается когда приложение выходина на передний план, дисплей разблокируется или когда страница становится видимой | |
| /// </para> | |
| /// </summary> | |
| protected virtual void OnPaused() | |
| { | |
| StopSensorOrientationListening(); | |
| } | |
| #endregion | |
| #region Base Orinetation Listeners | |
| private void UpdateOrientationListeners() | |
| { | |
| var isDeviceSendosEnabled = _Element.UseDeviceSensorOrientation; | |
| if (isDeviceSendosEnabled) | |
| { | |
| StartSensorOrientationListening(); | |
| } | |
| else | |
| { | |
| StopSensorOrientationListening(); | |
| } | |
| } | |
| #endregion | |
| #region Device Sensor Orientation Listeners | |
| private void StartSensorOrientationListening() | |
| { | |
| // TODO: Если у IDeviceOrientationService будет проверка которая указывает поддерживается сенсор или нет, | |
| // TODO: тогда добавить проверку сюда | |
| if (true) | |
| { | |
| if (!_deviceOrientationService.IsListening) | |
| _deviceOrientationService.StartListening(); | |
| } | |
| else | |
| { | |
| throw new NotSupportedException("Device doesn't support detect orientation"); | |
| } | |
| } | |
| private void StopSensorOrientationListening() | |
| { | |
| if (_deviceOrientationService.IsListening) | |
| _deviceOrientationService.StopListening(); | |
| } | |
| /// <summary> | |
| /// <para xml:lang="en"> | |
| /// Invoked if <see cref="RgOrientationPage.UseDeviceSensorOrientation"/> was enabled and sensor orientation was changed. | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Вызывается если <see cref="RgOrientationPage.UseDeviceSensorOrientation"/> был включен и ориентация экрана по датчикам была изменена | |
| /// </para> | |
| /// </summary> | |
| /// <param name="sender"></param> | |
| /// <param name="e"></param> | |
| protected virtual void OnSensorOrientationChanged(object sender, DeviceOrientationChangedEventArgs e) | |
| { | |
| UpdateOrientation(); | |
| } | |
| #endregion | |
| #region Native Orientation Supporting Methods | |
| public override bool ShouldAutorotate() | |
| { | |
| return !RequestOrientation.HasFlag(RequestScreenOrientation.Locked); | |
| } | |
| public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations() | |
| { | |
| if (_supportedInterfaceOrientations == 0) | |
| return base.GetSupportedInterfaceOrientations(); | |
| return _supportedInterfaceOrientations; | |
| } | |
| #endregion | |
| #region Orientation Methods | |
| /// <summary> | |
| /// <para xml:lang="en"> | |
| /// Returns current orientation of device or screen | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Возвращает текущую ориентацию устройства или экрана | |
| /// </para> | |
| /// </summary> | |
| /// <returns> | |
| /// <para xml:lang="en"> | |
| /// Device or screen orientation | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Ориентация устройства или экрана | |
| /// </para> | |
| /// </returns> | |
| protected virtual ScreenOrientation GetCurrentDeviceOrientation() | |
| { | |
| if (_Element.UseDeviceSensorOrientation) | |
| { | |
| var deviceOrientation = _deviceOrientationService.LastOrientation; | |
| var screenOrientation = deviceOrientation.ToScreenOrientation(); | |
| if (screenOrientation != ScreenOrientation.Unknown) | |
| _lastDeviceOrientation = screenOrientation; | |
| } | |
| else | |
| { | |
| ScreenOrientation orientation; | |
| var deviceOrientation = UIDevice.CurrentDevice.Orientation; | |
| var statusBarOrientation = UIApplication.SharedApplication.StatusBarOrientation; | |
| if (deviceOrientation != UIDeviceOrientation.Unknown) | |
| orientation = deviceOrientation.ToScreenOrientation(); | |
| else | |
| orientation = statusBarOrientation.ToScreenOrientation(); | |
| if (orientation != ScreenOrientation.Unknown) | |
| _lastDeviceOrientation = orientation; | |
| } | |
| return _lastDeviceOrientation; | |
| } | |
| /// <summary> | |
| /// <para xml:lang="en"> | |
| /// Invoked when screen orientation should be updated | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Вызывается когда ориентация экрана должна быть обновлена | |
| /// </para> | |
| /// </summary> | |
| /// <param name="isForce"> | |
| /// <para xml:lang="en"> | |
| /// If is <c>true</c> then orientation was changed user, if it is <c>false</c> then device orientation was changed device | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Если равно <c>true</c> значит ориентация была изменена пользователем, если <c>false</c> тогда ориентация была изменена устройством | |
| /// </para> | |
| /// </param> | |
| protected virtual void UpdateOrientation(bool isForce = false) | |
| { | |
| var requestOrientation = RequestOrientation; | |
| var useDeviceSensorOrientation = _Element.UseDeviceSensorOrientation; | |
| var deviceOrientation = GetCurrentDeviceOrientation(); | |
| if (deviceOrientation == ScreenOrientation.Unknown) | |
| return; | |
| UIInterfaceOrientation? interfaceOrientation = null; | |
| UIInterfaceOrientationMask? interfaceOrientationMask = null; | |
| if (deviceOrientation.IsPortrait()) | |
| { | |
| if (requestOrientation.HasPortrait()) | |
| { | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.Portrait) | |
| && (deviceOrientation == ScreenOrientation.Portrait || (!requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait) && isForce))) | |
| interfaceOrientation = UIInterfaceOrientation.Portrait; | |
| else if (requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait) | |
| && (deviceOrientation == ScreenOrientation.PortraitUpsideDown || (!requestOrientation.HasFlag(RequestScreenOrientation.Portrait) && isForce))) | |
| interfaceOrientation = UIInterfaceOrientation.PortraitUpsideDown; | |
| } | |
| else if (requestOrientation.HasLandscape() && isForce) | |
| { | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.Landscape)) | |
| interfaceOrientation = UIInterfaceOrientation.LandscapeRight; | |
| else if (requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape)) | |
| interfaceOrientation = UIInterfaceOrientation.LandscapeLeft; | |
| } | |
| } | |
| else if (deviceOrientation.IsLandscape()) | |
| { | |
| if (requestOrientation.HasLandscape()) | |
| { | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.Landscape) | |
| && (deviceOrientation == ScreenOrientation.LandscapeRight || (!requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape) && isForce))) | |
| interfaceOrientation = UIInterfaceOrientation.LandscapeRight; | |
| else if (requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape) | |
| && (deviceOrientation == ScreenOrientation.LandscapeLeft || (!requestOrientation.HasFlag(RequestScreenOrientation.Landscape) && isForce))) | |
| interfaceOrientation = UIInterfaceOrientation.LandscapeLeft; | |
| } | |
| else if (requestOrientation.HasPortrait() && isForce) | |
| { | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.Portrait)) | |
| interfaceOrientation = UIInterfaceOrientation.Portrait; | |
| else if (requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait)) | |
| interfaceOrientation = UIInterfaceOrientation.PortraitUpsideDown; | |
| } | |
| } | |
| if (useDeviceSensorOrientation) | |
| { | |
| if (interfaceOrientation == UIInterfaceOrientation.Portrait) | |
| interfaceOrientationMask = UIInterfaceOrientationMask.Portrait; | |
| if (interfaceOrientation == UIInterfaceOrientation.PortraitUpsideDown) | |
| interfaceOrientationMask = UIInterfaceOrientationMask.PortraitUpsideDown; | |
| if (interfaceOrientation == UIInterfaceOrientation.LandscapeRight) | |
| interfaceOrientationMask = UIInterfaceOrientationMask.LandscapeRight; | |
| if (interfaceOrientation == UIInterfaceOrientation.LandscapeLeft) | |
| interfaceOrientationMask = UIInterfaceOrientationMask.LandscapeLeft; | |
| } | |
| else | |
| { | |
| interfaceOrientationMask = 0; | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.Portrait)) | |
| interfaceOrientationMask |= UIInterfaceOrientationMask.Portrait; | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait)) | |
| interfaceOrientationMask |= UIInterfaceOrientationMask.PortraitUpsideDown; | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.Landscape)) | |
| interfaceOrientationMask |= UIInterfaceOrientationMask.LandscapeRight; | |
| if (requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape)) | |
| interfaceOrientationMask |= UIInterfaceOrientationMask.LandscapeLeft; | |
| } | |
| if (interfaceOrientationMask == null || interfaceOrientation == null) | |
| return; | |
| //Debug.WriteLine($"\nReques orientation: {requestOrientation} " + | |
| // $"\nDeviceOrientation: {deviceOrientation} " + | |
| // $"\nInterfaceOrientation {interfaceOrientation}" + | |
| // $"\nInterfaceOrientationMask {interfaceOrientationMask}"); | |
| SupportedInterfaceOrientations = _supportedInterfaceOrientations = interfaceOrientationMask.Value; | |
| SetScreenOrientation(interfaceOrientation.Value); | |
| } | |
| #endregion | |
| #region Screen Methods | |
| /// <summary> | |
| /// <para xml:lang="en"> | |
| /// Sets new screen orientation | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Устанавливает новую ориентацию экрана | |
| /// </para> | |
| /// </summary> | |
| /// <param name="uiOrientation"> | |
| /// <para xml:lang="en"> | |
| /// New screen orientation | |
| /// </para> | |
| /// <para xml:lang="ru"> | |
| /// Новая ориентация экрана | |
| /// </para> | |
| /// </param> | |
| protected virtual void SetScreenOrientation(UIInterfaceOrientation uiOrientation) | |
| { | |
| DispatchQueue.MainQueue.DispatchAsync(() => | |
| { | |
| NSNumber newOrientationNumber = new NSNumber((int)uiOrientation); | |
| NSString key = new NSString("orientation"); | |
| var currentDeviceOrientation = (NSNumber)UIDevice.CurrentDevice.ValueForKey(key); | |
| UIDevice.CurrentDevice.SetValueForKey(newOrientationNumber, key); | |
| UIDevice.CurrentDevice.SetValueForKey(currentDeviceOrientation, key); | |
| AttemptRotationToDeviceOrientation(); | |
| //UIDevice.CurrentDevice.BeginGeneratingDeviceOrientationNotifications(); | |
| }); | |
| } | |
| #endregion | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Threading.Tasks; | |
| using CoreMotion; | |
| using Foundation; | |
| using Rg.Forms.Enumerables.Device; | |
| using Rg.Forms.Events.Device; | |
| using Rg.Forms.Extensions.Device; | |
| using Rg.Forms.iOS.Services.Device; | |
| using Rg.Forms.Interfaces.Contracts.Services.Device; | |
| using Rg.Forms.Services.Device; | |
| [assembly: Xamarin.Forms.Dependency(typeof(DeviceOrientationServiceImpl))] | |
| namespace Rg.Forms.iOS.Services.Device | |
| { | |
| public class DeviceOrientationServiceImpl : IDeviceOrientationService | |
| { | |
| #region Constants | |
| private const int DefaultUpdatingDelay = DeviceOrientationService.DefaultUpdatingDelay; | |
| private const double FaceAngle = 0.9; | |
| private const double PortraitAndLandscapeAngle = 0.4; | |
| #endregion | |
| #region Private Fields | |
| private CMMotionManager _motionManager; | |
| private bool _isDisposed; | |
| private DeviceOrientation _lastOrientation; | |
| private DeviceOrientation _lastSetOrientation; | |
| #endregion | |
| #region Events | |
| public event EventHandler<DeviceOrientationChangedEventArgs> OrientationChanged; | |
| #endregion | |
| #region Properties | |
| public bool IsListening { get; private set; } | |
| public bool IsUpdatedIfUnknown { get; set; } | |
| public int UpdatingDelay { get; set; } = DefaultUpdatingDelay; | |
| public DeviceOrientation LastOrientation | |
| { | |
| get | |
| { | |
| if (_isDisposed) | |
| throw new ObjectDisposedException(nameof(IDeviceOrientationService)); | |
| return _lastOrientation; | |
| } | |
| private set { _lastOrientation = value; } | |
| } | |
| public bool IsLandscape => LastOrientation.IsLandscape(); | |
| public bool IsPortrait => LastOrientation.IsPortrait(); | |
| public bool IsFace => LastOrientation.IsFace(); | |
| #endregion | |
| #region Main Methods | |
| public DeviceOrientationServiceImpl() | |
| { | |
| _motionManager = new CMMotionManager(); | |
| } | |
| public void Dispose() | |
| { | |
| if (_isDisposed) | |
| return; | |
| _isDisposed = true; | |
| StopListening(); | |
| _motionManager.Dispose(); | |
| _motionManager = null; | |
| } | |
| #endregion | |
| #region Listening Methods | |
| public void StartListening() | |
| { | |
| if (IsListening) | |
| return; | |
| IsListening = true;; | |
| if (_motionManager.DeviceMotionAvailable) | |
| { | |
| _motionManager.DeviceMotionUpdateInterval = 0.4; | |
| _motionManager.StartDeviceMotionUpdates(NSOperationQueue.MainQueue, OnDeviceMotion); | |
| } | |
| } | |
| private void OnDeviceMotion(CMDeviceMotion motion, NSError error) | |
| { | |
| var orientation = DeviceOrientation.Unknown; | |
| var gravity = motion.Gravity; | |
| var x = gravity.X; | |
| var y = gravity.Y; | |
| var z = gravity.Z; | |
| if (z <= -FaceAngle) | |
| { | |
| orientation = DeviceOrientation.FaceUp; | |
| } | |
| else if (z >= FaceAngle) | |
| { | |
| orientation = DeviceOrientation.FaceDown; | |
| } | |
| else if (x >= -PortraitAndLandscapeAngle && x <= PortraitAndLandscapeAngle && y <= 0) | |
| { | |
| orientation = DeviceOrientation.Portrait; | |
| } | |
| else if (x >= -PortraitAndLandscapeAngle && x <= PortraitAndLandscapeAngle && y > 0) | |
| { | |
| orientation = DeviceOrientation.PortraitUpsideDown; | |
| } | |
| else if (x < -PortraitAndLandscapeAngle) | |
| { | |
| orientation = DeviceOrientation.LandscapeRight; | |
| } | |
| else if (x > PortraitAndLandscapeAngle) | |
| { | |
| orientation = DeviceOrientation.LandscapeLeft; | |
| } | |
| SetOrientation(orientation); | |
| } | |
| public void StopListening() | |
| { | |
| if(!IsListening) | |
| return; | |
| IsListening = false; | |
| _motionManager.StopDeviceMotionUpdates(); | |
| } | |
| #endregion | |
| #region Orientation Methods | |
| private async void SetOrientation(DeviceOrientation orientation) | |
| { | |
| if (_lastSetOrientation == orientation || (!IsUpdatedIfUnknown && orientation == DeviceOrientation.Unknown)) | |
| return; | |
| _lastSetOrientation = orientation; | |
| if (LastOrientation != DeviceOrientation.Unknown) | |
| await Task.Delay(UpdatingDelay); | |
| if (_lastSetOrientation != LastOrientation) | |
| { | |
| LastOrientation = _lastSetOrientation; | |
| OrientationChanged?.Invoke(this, new DeviceOrientationChangedEventArgs(_lastSetOrientation)); | |
| } | |
| } | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment