diff --git a/Perspex.sln b/Perspex.sln
index 5f5e82fca7..1ddd288c70 100644
--- a/Perspex.sln
+++ b/Perspex.sln
@@ -123,6 +123,7 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Skia.iOS", "src\Skia\Perspex.Skia.iOS\Perspex.Skia.iOS.csproj", "{47BE08A7-5985-410B-9FFC-2264B8EA595F}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TestApplicationShared", "samples\TestApplicationShared\TestApplicationShared.shproj", "{78345174-5B52-4A14-B9FD-D5F2428137F0}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Android", "src\Android\Perspex.Android\Perspex.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
@@ -136,17 +137,18 @@ Global
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{db070a10-bf39-4752-8456-86e9d5928478}*SharedItemsImports = 4
- src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
+ src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{78345174-5b52-4a14-b9fd-d5f2428137f0}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{54f237d5-a70a-4752-9656-0c70b1a7b047}*SharedItemsImports = 4
+ samples\TestApplicationShared\TestApplicationShared.projitems*{ff69b927-c545-49ae-8e16-3d14d621aa12}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
- src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
+ src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{e3a1060b-50d0-44e8-88b6-f44ef2e5bd72}*SharedItemsImports = 4
- src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
+ src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -1157,16 +1159,36 @@ Global
{47BE08A7-5985-410B-9FFC-2264B8EA595F}.Release|iPhone.Build.0 = Release|Any CPU
{47BE08A7-5985-410B-9FFC-2264B8EA595F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{47BE08A7-5985-410B-9FFC-2264B8EA595F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Build.0 = Release|Any CPU
{FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/samples/TestApplicationShared/MainWindow.cs b/samples/TestApplicationShared/MainWindow.cs
index 5df09b0d03..6a412a95b5 100644
--- a/samples/TestApplicationShared/MainWindow.cs
+++ b/samples/TestApplicationShared/MainWindow.cs
@@ -77,45 +77,30 @@ namespace TestApplication
TabControl container;
+
Window window = new Window
{
Title = "Perspex Test Application",
- Width = 900,
- Height = 480,
- Content = new Grid
+ //Width = 900,
+ //Height = 480,
+ Content = (container = new TabControl
{
- ColumnDefinitions = new ColumnDefinitions
- {
- new ColumnDefinition(1, GridUnitType.Star),
- new ColumnDefinition(1, GridUnitType.Star),
- },
- RowDefinitions = new RowDefinitions
+ Padding = new Thickness(5),
+ Items = new[]
{
- new RowDefinition(GridLength.Auto),
- new RowDefinition(1, GridUnitType.Star),
- new RowDefinition(GridLength.Auto),
+ ButtonsTab(),
+ TextTab(),
+ HtmlTab(),
+ ImagesTab(),
+ ListsTab(),
+ LayoutTab(),
+ AnimationsTab(),
},
- Children = new Controls
- {
- (container = new TabControl
- {
- Padding = new Thickness(5),
- Items = new[]
- {
- ButtonsTab(),
- TextTab(),
- HtmlTab(),
- ImagesTab(),
- ListsTab(),
- LayoutTab(),
- AnimationsTab(),
- },
- Transition = new CrossFade(TimeSpan.FromSeconds(0.25)),
- [Grid.RowProperty] = 1,
- [Grid.ColumnSpanProperty] = 2,
- })
- }
- },
+ Transition = new CrossFade(TimeSpan.FromSeconds(0.25)),
+ [Grid.RowProperty] = 1,
+ [Grid.ColumnSpanProperty] = 2,
+ })
+
};
container.Classes.Add(":container");
diff --git a/src/Android/Perspex.Android/AndroidAssetLoader.cs b/src/Android/Perspex.Android/AndroidAssetLoader.cs
deleted file mode 100644
index f10d0a8571..0000000000
--- a/src/Android/Perspex.Android/AndroidAssetLoader.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using Perspex.Android.Platform.Specific;
-using Perspex.Platform;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-
-namespace Perspex.Android.PlatformSupport
-{
- ///
- /// Loads assets compiled into the application binary.
- ///
- public class AndroidAssetLoader : IAssetLoader
- {
- internal Assembly DefaultAssetAssembly { get; set; }
- internal string DefaultResourcePrefix { get; set; }
-
- public AndroidAssetLoader()
- {
- //Assembly.GetEntryAssembly() not working on mono/xamarin for android!!!
- //may be this is better way to have at least the application assembly as default one
- DefaultAssetAssembly = PerspexLocator.Current.GetService().Activity.GetType().Assembly;
- }
-
- private static readonly Dictionary AssemblyNameCache = new Dictionary();
-
- ///
- /// Checks if an asset with the specified URI exists.
- ///
- /// The URI.
- /// True if the asset could be found; otherwise false.
- public bool Exists(Uri uri)
- {
- return OpenInternal(uri, false) != null;
- }
-
- ///
- /// Opens the resource with the requested URI.
- ///
- /// The URI.
- /// A stream containing the resource contents.
- ///
- /// The resource was not found.
- ///
- public Stream Open(Uri uri)
- {
- return OpenInternal(uri, true);
- }
-
- private Stream OpenInternal(Uri uri, bool throwIfNotSuccess)
- {
- //uri.AbsolutePath is NotFiniteNumberException escaped adn can cause problems in assembly names with spaces
- bool isAbsolute = uri.IsAbsoluteUri;
- var parts = (isAbsolute ? uri.LocalPath : uri.OriginalString).Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
- var asm = parts.Length == 1 ? DefaultAssetAssembly : GetAssembly(parts[0]);
- var typeName = parts[parts.Length == 1 ? 0 : 1];
- if (typeName != null && !isAbsolute && !typeName.StartsWith(DefaultResourcePrefix))
- {
- typeName = DefaultResourcePrefix + typeName;
- }
-
- var rv = asm.GetManifestResourceStream(typeName);
- if (rv == null && throwIfNotSuccess)
- throw new FileNotFoundException($"The resource {uri} could not be found.");
- return rv;
- }
-
- private static Assembly GetAssembly(string name)
- {
- Assembly rv;
- if (!AssemblyNameCache.TryGetValue(name, out rv))
- AssemblyNameCache[name] = rv =
- AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == name)
- ?? Assembly.Load(name);
- return rv;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Android/Perspex.Android/AndroidPlatform.cs b/src/Android/Perspex.Android/AndroidPlatform.cs
index 6e6b061d8c..feb4288c8c 100644
--- a/src/Android/Perspex.Android/AndroidPlatform.cs
+++ b/src/Android/Perspex.Android/AndroidPlatform.cs
@@ -1,11 +1,7 @@
-using Android.OS;
using Perspex.Android.CanvasRendering;
using Perspex.Android.Platform;
using Perspex.Android.Platform.Input;
using Perspex.Android.Platform.Specific;
-using Perspex.Android.Platform.Specific.Helpers;
-using Perspex.Android.PlatformSupport;
-using Perspex.Animation;
using Perspex.Controls.Platform;
using Perspex.Input;
using Perspex.Input.Platform;
@@ -14,228 +10,44 @@ using Perspex.Shared.PlatformSupport;
using Perspex.Skia;
using System;
using System.Collections.Generic;
-using System.Reactive.Disposables;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
namespace Perspex.Android
{
- public class AndroidPlatform : IPlatformThreadingInterface, IPlatformSettings
+ public class AndroidPlatform : IPlatformSettings
{
public static readonly AndroidPlatform Instance = new AndroidPlatform();
public Size DoubleClickSize => new Size(4, 4);
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(200);
-
- private HashSet _renderingTask = new HashSet();
-
- public void RegisterRenderingTaskId(int taskId)
- {
- _renderingTask.Add(taskId);
- }
-
- public void UnRegisterRenderingTaskId(int taskId)
- {
- _renderingTask.Add(taskId);
- }
-
- public bool CurrentThreadIsLoopThread
- {
- get
- {
- bool res = Looper.MainLooper.Thread.Equals(Java.Lang.Thread.CurrentThread());
- if (!res)
- {
- if (Task.CurrentId != null)
- {
- int id = Task.CurrentId.Value;
- res = _renderingTask.Contains(Task.CurrentId.Value);
- }
- }
-
- return res;
- }
- }
-
- public void RunLoop(CancellationToken cancellationToken)
- {
- }
-
- private TimeSpan _animationTick = TimeSpan.FromSeconds(1.0 / Animate.FramesPerSecond);
-
- public IDisposable StartTimer(TimeSpan interval, Action tick)
- {
- //we should be carefull about the smallest amount of time the timer ticks
- //as on android for animations so far there is some issue with 60 fps
- //for now we can get set it to 16 fps with observable timer
- //and still random native exceptions causing app to stop!!!!
- //better not use it
- //return Observable.Timer(interval, interval).Subscribe(_ => tick());
-
- //System.Threading.Timer
- //int ms = (int)interval.TotalMilliseconds;
- ////regular timer is working perfect
- //return new System.Threading.Timer(_ => tick(), null, ms, ms);
-
- //when interval is 0 normal System.Timers.Timer is not working
- //so we ween other strategy for that
- if (interval.TotalMilliseconds == 0)
- {
- if(DefaultViewDrawType == ViewDrawType.Skia)
- {
- //or start timer with low enough interval 10ms ???
- //with skia when animation active if we use RunOnUiThread UI is not handling the events!!! issue or not?
- interval = TimeSpan.FromMilliseconds(10);
- }
- else
- {
- //android ui thread
- PerspexLocator.Current.GetService().Activity.RunOnUiThread(tick);
- return Disposable.Empty;
- }
- }
- else if (OverrideAnimateFramesPerSecond > 0)
- {
- if (_animationTick >= interval)
- {
- interval = TimeSpan.FromSeconds(1.0 / OverrideAnimateFramesPerSecond);
- }
- }
-
- //working not very bad with standard timer
- //var timer = new System.Timers.Timer(interval.TotalMilliseconds);
- //System.Timers.ElapsedEventHandler elapsed = (s, e) => tick();
- //timer.Elapsed += elapsed;
- //timer.Start();
- //return Disposable.Create(() =>
- //{
- // timer.Stop();
- // timer.Elapsed -= elapsed;
- // timer.Dispose();
- //});
-
- //common ui timer is optimizing performance
- //a lot almost twice, also may be processor usage
- //most probably because it optimizes invalidation calls in animations and using less system resources
- CommonUITimer timer;
- if (!_commonTimers.TryGetValue(interval, out timer))
- {
- timer = new CommonUITimer(interval);
- _commonTimers.Add(interval, timer);
- }
-
- timer.AddAction(tick);
-
- return Disposable.Create(() =>
- {
- timer.RemoveAction(tick);
-
- if (timer.ActiveActionsCount == 0)
- {
- timer.Dispose();
- _commonTimers.Remove(interval);
- }
- });
- }
-
- private Dictionary _commonTimers = new Dictionary();
-
- public void Signal()
- {
- EnsureInvokeOnMainThread(() => Signaled?.Invoke());
- }
-
- public event Action Signaled;
-
- public static void Initialize()
+
+ AndroidPlatform()
{
+ var settings = new PlatformSettings();
PerspexLocator.CurrentMutable
.Bind().ToTransient()
.Bind().ToTransient()
.Bind().ToSingleton()
.Bind().ToSingleton()
- .Bind().ToConstant(Instance)
- .Bind().ToConstant(Instance)
+ .Bind().ToConstant(this)
+ .Bind().ToConstant(new AndroidThreadingInterface())
.Bind().ToTransient()
- .Bind().ToSingleton()
.Bind().ToTransient()
- .Bind().ToConstant(new PlatformSettings());
- ;
-
- Instance.RegisterViewDrawType();
- //set defaults for simple resources
- //Assembly.GetEntryAssembly(); //not working on mono for android
- var appType = Application.Current.GetType();
- var entryAssembly = appType.Assembly;
- AndroidPlatform.Instance.InitializeAssetsLoader(entryAssembly, appType.Namespace + ".");
- // SharedPlatform.Register();
- //we have custom Assetloader so no need to overwrite it
- PerspexLocator.CurrentMutable
- .Bind().ToSingleton();
- }
-
- public void RegisterViewDrawType()
- {
- if (Instance.DefaultViewDrawType == ViewDrawType.Skia)
- {
- //do the default initialization for skia rendering
- SkiaPlatform.Initialize();
- PerspexLocator.CurrentMutable.Bind().ToSingleton();
- }
- else
- {
- AndroidPlatformRender.Initialize();
-
- if (Instance.DefaultViewDrawType == ViewDrawType.SurfaceViewCanvasOnDraw)
- {
- PerspexLocator.CurrentMutable.Bind().ToSingleton();
- }
- else
- {
- PerspexLocator.CurrentMutable.Bind().ToSingleton();
- }
-
- PerspexLocator.CurrentMutable.Bind().ToTransient();
- }
- }
+ .Bind().ToConstant(settings);
- public void RegisterViewPointUnits()
- {
- PerspexLocator.CurrentMutable.Bind().ToSingleton();
- }
+ SkiaPlatform.Initialize();
+ Application.SuppressPlatformInitialization();
+ PerspexLocator.CurrentMutable.Bind().ToSingleton();
- public void InitializeAssetsLoader(Assembly assembly, string defaultResourcePrefix)
- {
- var assetLoder = PerspexLocator.Current.GetService() as AndroidAssetLoader;
- if (assetLoder != null)
- {
- assetLoder.DefaultAssetAssembly = assembly;
- assetLoder.DefaultResourcePrefix = defaultResourcePrefix;
- }
+ var scale = global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity;
+ PerspexLocator.Current.GetService().LayoutScalingFactor = scale;
+ PerspexLocator.Current.GetService().RenderScalingFactor = scale;
+
+ //we have custom Assetloader so no need to overwrite it
+
}
- private void EnsureInvokeOnMainThread(Action action)
+ public void Init(Type applicationType)
{
- var mainHandler = new Handler(global::Android.App.Application.Context.MainLooper);
- mainHandler.Post(action);
+ SharedPlatform.Register(applicationType.Assembly);
}
-
- public bool DrawDebugInfo { get; set; } = false;
-
- ///
- /// Default View DrawType
- /// WindowImpl supports:
- /// ViewDrawType.CanvasOnDraw
- /// ViewDrawType.BitmapOnPreDraw
- /// BitmapBackgroundRender
- /// by default it is ViewDrawType.CanvasOnDraw simples way of drawing view
- /// SurfaceWindowImpl supports:
- /// SurfaceViewCanvasOnDraw
- ///
- public ViewDrawType DefaultViewDrawType { get; set; } = ViewDrawType.Skia;
-
- public Platform.CanvasPlatform.PointUnit DefaultPointUnit { get; set; } = Platform.CanvasPlatform.PointUnit.DP;
-
- public int OverrideAnimateFramesPerSecond { get; set; } = -1;
}
}
\ No newline at end of file
diff --git a/src/Android/Perspex.Android/AndroidThreadingInterface.cs b/src/Android/Perspex.Android/AndroidThreadingInterface.cs
new file mode 100644
index 0000000000..072b507b79
--- /dev/null
+++ b/src/Android/Perspex.Android/AndroidThreadingInterface.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Text;
+using System.Threading;
+using Android.App;
+using Android.Content;
+using Android.OS;
+using Android.Runtime;
+using Android.Views;
+using Android.Widget;
+using Perspex.Platform;
+
+namespace Perspex.Android
+{
+ class AndroidThreadingInterface : IPlatformThreadingInterface
+ {
+ private Handler _handler;
+
+ public AndroidThreadingInterface()
+ {
+ _handler = new Handler(global::Android.App.Application.Context.MainLooper);
+ }
+
+ public void RunLoop(CancellationToken cancellationToken)
+ {
+ return;
+ }
+
+ public IDisposable StartTimer(TimeSpan interval, Action tick)
+ {
+ if (interval.TotalMilliseconds < 10)
+ interval = TimeSpan.FromMilliseconds(10);
+ object l = new object();
+ var stopped = false;
+ Timer timer = null;
+ var scheduled = false;
+ timer = new Timer(_ =>
+ {
+ lock (l)
+ {
+ if (stopped)
+ {
+ timer.Dispose();
+ return;
+ }
+ if (scheduled)
+ return;
+ scheduled = true;
+ EnsureInvokeOnMainThread(() =>
+ {
+ tick();
+ lock (l)
+ {
+ scheduled = false;
+ }
+ });
+ }
+ }, null, TimeSpan.Zero, interval);
+
+ return Disposable.Create(() =>
+ {
+ lock (l)
+ {
+ stopped = true;
+ timer.Dispose();
+ }
+ });
+ }
+
+ private void EnsureInvokeOnMainThread(Action action) => _handler.Post(action);
+
+ public void Signal()
+ {
+ EnsureInvokeOnMainThread(() => Signaled?.Invoke());
+ }
+
+ public bool CurrentThreadIsLoopThread => Looper.MainLooper.Thread.Equals(Java.Lang.Thread.CurrentThread());
+ public event Action Signaled;
+ }
+}
+
\ No newline at end of file
diff --git a/src/Android/Perspex.Android/CanvasRendering/AndroidGraphicsExtensions.cs b/src/Android/Perspex.Android/CanvasRendering/AndroidGraphicsExtensions.cs
deleted file mode 100644
index 2ee877352a..0000000000
--- a/src/Android/Perspex.Android/CanvasRendering/AndroidGraphicsExtensions.cs
+++ /dev/null
@@ -1,246 +0,0 @@
-using Perspex.Android.Platform.CanvasPlatform;
-using Perspex.Android.Platform.Specific;
-using Perspex.Media;
-using System;
-using AAllignment = Android.Text.Layout.Alignment;
-using ACap = Android.Graphics.Paint.Cap;
-using AColor = Android.Graphics.Color;
-using AJoin = Android.Graphics.Paint.Join;
-using AMatrix = Android.Graphics.Matrix;
-using APoint = Android.Graphics.PointF;
-using ARect = Android.Graphics.Rect;
-using ARectF = Android.Graphics.RectF;
-using AStyle = Android.Graphics.Paint.Style;
-using ATextAlign = Android.Graphics.Paint.Align;
-using ATileMode = Android.Graphics.Shader.TileMode;
-using ATypeface = Android.Graphics.Typeface;
-using ATypeStyle = Android.Graphics.TypefaceStyle;
-
-namespace Perspex.Android.CanvasRendering
-{
- public static class AndroidGraphicsExtensions
- {
- public static AColor ToAndroidGraphics(this Color c)
- {
- return new AColor(c.R, c.G, c.B, c.A);
- }
-
- public static AMatrix ToAndroidGraphics(this Matrix matrix)
- {
- var ps = PointUnitService.Instance;
- //TODO: !!!
- var transformValues = new float[9];
- transformValues[0] = (float)matrix.M11;
- transformValues[1] = (float)matrix.M21;
- transformValues[2] = ps.PerspexToNativeXF(matrix.M31);
- //transformValues[2] = (float)matrix.M31;
-
- transformValues[3] = (float)matrix.M12;
- transformValues[4] = (float)matrix.M22;
- transformValues[5] = ps.PerspexToNativeYF(matrix.M32);
- //transformValues[5] = (float)matrix.M32;
-
- transformValues[6] = 0;
- transformValues[7] = 0;
- transformValues[8] = 1;
- var am = new AMatrix();
- am.SetValues(transformValues);
- return am;
- }
-
- //public static Matrix ToPerspex(this AMatrix m)
- //{
- // var v = new float[9];
- // m.GetValues(v);
- // return new Matrix(v[0], v[3], v[1], v[4], v[2], v[5]);
- //}
-
- public static APoint ToAndroidGraphics(this Point p, bool notransform = false)
- {
- if (notransform) new APoint((float)p.X, (float)p.Y);
- var ps = PointUnitService.Instance;
- return new APoint(ps.PerspexToNativeXF(p.X), ps.PerspexToNativeYF(p.Y));
- }
-
- //public static Point ToPerspex(this APoint p)
- //{
- // return new Point(p.X, p.Y);
- //}
-
- public static ARect ToAndroidGraphics(this Rect r, bool notransform = false)
- {
- if (notransform) return new ARect((int)r.X, (int)r.Y, (int)r.Right, (int)r.Bottom);
- var ps = PointUnitService.Instance;
- return new ARect(ps.PerspexToNativeXInt(r.X), ps.PerspexToNativeYInt(r.Y), ps.PerspexToNativeXInt(r.Right), ps.PerspexToNativeYInt(r.Bottom));
- }
-
- public static ARectF ToAndroidGraphicsF(this Rect r)
- {
- var ps = PointUnitService.Instance;
- return new ARectF(ps.PerspexToNativeXF(r.X), ps.PerspexToNativeYF(r.Y), ps.PerspexToNativeXF(r.Right), ps.PerspexToNativeYF(r.Bottom));
- }
-
- //public static Rect ToPerspex(this ARect r)
- //{
- // return new Rect(r.Left, r.Top, r.Width(), r.Height());
- //}
-
- public static Rect ToPerspex(this ARectF r)
- {
- var ps = PointUnitService.Instance;
- return new Rect(ps.NativeToPerspexX(r.Left), ps.NativeToPerspexY(r.Top), ps.NativeToPerspexX(r.Width()), ps.NativeToPerspexY(r.Height()));
- }
-
- public static AJoin ToAndroidGraphics(this PenLineJoin plj)
- {
- switch (plj)
- {
- default:
- case PenLineJoin.Bevel:
- return AJoin.Bevel;
-
- case PenLineJoin.Miter:
- return AJoin.Miter;
-
- case PenLineJoin.Round:
- return AJoin.Round;
- }
- }
-
- public static ACap ToAndroidGraphics(this PenLineCap plc)
- {
- switch (plc)
- {
- default:
- case PenLineCap.Flat:
- return ACap.Butt;
-
- case PenLineCap.Round:
- return ACap.Round;
-
- case PenLineCap.Square:
- return ACap.Square;
- //Triangle not supported
- case PenLineCap.Triangle:
- goto default;
- }
- }
-
- public static AStyle ToAndroidGraphics(this BrushUsage bu)
- {
- switch (bu)
- {
- default:
- case BrushUsage.Fill:
- return AStyle.Fill;
-
- case BrushUsage.Stroke:
- return AStyle.Stroke;
-
- case BrushUsage.Both:
- return AStyle.FillAndStroke;
- }
- }
-
- public static ATypeface ToAndroidGraphicsTypeface(this FontStyle s, FontWeight fontWeight)
- {
- ATypeface result = ATypeface.Default;
-
- if (fontWeight >= FontWeight.Bold)
- {
- result = ATypeface.DefaultBold;
- }
-
- return result;
- }
-
- public static ATypeStyle ToAndroidGraphicsTypefaceStyle(this FontStyle s, FontWeight fontWeight)
- {
- ATypeStyle result;
- switch (s)
- {
- default:
- case FontStyle.Normal:
- result = ATypeStyle.Normal;
- break;
-
- case FontStyle.Italic:
- result = ATypeStyle.Italic;
- break;
- //Oblique not supported, return normal
- case FontStyle.Oblique:
- goto default;
- }
-
- if (fontWeight >= FontWeight.Bold)
- {
- result = result == ATypeStyle.Italic ? ATypeStyle.BoldItalic : ATypeStyle.Bold;
- }
-
- return result;
- }
-
- public static ATextAlign ToAndroidGraphics(this TextAlignment ta)
- {
- switch (ta)
- {
- default:
- case TextAlignment.Left:
- return ATextAlign.Left;
-
- case TextAlignment.Center:
- return ATextAlign.Center;
-
- case TextAlignment.Right:
- return ATextAlign.Right;
- }
- }
-
- public static AAllignment ToAndroidGraphicsLayoutAligment(this TextAlignment ta)
- {
- switch (ta)
- {
- default:
- case TextAlignment.Left:
- return AAllignment.AlignNormal;
-
- case TextAlignment.Center:
- return AAllignment.AlignCenter;
-
- case TextAlignment.Right:
- return AAllignment.AlignOpposite;
- }
- }
-
- public static ATileMode ToAndroidGraphicsX(this TileMode tileMode)
- {
- switch (tileMode)
- {
- case TileMode.Tile: return ATileMode.Repeat;
- case TileMode.FlipX: return ATileMode.Mirror;
- case TileMode.FlipXY: return ATileMode.Mirror;
- case TileMode.FlipY: return ATileMode.Clamp;
- case TileMode.None: return ATileMode.Clamp;
- default: return ATileMode.Clamp;
- }
- }
-
- public static ATileMode ToAndroidGraphicsY(this TileMode tileMode)
- {
- switch (tileMode)
- {
- case TileMode.Tile: return ATileMode.Repeat;
- case TileMode.FlipX: return ATileMode.Clamp;
- case TileMode.FlipXY: return ATileMode.Mirror;
- case TileMode.FlipY: return ATileMode.Mirror;
- case TileMode.None: return ATileMode.Clamp;
- default: return ATileMode.Clamp;
- }
- }
-
- public static int OpacityToAndroidAlfa(double opacity)
- {
- return Convert.ToInt32(opacity * 255);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Android/Perspex.Android/CanvasRendering/AndroidPlatformRender.cs b/src/Android/Perspex.Android/CanvasRendering/AndroidPlatformRender.cs
deleted file mode 100644
index bab5a6a287..0000000000
--- a/src/Android/Perspex.Android/CanvasRendering/AndroidPlatformRender.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Perspex.Android.Platform.Specific;
-using Perspex.Media;
-using Perspex.Platform;
-using System.IO;
-using AG = Android.Graphics;
-using System;
-using Perspex.Android.Platform.CanvasPlatform;
-
-namespace Perspex.Android.CanvasRendering
-{
- public class AndroidPlatformRender : IPlatformRenderInterface
- {
- private static readonly AndroidPlatformRender instance = new AndroidPlatformRender();
-
- public IFormattedTextImpl CreateFormattedText(string text, string fontFamilyName, double fontSize,
- FontStyle fontStyle,
- TextAlignment textAlignment, FontWeight fontWeight)
- {
- return new FormattedTextImpl(text, fontFamilyName, fontSize, fontStyle, textAlignment, fontWeight);
- }
-
- public IStreamGeometryImpl CreateStreamGeometry()
- {
- return new StreamGeometryImpl();
- }
-
- public IRenderTarget CreateRenderer(IPlatformHandle handle)
- {
- return new RenderTarget((IAndroidCanvasView)handle);
- }
-
- public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int pwidth, int pheight)
- {
- int width = PointUnitService.Instance.PerspexToNativeXInt(pwidth);
- int height = PointUnitService.Instance.PerspexToNativeYInt(pwidth);
-
- return new RenderTargetBitmapImpl(CreateNativeBitmap(width, height));
- }
-
- public IBitmapImpl CreateBitmap(int pwidth, int pheight)
- {
- int width = PointUnitService.Instance.PerspexToNativeXInt(pwidth);
- int height = PointUnitService.Instance.PerspexToNativeYInt(pwidth);
- return new BitmapImpl(CreateNativeBitmap(width, height));
- }
-
- public IBitmapImpl LoadBitmap(string fileName)
- {
- return new BitmapImpl(AG.BitmapFactory.DecodeFile(fileName));
- }
-
- public IBitmapImpl LoadBitmap(Stream stream)
- {
- return new BitmapImpl(AG.BitmapFactory.DecodeStream(stream));
- }
-
- public static void Initialize()
- => PerspexLocator.CurrentMutable.Bind().ToConstant(instance);
-
- private AG.Bitmap CreateNativeBitmap(int width, int height)
- {
- return AG.Bitmap.CreateBitmap(width, height, AG.Bitmap.Config.Argb8888);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Android/Perspex.Android/CanvasRendering/BitmapImpl.cs b/src/Android/Perspex.Android/CanvasRendering/BitmapImpl.cs
deleted file mode 100644
index d34d5aa59a..0000000000
--- a/src/Android/Perspex.Android/CanvasRendering/BitmapImpl.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using Perspex.Platform;
-using System.IO;
-using AG = Android.Graphics;
-
-namespace Perspex.Android.CanvasRendering
-{
- public class BitmapImpl : IBitmapImpl
- {
- public BitmapImpl(AG.Bitmap bitmap)
- {
- PlatformBitmap = bitmap;
- }
-
- public int PixelWidth => PlatformBitmap.Width;
-
- public int PixelHeight => PlatformBitmap.Height;
-
- public AG.Bitmap PlatformBitmap
- {
- get;
- }
-
- public void Save(string fileName)
- {
- using (var f = File.OpenWrite(fileName))
- {
- PlatformBitmap.Compress(AG.Bitmap.CompressFormat.Png, 100, f);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Android/Perspex.Android/CanvasRendering/BrushImpl.cs b/src/Android/Perspex.Android/CanvasRendering/BrushImpl.cs
deleted file mode 100644
index 7ddc3610d7..0000000000
--- a/src/Android/Perspex.Android/CanvasRendering/BrushImpl.cs
+++ /dev/null
@@ -1,363 +0,0 @@
-using Android.Graphics;
-using Android.Util;
-using Perspex.Android.Platform.CanvasPlatform;
-using Perspex.Android.Platform.Specific;
-using Perspex.Media;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using AColor = Android.Graphics.Color;
-
-namespace Perspex.Android.CanvasRendering
-{
- public enum BrushUsage
- {
- Fill,
- Stroke,
- Both
- };
-
- public interface INativePaintWrapper : IDisposable
- {
- Paint NativePaint { get; }
-
- void PushOpacity(double value);
-
- void PopOpacity();
- }
-
- public abstract class BrushImpl : IDisposable, INativePaintWrapper
- {
- public BrushImpl(Brush brush)
- {
- Brush = brush;
- Brush.PropertyChanged += Brush_PropertyChanged;
- Init();
- }
-
- protected virtual void Init()
- {
- Opacity = Brush.Opacity;
- }
-
- private void Brush_PropertyChanged(object sender, PerspexPropertyChangedEventArgs e)
- {
- _isDirty = true;
- }
-
- protected bool _isDirty = false;
-
- public Brush Brush { get; private set; }
-
- private double _oldOpacity = 1;
-
- public double Opacity { get; private set; }
-
- public virtual void Dispose()
- {
- Brush.PropertyChanged -= Brush_PropertyChanged;
- Brush = null;
- NativePaint?.Dispose();
- }
-
- private Paint _nativeBrush;
-
- public Paint NativePaint
- {
- get
- {
- if (_nativeBrush == null)
- {
- _nativeBrush = new Paint(PaintFlags.AntiAlias);
- _isDirty = true;
- }
-
- if (_isDirty)
- {
- Apply();
- }
-
- return _nativeBrush;
- }
- }
-
- public Paint.Style Style { get; set; }
-
- public virtual void ApplyTo(Paint paint)
- {
- paint.Alpha = AndroidGraphicsExtensions.OpacityToAndroidAlfa(Opacity);
- paint.SetStyle(Style);
- }
-
- private void Apply()
- {
- _isDirty = false;
- ApplyTo(NativePaint);
- }
-
- private static readonly BrushImpl FallbackBrush = new SolidColorBrushImpl(new SolidColorBrush(Colors.Transparent));
-
- public static BrushImpl Create(Brush brush, Size dstRect, Paint.Style style)
- {
- //Size dstRect = PointService.Instance.PerspexToNative(pdstRect);
- BrushImpl impl = null;
-
- if (brush is SolidColorBrush)
- {
- impl = new SolidColorBrushImpl(brush as SolidColorBrush);
- }
- else if (brush is LinearGradientBrush)
- {
- impl = new LinearGradientBrushImpl(brush as LinearGradientBrush, dstRect);
- }
- else if (brush is RadialGradientBrush)
- {
- impl = new RadialGradientBrushImpl(brush as RadialGradientBrush, dstRect);
- }
- else if (brush is ImageBrush)
- {
- impl = new ImageBrushImpl(brush as ImageBrush);
- }
- else if (brush is VisualBrush)
- {
- // TODO: Implement me
- Log.Debug("REND", "VisualBrush not implemented");
- impl = FallbackBrush;
- }
- else
- {
- impl = new SolidColorBrushImpl(null);
- }
-
- impl.Style = style;
-
- return impl;
- }
-
- private Stack _opacityStack = new Stack();
-
- public void PushOpacity(double value)
- {
- _opacityStack.Push(this.Opacity);
- Opacity = Opacity * value;
- _isDirty = value != 1 ? true : false;
- }
-
- public void PopOpacity()
- {
- double newOpacity = _opacityStack.Pop();
- if (newOpacity != Opacity)
- {
- Opacity = newOpacity;
- _isDirty = true;
- }
- }
- }
-
- public class PenImpl : IDisposable, INativePaintWrapper
- {
- public PenImpl(Pen pen, Size dstRect, Paint.Style style)
- {
- this.Pen = pen;
- if (this.Pen.Brush != null)
- {
- this.Brush = BrushImpl.Create(this.Pen.Brush, dstRect, style);
- }
- }
-
- private BrushImpl Brush { get; set; }
-
- public Pen Pen { get; }
-
- private Paint _nativePaint;
-
- public Paint NativePaint
- {
- get
- {
- if (_nativePaint == null)
- {
- _nativePaint = new Paint(PaintFlags.AntiAlias);
- Apply();
- }
-
- return _nativePaint;
- }
- set
- {
- this._nativePaint = value;
- }
- }
-
- public void PushOpacity(double opacity)
- {
- Brush?.PushOpacity(opacity);
- }
-
- public void PopOpacity()
- {
- Brush?.PopOpacity();
- }
-
- private void Apply()
- {
- if (Pen.DashStyle?.Dashes != null && Pen.DashStyle.Dashes.Count > 0)
- {
- var cray = Pen.DashStyle.Dashes.Select(d => (float)d).ToArray();
- NativePaint.SetPathEffect(new DashPathEffect(cray, (float)Pen.DashStyle.Offset));
- }
-
- NativePaint.StrokeWidth = PointUnitService.Instance.PerspexToNativeXF(Pen.Thickness);
- NativePaint.StrokeMiter = (float)Pen.MiterLimit;
-
- NativePaint.StrokeJoin = Pen.LineJoin.ToAndroidGraphics();
- NativePaint.StrokeCap = Pen.StartLineCap.ToAndroidGraphics();
-
- if (Brush != null)
- {
- Brush.ApplyTo(NativePaint);
- }
- }
-
- public void Dispose()
- {
- Brush?.Dispose();
- }
- }
-
- public class SolidColorBrushImpl : BrushImpl
- {
- private AColor _nativeColor;
-
- public SolidColorBrushImpl(SolidColorBrush brush) : base(brush)
- {
- }
-
- public override void ApplyTo(Paint paint)
- {
- paint.Color = _nativeColor;
- base.ApplyTo(paint);
- }
-
- protected override void Init()
- {
- base.Init();
- var brush = Brush as SolidColorBrush;
-
- _nativeColor = brush.Color.ToAndroidGraphics();
- }
- }
-
- public class ShaderBrushImpl : BrushImpl
- {
- protected Shader Shader { get; set; }
-
- protected Size DestinationSize { get; }
-
- public ShaderBrushImpl(Brush brush, Size destinationSize) : base(brush)
- {
- DestinationSize = destinationSize;
- }
-
- public override void ApplyTo(Paint paint)
- {
- base.ApplyTo(paint);
-
- if (Shader != null)
- {
- paint.SetShader(Shader);
- }
- else
- {
- //we had some problem better set something
- paint.Color = new AColor(0, 0, 0, 255);
- }
- }
-
- public override void Dispose()
- {
- Shader?.Dispose();
- base.Dispose();
- }
- }
-
- public class LinearGradientBrushImpl : ShaderBrushImpl
- {
- public LinearGradientBrushImpl(LinearGradientBrush brush, Size destinationSize) : base(brush, destinationSize)
- {
- }
-
- protected override void Init()
- {
- base.Init();
- var brush = Brush as LinearGradientBrush;
- int n = brush.GradientStops.Count;
- var offsets = new float[n];
- var colors = new int[n];
- for (var i = 0; i < n; i++)
- {
- var s = brush.GradientStops[i];
- offsets[i] = (float)s.Offset;
- colors[i] = (int)s.Color.ToUint32();
- }
-
- var startPoint = brush.StartPoint.ToPixels(DestinationSize);
- var endPoint = brush.EndPoint.ToPixels(DestinationSize);
-
- this.Shader = new LinearGradient(
- (float)startPoint.X, (float)startPoint.Y,
- (float)endPoint.X, (float)endPoint.Y,
- colors,
- offsets,
- Shader.TileMode.Clamp);
- }
- }
-
- public class RadialGradientBrushImpl : ShaderBrushImpl
- {
- public RadialGradientBrushImpl(RadialGradientBrush brush, Size destinationSize) : base(brush, destinationSize)
- {
- }
-
- protected override void Init()
- {
- base.Init();
- var brush = Brush as RadialGradientBrush;
- int n = brush.GradientStops.Count;
- var offsets = new float[n];
- var colors = new int[n];
-
- for (var i = 0; i < n; i++)
- {
- var s = brush.GradientStops[i];
- offsets[i] = (float)s.Offset;
- colors[i] = (int)s.Color.ToUint32();
- }
-
- var centerPoint = brush.Center.ToPixels(DestinationSize);
- double radius = brush.Center.Unit == RelativeUnit.Absolute ? brush.Radius : Math.Max(brush.Radius * DestinationSize.Width, brush.Radius * DestinationSize.Height);
-
- Shader = new RadialGradient(
- (float)centerPoint.X, (float)centerPoint.Y,
- (float)brush.Radius,
- colors,
- offsets,
- Shader.TileMode.Clamp);
- }
- }
-
- public class ImageBrushImpl : ShaderBrushImpl
- {
- public ImageBrushImpl(ImageBrush brush) : base(brush, default(Size))
- {
- }
-
- protected override void Init()
- {
- base.Init();
- var brush = Brush as ImageBrush;
- var bmp = brush.Source.PlatformImpl as BitmapImpl;
- Shader = new BitmapShader(bmp.PlatformBitmap, brush.TileMode.ToAndroidGraphicsX(), brush.TileMode.ToAndroidGraphicsY());
- }
- }
-}
\ No newline at end of file
diff --git a/src/Android/Perspex.Android/CanvasRendering/DrawingContext.cs b/src/Android/Perspex.Android/CanvasRendering/DrawingContext.cs
deleted file mode 100644
index 522012d1d2..0000000000
--- a/src/Android/Perspex.Android/CanvasRendering/DrawingContext.cs
+++ /dev/null
@@ -1,248 +0,0 @@
-using Android.Graphics;
-using Perspex.Android.Platform.CanvasPlatform;
-using Perspex.Android.Platform.Specific;
-using Perspex.Media;
-using Perspex.Media.Imaging;
-using System;
-using System.Collections.Generic;
-using System.Reactive.Disposables;
-
-namespace Perspex.Android.CanvasRendering
-{
- public class DrawingContextImpl : IDrawingContextImpl
- {
- private double _currentOpacity = 1.0f;
-
- private double CurrentOpacity
- {
- get { return _currentOpacity; }
- set
- {
- _currentOpacity = value;
- }
- }
-
- public Canvas _canvas;
- private Dictionary