maui-linux/Xamarin.Forms.Platform.iOS/Forms.cs

337 строки
9.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms.Internals;
using Foundation;
#if __MOBILE__
using UIKit;
using Xamarin.Forms.Platform.iOS;
using TNativeView = UIKit.UIView;
#else
using AppKit;
using Xamarin.Forms.Platform.MacOS;
using TNativeView = AppKit.NSView;
#endif
namespace Xamarin.Forms
{
public static class Forms
{
//Preserve GetCallingAssembly
static readonly bool nevertrue = false;
public static bool IsInitialized { get; private set; }
#if __MOBILE__
static bool? s_isiOS9OrNewer;
static bool? s_isiOS10OrNewer;
#endif
static Forms()
{
if (nevertrue)
Assembly.GetCallingAssembly();
}
#if __MOBILE__
internal static bool IsiOS9OrNewer
{
get
{
if (!s_isiOS9OrNewer.HasValue)
s_isiOS9OrNewer = UIDevice.CurrentDevice.CheckSystemVersion(9, 0);
return s_isiOS9OrNewer.Value;
}
}
internal static bool IsiOS10OrNewer
{
get
{
if (!s_isiOS10OrNewer.HasValue)
s_isiOS10OrNewer = UIDevice.CurrentDevice.CheckSystemVersion(10, 0);
return s_isiOS10OrNewer.Value;
}
}
#endif
static IReadOnlyList<string> s_flags;
public static IReadOnlyList<string> Flags => s_flags ?? (s_flags = new List<string>().AsReadOnly());
public static void SetFlags(params string[] flags)
{
if (IsInitialized)
{
throw new InvalidOperationException($"{nameof(SetFlags)} must be called before {nameof(Init)}");
}
s_flags = flags.ToList().AsReadOnly();
}
public static void Init()
{
if (IsInitialized)
return;
IsInitialized = true;
Color.SetAccent(Color.FromRgba(50, 79, 133, 255));
Log.Listeners.Add(new DelegateLogListener((c, m) => Trace.WriteLine(m, c)));
#if __MOBILE__
Device.SetIdiom(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad ? TargetIdiom.Tablet : TargetIdiom.Phone);
#else
Device.SetIdiom(TargetIdiom.Desktop);
#endif
Device.PlatformServices = new IOSPlatformServices();
Device.Info = new IOSDeviceInfo();
Internals.Registrar.RegisterAll(new[]
{ typeof(ExportRendererAttribute), typeof(ExportCellAttribute), typeof(ExportImageSourceHandlerAttribute) });
ExpressionSearch.Default = new iOSExpressionSearch();
}
public static event EventHandler<ViewInitializedEventArgs> ViewInitialized;
internal static void SendViewInitialized(this VisualElement self, TNativeView nativeView)
{
ViewInitialized?.Invoke(self, new ViewInitializedEventArgs { View = self, NativeView = nativeView });
}
class iOSExpressionSearch : ExpressionVisitor, IExpressionSearch
{
List<object> _results;
Type _targetType;
public List<T> FindObjects<T>(Expression expression) where T : class
{
_results = new List<object>();
_targetType = typeof(T);
Visit(expression);
return _results.Select(o => o as T).ToList();
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression is ConstantExpression && node.Member is FieldInfo)
{
var container = ((ConstantExpression)node.Expression).Value;
var value = ((FieldInfo)node.Member).GetValue(container);
if (_targetType.IsInstanceOfType(value))
_results.Add(value);
}
return base.VisitMember(node);
}
}
internal class IOSDeviceInfo : DeviceInfo
{
#if __MOBILE__
readonly NSObject _notification;
#endif
readonly Size _scaledScreenSize;
readonly double _scalingFactor;
public IOSDeviceInfo()
{
#if __MOBILE__
_notification = UIDevice.Notifications.ObserveOrientationDidChange((sender, args) => CurrentOrientation = UIDevice.CurrentDevice.Orientation.ToDeviceOrientation());
_scalingFactor = UIScreen.MainScreen.Scale;
_scaledScreenSize = new Size(UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height);
#else
_scalingFactor = NSScreen.MainScreen.BackingScaleFactor;
_scaledScreenSize = new Size(NSScreen.MainScreen.Frame.Width, NSScreen.MainScreen.Frame.Height);
#endif
PixelScreenSize = new Size(_scaledScreenSize.Width * _scalingFactor, _scaledScreenSize.Height * _scalingFactor);
}
public override Size PixelScreenSize { get; }
public override Size ScaledScreenSize => _scaledScreenSize;
public override double ScalingFactor => _scalingFactor;
protected override void Dispose(bool disposing)
{
#if __MOBILE__
_notification.Dispose();
#endif
base.Dispose(disposing);
}
}
class IOSPlatformServices : IPlatformServices
{
static readonly MD5CryptoServiceProvider s_checksum = new MD5CryptoServiceProvider();
public void BeginInvokeOnMainThread(Action action)
{
NSRunLoop.Main.BeginInvokeOnMainThread(action.Invoke);
}
public Ticker CreateTicker()
{
return new CADisplayLinkTicker();
}
public Assembly[] GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
public string GetMD5Hash(string input)
{
var bytes = s_checksum.ComputeHash(Encoding.UTF8.GetBytes(input));
var ret = new char[32];
for (var 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);
}
public double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes)
{
// We make these up anyway, so new sizes didn't really change
// iOS docs say default button font size is 15, default label font size is 17 so we use those as the defaults.
switch (size)
{
case NamedSize.Default:
return typeof(Button).IsAssignableFrom(targetElementType) ? 15 : 17;
case NamedSize.Micro:
return 12;
case NamedSize.Small:
return 14;
case NamedSize.Medium:
return 17;
case NamedSize.Large:
return 22;
default:
throw new ArgumentOutOfRangeException("size");
}
}
public async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
{
using (var client = GetHttpClient())
using (var response = await client.GetAsync(uri, cancellationToken))
{
if (!response.IsSuccessStatusCode)
{
Log.Warning("HTTP Request", $"Could not retrieve {uri}, status code {response.StatusCode}");
return null;
}
return await response.Content.ReadAsStreamAsync();
}
}
public IIsolatedStorageFile GetUserStoreForApplication()
{
return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication());
}
public bool IsInvokeRequired => !NSThread.IsMain;
#if __MOBILE__
public string RuntimePlatform => Device.iOS;
#else
public string RuntimePlatform => Device.macOS;
#endif
public void OpenUriAction(Uri uri)
{
var url = NSUrl.FromString(uri.OriginalString) ?? new NSUrl(uri.Scheme, uri.Host, uri.PathAndQuery);
#if __MOBILE__
UIApplication.SharedApplication.OpenUrl(url);
#else
NSWorkspace.SharedWorkspace.OpenUrl(url);
#endif
}
public void StartTimer(TimeSpan interval, Func<bool> callback)
{
NSTimer timer = NSTimer.CreateRepeatingTimer(interval, t =>
{
if (!callback())
t.Invalidate();
});
NSRunLoop.Main.AddTimer(timer, NSRunLoopMode.Common);
}
HttpClient GetHttpClient()
{
var proxy = CoreFoundation.CFNetwork.GetSystemProxySettings();
var handler = new HttpClientHandler();
if (!string.IsNullOrEmpty(proxy.HTTPProxy))
{
handler.Proxy = CoreFoundation.CFNetwork.GetDefaultProxy();
handler.UseProxy = true;
}
return new HttpClient(handler);
}
static int Hex(int v)
{
if (v < 10)
return '0' + v;
return 'a' + v - 10;
}
public class _IsolatedStorageFile : IIsolatedStorageFile
{
readonly IsolatedStorageFile _isolatedStorageFile;
public _IsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
{
_isolatedStorageFile = isolatedStorageFile;
}
public Task CreateDirectoryAsync(string path)
{
_isolatedStorageFile.CreateDirectory(path);
return Task.FromResult(true);
}
public Task<bool> GetDirectoryExistsAsync(string path)
{
return Task.FromResult(_isolatedStorageFile.DirectoryExists(path));
}
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 Task<Stream> OpenFileAsync(string path, Internals.FileMode mode, Internals.FileAccess access)
{
Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access);
return Task.FromResult(stream);
}
public Task<Stream> OpenFileAsync(string path, Internals.FileMode mode, Internals.FileAccess access, Internals.FileShare share)
{
Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access,
(System.IO.FileShare)share);
return Task.FromResult(stream);
}
}
}
}
}