Browse Source

initial support for skia in android

pull/327/head
andrey 11 years ago
parent
commit
3de3dd4244
  1. 54
      src/Android/Perspex.Android/AndroidPlatform.cs
  2. 8
      src/Android/Perspex.Android/Perspex.Android.csproj
  3. 0
      src/Android/Perspex.Android/Platform/AndroidTopLevelRenderer.cs
  4. 37
      src/Android/Perspex.Android/Platform/SkiaPlatform/MainWindowImpl.cs
  5. 171
      src/Android/Perspex.Android/Platform/SkiaPlatform/WindowImpl.cs
  6. 14
      src/Android/Perspex.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
  7. 31
      src/Android/Perspex.AndroidTestApplication/MainActivity.cs
  8. 5
      src/Android/Perspex.AndroidTestApplication/Perspex.AndroidTestApplication.csproj

54
src/Android/Perspex.Android/AndroidPlatform.cs

@ -1,7 +1,6 @@
using Android.OS;
using Perspex.Android.CanvasRendering;
using Perspex.Android.Platform;
using Perspex.Android.Platform.CanvasPlatform;
using Perspex.Android.Platform.Input;
using Perspex.Android.Platform.Specific;
using Perspex.Android.Platform.Specific.Helpers;
@ -12,6 +11,7 @@ using Perspex.Input;
using Perspex.Input.Platform;
using Perspex.Platform;
using Perspex.Shared.PlatformSupport;
using Perspex.Skia;
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
@ -77,15 +77,24 @@ namespace Perspex.Android
////regular timer is working perfect
//return new System.Threading.Timer(_ => tick(), null, ms, ms);
//System.Timers.Timer
//when interval is 0 normal System.Timers.Timer is not working
//so we ween other strategy for that
if (interval.TotalMilliseconds == 0)
{
//android ui thread
PerspexLocator.Current.GetService<IAndroidActivity>().Activity.RunOnUiThread(tick);
return Disposable.Empty;
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<IAndroidActivity>().Activity.RunOnUiThread(tick);
return Disposable.Empty;
}
}
if (OverrideAnimateFramesPerSecond > 0)
else if (OverrideAnimateFramesPerSecond > 0)
{
if (_animationTick >= interval)
{
@ -150,8 +159,8 @@ namespace Perspex.Android
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IAssetLoader>().ToSingleton<AndroidAssetLoader>()
.Bind<ITopLevelRenderer>().ToTransient<AndroidTopLevelRenderer>()
;
AndroidPlatformRender.Initialize();
.Bind<PlatformSettings>().ToConstant(new PlatformSettings());
;
Instance.RegisterViewDrawType();
//set defaults for simple resources
@ -167,21 +176,32 @@ namespace Perspex.Android
public void RegisterViewDrawType()
{
if (Instance.DefaultViewDrawType == ViewDrawType.SurfaceViewCanvasOnDraw)
if (Instance.DefaultViewDrawType == ViewDrawType.Skia)
{
PerspexLocator.CurrentMutable.Bind<IWindowImpl>().ToSingleton<Platform.CanvasPlatform.SurfaceMainWindowImpl>();
//do the default initialization for skia rendering
SkiaPlatform.Initialize();
PerspexLocator.CurrentMutable.Bind<IWindowImpl>().ToSingleton<Platform.SkiaPlatform.MainWindowImpl>();
}
else
{
PerspexLocator.CurrentMutable.Bind<IWindowImpl>().ToSingleton<Platform.CanvasPlatform.MainWindowImpl>();
}
AndroidPlatformRender.Initialize();
PerspexLocator.CurrentMutable.Bind<IPopupImpl>().ToTransient<Platform.CanvasPlatform.PopupImpl>();
if (Instance.DefaultViewDrawType == ViewDrawType.SurfaceViewCanvasOnDraw)
{
PerspexLocator.CurrentMutable.Bind<IWindowImpl>().ToSingleton<Platform.CanvasPlatform.SurfaceMainWindowImpl>();
}
else
{
PerspexLocator.CurrentMutable.Bind<IWindowImpl>().ToSingleton<Platform.CanvasPlatform.MainWindowImpl>();
}
PerspexLocator.CurrentMutable.Bind<IPopupImpl>().ToTransient<Platform.CanvasPlatform.PopupImpl>();
}
}
public void RegisterViewPointUnits()
{
PerspexLocator.CurrentMutable.Bind<IPointUnitService>().ToSingleton<PointUnitService>();
PerspexLocator.CurrentMutable.Bind<Platform.CanvasPlatform.IPointUnitService>().ToSingleton<Platform.CanvasPlatform.PointUnitService>();
}
public void InitializeAssetsLoader(Assembly assembly, string defaultResourcePrefix)
@ -212,9 +232,9 @@ namespace Perspex.Android
/// SurfaceWindowImpl supports:
/// SurfaceViewCanvasOnDraw
/// </summary>
public ViewDrawType DefaultViewDrawType { get; set; } = ViewDrawType.SurfaceViewCanvasOnDraw;
public ViewDrawType DefaultViewDrawType { get; set; } = ViewDrawType.Skia;
public PointUnit DefaultPointUnit { get; set; } = PointUnit.DP;
public Platform.CanvasPlatform.PointUnit DefaultPointUnit { get; set; } = Platform.CanvasPlatform.PointUnit.DP;
public int OverrideAnimateFramesPerSecond { get; set; } = -1;
}

8
src/Android/Perspex.Android/Perspex.Android.csproj

@ -73,6 +73,8 @@
<Compile Include="CursorFactory.cs" />
<Compile Include="Platform\Input\AndroidKeyboardDevice.cs" />
<Compile Include="Platform\Input\AndroidMouseDevice.cs" />
<Compile Include="Platform\SkiaPlatform\MainWindowImpl.cs" />
<Compile Include="Platform\SkiaPlatform\WindowImpl.cs" />
<Compile Include="Platform\Specific\CommonUITimer.cs" />
<Compile Include="Platform\CanvasPlatform\SurfaceMainWindowImpl.cs" />
<Compile Include="Platform\CanvasPlatform\Specific\Helpers\AndroidViewRenderHelpers.cs" />
@ -88,7 +90,7 @@
<Compile Include="CanvasRendering\BrushImpl.cs" />
<Compile Include="CanvasRendering\DrawingContext.cs" />
<Compile Include="CanvasRendering\FormattedTextImpl.cs" />
<Compile Include="CanvasRendering\AndroidTopLevelRenderer.cs" />
<Compile Include="Platform\AndroidTopLevelRenderer.cs" />
<Compile Include="Platform\CanvasPlatform\MainWindowImpl.cs" />
<Compile Include="Platform\CanvasPlatform\SurfaceWindowImpl.cs" />
<Compile Include="Platform\CanvasPlatform\WindowImpl.cs" />
@ -157,6 +159,10 @@
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Perspex.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Perspex.Skia.Android\Perspex.Skia.Android.csproj">
<Project>{bd43f7c0-396b-4aa1-bad9-dfde54d51298}</Project>
<Name>Perspex.Skia.Android</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />

0
src/Android/Perspex.Android/CanvasRendering/AndroidTopLevelRenderer.cs → src/Android/Perspex.Android/Platform/AndroidTopLevelRenderer.cs

37
src/Android/Perspex.Android/Platform/SkiaPlatform/MainWindowImpl.cs

@ -0,0 +1,37 @@
using Android.Views;
using Perspex.Android.Platform.Specific;
using Perspex.Input;
using Perspex.Platform;
namespace Perspex.Android.Platform.SkiaPlatform
{
public class MainWindowImpl :
WindowImpl
, IWindowImpl
{
public MainWindowImpl()
{
}
protected override void Init()
{
base.Init();
HandleEvents = true;
_keyboardHelper.ActivateAutoShowKeybord();
}
void ITopLevelImpl.Show()
{
(Parent as ViewGroup)?.RemoveAllViews();
PerspexLocator.Current.GetService<IAndroidActivity>().ContentView = this;
//this.Visibility = ViewStates.Visible;
}
void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot)
{
base.SetInputRoot(inputRoot);
_keyboardHelper.UpdateKeyboardState(inputRoot);
}
}
}

171
src/Android/Perspex.Android/Platform/SkiaPlatform/WindowImpl.cs

@ -0,0 +1,171 @@
using Android.App;
using Android.Content;
using Android.Util;
using Android.Views;
using Perspex.Android.Platform.Specific;
using Perspex.Android.Platform.Specific.Helpers;
using Perspex.Input;
using Perspex.Input.Raw;
using Perspex.Platform;
using Perspex.Skia.Android;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Perspex.Android.Platform.SkiaPlatform
{
public class WindowImpl : SkiaView, IAndroidView, IWindowImpl
{
protected AndroidKeyboardEventsHelper<WindowImpl> _keyboardHelper;
private AndroidTouchEventsHelper<WindowImpl> _touchHelper;
public WindowImpl(Context context) : base((Activity)context)
{
_keyboardHelper = new AndroidKeyboardEventsHelper<WindowImpl>(this);
_touchHelper = new AndroidTouchEventsHelper<WindowImpl>(this, () => InputRoot, p => GetPerspexPointFromEvent(p));
ClientSize = MaxClientSize;
Resized = size => Invalidate(new Rect(size));
Init();
}
public WindowImpl() : this(PerspexLocator.Current.GetService<IAndroidActivity>().Activity)
{
}
protected virtual void Init()
{
}
private bool _handleEvents;
public bool HandleEvents
{
get { return _handleEvents; }
set
{
_handleEvents = value;
_keyboardHelper.HandleEvents = _handleEvents;
}
}
public virtual Point GetPerspexPointFromEvent(MotionEvent e) => new Point(e.GetX(), e.GetY());
public IInputRoot InputRoot { get; private set; }
public Size ClientSize { get; set; }
public Action Closed { get; set; }
public Action Deactivated { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
public Size MaxClientSize => new Size(Resources.DisplayMetrics.WidthPixels, Resources.DisplayMetrics.HeightPixels);
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public View View => this;
Action ITopLevelImpl.Activated { get; set; }
IPlatformHandle ITopLevelImpl.Handle => this;
public void Activate()
{
}
public void Hide()
{
this.Visibility = ViewStates.Invisible;
}
public void Invalidate(Rect rect)
{
//not working if invalidate is called before Draw app is crashing !!!????
//TODO: investigate and make better workaround also this solution is ok so far
//if (this.Holder?.Surface != null)
if(Holder?.Surface?.IsValid == true) base.Invalidate();
// if (_drawInitialized) base.Invalidate();
}
public Point PointToScreen(Point point)
{
return point;
}
public void SetCursor(IPlatformHandle cursor)
{
//still not implemented
}
public void SetInputRoot(IInputRoot inputRoot)
{
InputRoot = inputRoot;
}
public void SetTitle(string title)
{
}
public void Show()
{
this.Visibility = ViewStates.Visible;
}
public IDisposable ShowDialog()
{
throw new NotImplementedException();
}
public override bool DispatchTouchEvent(MotionEvent e)
{
bool callBase;
bool? result = _touchHelper.DispatchTouchEvent(e, out callBase);
bool baseResult = callBase ? base.DispatchTouchEvent(e) : false;
return result != null ? result.Value : baseResult;
}
public override bool DispatchKeyEvent(KeyEvent e)
{
bool callBase;
bool? res = _keyboardHelper.DispatchKeyEvent(e, out callBase);
bool baseResult = callBase ? base.DispatchKeyEvent(e) : false;
return res != null ? res.Value : baseResult;
}
private Queue<double> _avgDrawQueue = new Queue<double>();
protected override void Draw()
{
_drawInitialized = true;
DateTime begin = DateTime.Now;
Paint?.Invoke(new Rect(new Point(0, 0), ClientSize));
TimeSpan duration = DateTime.Now - begin;
if (AndroidPlatform.Instance.DrawDebugInfo)
{
//draw some basic debug info about rendering
//we can't create drawing context so push some info to std out
//double ms = duration.TotalMilliseconds;
//_avgDrawQueue.Enqueue(ms);
//if (_avgDrawQueue.Count > 50) _avgDrawQueue.Dequeue();
//double msAvg = _avgDrawQueue.Average();
//string text = $"DrawType={AndroidPlatform.Instance.DefaultViewDrawType}, OnDraw={ms.ToString("0.00")}, aOnDraw={msAvg.ToString("0.00")}";
//Log.Debug("render", text);
}
}
private bool _drawInitialized = false;
}
}

14
src/Android/Perspex.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs

@ -3,6 +3,7 @@ using Android.Views;
using Perspex.Android.Platform.CanvasPlatform;
using Perspex.Input;
using Perspex.Input.Raw;
using Perspex.Media;
using Perspex.Platform;
using System;
@ -127,24 +128,31 @@ namespace Perspex.Android.Platform.Specific.Helpers
private Paint _paint;
public void DrawLastMousePoint(DrawingContext context)
{
if (!AndroidPlatform.Instance.DrawDebugInfo) return;
double w = AndroidPlatform.Instance.DoubleClickSize.Width;
context.FillRectangle(Brushes.Red, new Rect(_point.X - w / 2, _point.Y - w / 2, w, w));
}
public void DrawLastMousePoint(Canvas canvas)
{
if (!AndroidPlatform.Instance.DrawDebugInfo) return;
if (_paint == null)
{
_paint = new Paint() { Color = Color.Red, };
_paint = new Paint() { Color = global::Android.Graphics.Color.Red, };
_paint.SetStyle(Paint.Style.Fill);
}
if(AndroidPlatform.Instance.DefaultViewDrawType != ViewDrawType.Skia)
if (AndroidPlatform.Instance.DefaultViewDrawType != ViewDrawType.Skia)
{
canvas.DrawCircle(PointUnitService.Instance.PerspexToNativeXF(_point.X),
PointUnitService.Instance.PerspexToNativeYF(_point.Y),
(float)AndroidPlatform.Instance.DoubleClickSize.Width,
_paint);
}
}
public void Dispose()

31
src/Android/Perspex.AndroidTestApplication/MainActivity.cs

@ -4,17 +4,8 @@ using Android.OS;
using Perspex.Android;
using Perspex.Android.Platform.Specific;
using Perspex.Android.Platform.Specific.Helpers;
using Perspex.Controls;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Markup.Xaml.Data;
using Perspex.Controls.Platform;
using Perspex.Platform;
using Perspex.Styling;
using ReactiveUI;
using System;
using System.Reactive.Linq;
using System.Windows.Input;
namespace Perspex.AndroidTestApplication
{
@ -31,12 +22,16 @@ namespace Perspex.AndroidTestApplication
//set some parameters to android platform
AndroidPlatform.Instance.DrawDebugInfo = true;
//skia is the default rendering method on android so no need to set it
AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.Skia;
//AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.BitmapOnPreDraw;
AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.CanvasOnDraw;
//AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.CanvasOnDraw;
//AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.BitmapBackgroundRender;
//AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.SurfaceViewCanvasOnDraw;
AndroidPlatform.Instance.DefaultPointUnit = Android.Platform.CanvasPlatform.PointUnit.DP;
//AndroidPlatform.Instance.DefaultPointUnit = Android.Platform.CanvasPlatform.PointUnit.DP;
//AndroidPlatform.Instance.DefaultPointUnit = Android.Platform.CanvasPlatform.PointUnit.Pixel;
//60 fps animation are causing user interface in animations to stop responding
AndroidPlatform.Instance.OverrideAnimateFramesPerSecond = 16;
App app;
@ -44,13 +39,23 @@ namespace Perspex.AndroidTestApplication
app = (App)Perspex.Application.Current;
else
app = new App();
if (AndroidPlatform.Instance.DefaultPointUnit == Android.Platform.CanvasPlatform.PointUnit.DP &&
AndroidPlatform.Instance.DefaultViewDrawType == ViewDrawType.Skia)
{
double scale = Resources.DisplayMetrics.ScaledDensity;
//make it DiP in skia
PerspexLocator.Current.GetService<PlatformSettings>().LayoutScalingFactor = scale;
PerspexLocator.Current.GetService<PlatformSettings>().RenderScalingFactor = scale;
}
var window = TestUI.TestUIBuilder.BuildTestUI();
//var window = BuildPlatforSetup();
window.Show();
app.Run(window);
}
public static void StartAppFromSetup()
{
PerspexLocator.Current.GetService<IWindowImpl>().Dispose();

5
src/Android/Perspex.AndroidTestApplication/Perspex.AndroidTestApplication.csproj

@ -79,6 +79,7 @@
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Collections" />
<Reference Include="System.Core" />
<Reference Include="System.IO" />
<Reference Include="System.Linq.Expressions" />
@ -193,6 +194,10 @@
<Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
<Name>Perspex.HtmlRenderer</Name>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Perspex.Skia.Android\Perspex.Skia.Android.csproj">
<Project>{bd43f7c0-396b-4aa1-bad9-dfde54d51298}</Project>
<Name>Perspex.Skia.Android</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="github_icon.png">

Loading…
Cancel
Save