[DROID] Implemented Popups, fixed AppBuilder

This commit is contained in:
Nikita Tsukanov 2017-02-07 21:22:26 +03:00
Родитель 7e517a79c3
Коммит 8c6d8ae832
16 изменённых файлов: 326 добавлений и 102 удалений

Просмотреть файл

@ -29,7 +29,7 @@
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi;armeabi-v7a;x86</AndroidSupportedAbis>
@ -112,10 +112,6 @@
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
<Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
<Name>Avalonia.DotNetFrameworkRuntime</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.HtmlRenderer\Avalonia.HtmlRenderer.csproj">
<Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
<Name>Avalonia.HtmlRenderer</Name>

Просмотреть файл

@ -0,0 +1,47 @@
using Android.App;
using Android.OS;
namespace Avalonia.Android
{
internal class ActivityTracker : Java.Lang.Object, global::Android.App.Application.IActivityLifecycleCallbacks
{
public static Activity Current { get; private set; }
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
Current = activity;
}
public void OnActivityDestroyed(Activity activity)
{
if (Current == activity)
Current = null;
}
public void OnActivityPaused(Activity activity)
{
if (Current == activity)
Current = null;
}
public void OnActivityResumed(Activity activity)
{
Current = activity;
}
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
Current = activity;
}
public void OnActivityStarted(Activity activity)
{
Current = activity;
}
public void OnActivityStopped(Activity activity)
{
if (Current == activity)
Current = null;
}
}
}

Просмотреть файл

@ -62,6 +62,8 @@ namespace Avalonia.Android
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
SkiaPlatform.Initialize();
((global::Android.App.Application) global::Android.App.Application.Context.ApplicationContext)
.RegisterActivityLifecycleCallbacks(new ActivityTracker());
}
public IWindowImpl CreateWindow()
@ -76,7 +78,7 @@ namespace Avalonia.Android
public IPopupImpl CreatePopup()
{
throw new NotImplementedException();
return new PopupImpl();
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
namespace Avalonia
{
public sealed class AppBuilder : AppBuilderBase<AppBuilder>
{
public AppBuilder() : base(new StandardRuntimePlatform(),
builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType()?.Assembly))
{
}
}
}

Просмотреть файл

@ -61,8 +61,10 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ActivityTracker.cs" />
<Compile Include="AndroidPlatform.cs" />
<Compile Include="AndroidThreadingInterface.cs" />
<Compile Include="AppBuilder.cs" />
<Compile Include="AvaloniaActivity.cs" />
<Compile Include="AvaloniaView.cs" />
<Compile Include="PlatformIconLoader.cs" />
@ -72,6 +74,7 @@
<Compile Include="Platform\Input\AndroidMouseDevice.cs" />
<Compile Include="Platform\SkiaPlatform\AndroidFramebuffer.cs" />
<Compile Include="Platform\SkiaPlatform\InvalidationAwareSurfaceView.cs" />
<Compile Include="Platform\SkiaPlatform\PopupImpl.cs" />
<Compile Include="Platform\SkiaPlatform\TopLevelImpl.cs" />
<Compile Include="Platform\Specific\IAndroidView.cs" />
<Compile Include="Platform\Specific\Helpers\AndroidTouchEventsHelper.cs" />

Просмотреть файл

@ -14,16 +14,17 @@ namespace Avalonia.Android
{
public abstract class AvaloniaActivity : Activity
{
AvaloniaView _view;
internal AvaloniaView View;
object _content;
protected override void OnCreate(Bundle savedInstanceState)
{
RequestWindowFeature(WindowFeatures.NoTitle);
_view = new AvaloniaView(this);
View = new AvaloniaView(this);
if(_content != null)
_view.Content = _content;
SetContentView(_view);
View.Content = _content;
SetContentView(View);
TakeKeyEvents(true);
base.OnCreate(savedInstanceState);
}
@ -37,14 +38,14 @@ namespace Avalonia.Android
set
{
_content = value;
if (_view != null)
_view.Content = value;
if (View != null)
View.Content = value;
}
}
public override bool DispatchKeyEvent(KeyEvent e)
{
return _view.DispatchKeyEvent(e);
return View.DispatchKeyEvent(e);
}
}
}

Просмотреть файл

@ -10,6 +10,7 @@ using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Controls;
using Avalonia.Controls.Embedding;
using Avalonia.Platform;
@ -23,7 +24,7 @@ namespace Avalonia.Android
public AvaloniaView(Context context) : base(context)
{
_view = new ViewImpl(context);
AddView(_view);
AddView(_view.View);
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
}
@ -36,7 +37,7 @@ namespace Avalonia.Android
public override bool DispatchKeyEvent(KeyEvent e)
{
return _view.DispatchKeyEvent(e);
return _view.View.DispatchKeyEvent(e);
}
class ViewImpl : TopLevelImpl, IEmbeddableWindowImpl
@ -45,8 +46,8 @@ namespace Avalonia.Android
public ViewImpl(Context context) : base(context)
{
Focusable = true;
FocusChange += ViewImpl_FocusChange;
View.Focusable = true;
View.FocusChange += ViewImpl_FocusChange;
}
private void ViewImpl_FocusChange(object sender, FocusChangeEventArgs e)
@ -54,6 +55,15 @@ namespace Avalonia.Android
if(!e.HasFocus)
LostFocus?.Invoke();
}
protected override void OnResized(Size size)
{
MaxClientSize = size;
base.OnResized(size);
}
public WindowState WindowState { get; set; }
public IDisposable ShowDialog() => null;
}
}
}

Просмотреть файл

@ -12,7 +12,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public AndroidFramebuffer(Surface surface)
{
if(surface == null)
throw new ArgumentNullException(nameof(surface));
_window = ANativeWindow_fromSurface(JNIEnv.Handle, surface.Handle);
if (_window == IntPtr.Zero)
throw new Exception("Unable to obtain ANativeWindow");
ANativeWindow_Buffer buffer;
var rc = new ARect()
{

Просмотреть файл

@ -18,14 +18,13 @@ namespace Avalonia.Android
{
public abstract class InvalidationAwareSurfaceView : SurfaceView, ISurfaceHolderCallback, IPlatformHandle
{
private readonly Context _context;
bool _invalidateQueued;
readonly object _lock = new object();
private readonly Handler _handler;
public InvalidationAwareSurfaceView(Context context) : base(context)
{
_context = context;
Holder.AddCallback(this);
_handler = new Handler(context.MainLooper);
}
@ -38,13 +37,11 @@ namespace Avalonia.Android
return;
_handler.Post(() =>
{
lock (_lock)
{
_invalidateQueued = false;
}
if (Holder.Surface?.IsValid != true)
return;
try
{
Draw();
DoDraw();
}
catch (Exception e)
{
@ -67,20 +64,29 @@ namespace Avalonia.Android
public void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height)
{
Log.Info("AVALONIA", "Surface Changed");
Draw();
DoDraw();
}
public void SurfaceCreated(ISurfaceHolder holder)
{
Log.Info("AVALONIA", "Surface Created");
Draw();
DoDraw();
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
Log.Info("AVALONIA", "Surface Destroyed");
}
protected void DoDraw()
{
lock (_lock)
{
_invalidateQueued = false;
}
Draw();
}
protected abstract void Draw();
public string HandleDescriptor => "SurfaceView";
}

Просмотреть файл

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Platform;
namespace Avalonia.Android.Platform.SkiaPlatform
{
class PopupImpl : TopLevelImpl, IPopupImpl
{
private Point _position;
private bool _isAdded;
public PopupImpl() : base(ActivityTracker.Current, true)
{
}
private Size _clientSize = new Size(1, 1);
public override Size ClientSize
{
get { return base.ClientSize; }
set
{
if(View == null)
return;
_clientSize = value;
UpdateParams();
}
}
public override Point Position
{
get { return _position; }
set
{
_position = value;
PositionChanged?.Invoke(_position);
UpdateParams();
}
}
WindowManagerLayoutParams CreateParams() => new WindowManagerLayoutParams(0,
WindowManagerFlags.NotTouchModal, Format.Translucent)
{
Gravity = GravityFlags.Left | GravityFlags.Top,
WindowAnimations = 0,
X = (int) _position.X,
Y = (int) _position.Y,
Width = Math.Max(1, (int) _clientSize.Width),
Height = Math.Max(1, (int) _clientSize.Height)
};
void UpdateParams()
{
if (_isAdded)
ActivityTracker.Current?.WindowManager?.UpdateViewLayout(View, CreateParams());
}
public override void Show()
{
if (_isAdded)
return;
ActivityTracker.Current.WindowManager.AddView(View, CreateParams());
_isAdded = true;
}
public override void Hide()
{
if (_isAdded)
{
var wm = View.Context.ApplicationContext.GetSystemService(Context.WindowService)
.JavaCast<IWindowManager>();
wm.RemoveView(View);
_isAdded = false;
}
}
public override void Dispose()
{
Hide();
base.Dispose();
}
}
}

Просмотреть файл

@ -9,42 +9,32 @@ using Avalonia.Input.Raw;
using Avalonia.Platform;
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
namespace Avalonia.Android.Platform.SkiaPlatform
{
class TopLevelImpl : InvalidationAwareSurfaceView, IAndroidView, ITopLevelImpl,
ISurfaceHolderCallback, IFramebufferPlatformSurface
class TopLevelImpl : IAndroidView, ITopLevelImpl, IFramebufferPlatformSurface
{
protected AndroidKeyboardEventsHelper<TopLevelImpl> _keyboardHelper;
private readonly AndroidKeyboardEventsHelper<TopLevelImpl> _keyboardHelper;
private readonly AndroidTouchEventsHelper<TopLevelImpl> _touchHelper;
private ViewImpl _view;
private AndroidTouchEventsHelper<TopLevelImpl> _touchHelper;
public TopLevelImpl(Context context) : base(context)
public TopLevelImpl(Context context, bool placeOnTop = false)
{
_view = new ViewImpl(context, this, placeOnTop);
_keyboardHelper = new AndroidKeyboardEventsHelper<TopLevelImpl>(this);
_touchHelper = new AndroidTouchEventsHelper<TopLevelImpl>(this, () => InputRoot, p => GetAvaloniaPointFromEvent(p));
_touchHelper = new AndroidTouchEventsHelper<TopLevelImpl>(this, () => InputRoot,
p => GetAvaloniaPointFromEvent(p));
MaxClientSize = new Size(Resources.DisplayMetrics.WidthPixels, Resources.DisplayMetrics.HeightPixels);
ClientSize = MaxClientSize;
MaxClientSize = new Size(_view.Resources.DisplayMetrics.WidthPixels,
_view.Resources.DisplayMetrics.HeightPixels);
}
void ISurfaceHolderCallback.SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height)
{
var newSize = new Size(width, height);
if (newSize != ClientSize)
{
MaxClientSize = newSize;
ClientSize = newSize;
Resized?.Invoke(ClientSize);
}
base.SurfaceChanged(holder, format, width, height);
}
private bool _handleEvents;
public bool HandleEvents
@ -56,17 +46,24 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_keyboardHelper.HandleEvents = _handleEvents;
}
}
public WindowState WindowState
{
get { return WindowState.Normal; }
set { }
}
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e) => new Point(e.GetX(), e.GetY());
public IInputRoot InputRoot { get; private set; }
public Size ClientSize { get; set; }
public virtual Size ClientSize
{
get
{
if (_view == null)
return new Size(0, 0);
return new Size(_view.Width, _view.Height);
}
set
{
}
}
public Action Closed { get; set; }
@ -74,7 +71,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action<RawInputEventArgs> Input { get; set; }
public Size MaxClientSize { get; private set; }
public Size MaxClientSize { get; protected set; }
public Action<Rect> Paint { get; set; }
@ -84,21 +81,21 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action<Point> PositionChanged { get; set; }
public View View => this;
public View View => _view;
Action ITopLevelImpl.Activated { get; set; }
IPlatformHandle ITopLevelImpl.Handle => this;
IPlatformHandle ITopLevelImpl.Handle => _view;
public IEnumerable<object> Surfaces => new object[] { this };
public IEnumerable<object> Surfaces => new object[] {this};
public void Activate()
{
}
public void Hide()
public virtual void Hide()
{
this.Visibility = ViewStates.Invisible;
_view.Visibility = ViewStates.Invisible;
}
public void SetSystemDecorations(bool enabled)
@ -107,7 +104,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public void Invalidate(Rect rect)
{
if (Holder?.Surface?.IsValid == true) base.Invalidate();
if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate();
}
public Point PointToClient(Point point)
@ -134,9 +131,9 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
}
public void Show()
public virtual void Show()
{
this.Visibility = ViewStates.Visible;
_view.Visibility = ViewStates.Visible;
}
public void BeginMoveDrag()
@ -149,34 +146,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform
//Not supported
}
public Point Position { get; set; }
public virtual Point Position { get; set; }
public double Scaling => 1;
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;
}
protected override void Draw()
void Draw()
{
Paint?.Invoke(new Rect(new Point(0, 0), ClientSize));
}
@ -186,6 +160,66 @@ namespace Avalonia.Android.Platform.SkiaPlatform
// No window icons for mobile platforms
}
ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(Holder.Surface);
public virtual void Dispose()
{
_view.Dispose();
_view = null;
}
protected virtual void OnResized(Size size)
{
Resized?.Invoke(size);
}
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback
{
private readonly TopLevelImpl _tl;
private Size _oldSize;
public ViewImpl(Context context, TopLevelImpl tl, bool placeOnTop) : base(context)
{
_tl = tl;
if (placeOnTop)
SetZOrderOnTop(true);
}
protected override void Draw()
{
_tl.Draw();
}
public override bool DispatchTouchEvent(MotionEvent e)
{
bool callBase;
bool? result = _tl._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 = _tl._keyboardHelper.DispatchKeyEvent(e, out callBase);
bool baseResult = callBase ? base.DispatchKeyEvent(e) : false;
return res != null ? res.Value : baseResult;
}
void ISurfaceHolderCallback.SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height)
{
var newSize = new Size(width, height);
if (newSize != _oldSize)
{
_oldSize = newSize;
_tl.OnResized(newSize);
}
base.SurfaceChanged(holder, format, width, height);
}
}
ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface);
}
}

Просмотреть файл

@ -12,7 +12,7 @@ using System.ComponentModel;
namespace Avalonia.Android.Platform.Specific.Helpers
{
public class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : View, ITopLevelImpl, IAndroidView
public class AndroidKeyboardEventsHelper<TView> : IDisposable where TView :ITopLevelImpl, IAndroidView
{
private TView _view;
private IInputElement _lastFocusedElement;
@ -90,7 +90,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
private void TryShowHideKeyboard(IInputElement element, bool value)
{
var input = _view.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
var input = _view.View.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
if (value)
{
@ -102,7 +102,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
else
{
//hide keyboard
input.HideSoftInputFromWindow(_view.WindowToken, HideSoftInputFlags.None);
input.HideSoftInputFromWindow(_view.View.WindowToken, HideSoftInputFlags.None);
}
}

Просмотреть файл

@ -8,7 +8,7 @@ using System;
namespace Avalonia.Android.Platform.Specific.Helpers
{
public class AndroidTouchEventsHelper<TView> : IDisposable where TView : View, ITopLevelImpl, IAndroidView
public class AndroidTouchEventsHelper<TView> : IDisposable where TView : ITopLevelImpl, IAndroidView
{
private TView _view;
public bool HandleEvents { get; set; }
@ -63,10 +63,10 @@ namespace Avalonia.Android.Platform.Specific.Helpers
//if point is in view otherwise it's possible avalonia not to find the proper window to dispatch the event
_point = _getPointFunc(e);
double x = _view.GetX();
double y = _view.GetY();
double r = x + _view.Width;
double b = y + _view.Height;
double x = _view.View.GetX();
double y = _view.View.GetY();
double r = x + _view.View.Width;
double b = y + _view.View.Height;
if (x <= _point.X && r >= _point.X && y <= _point.Y && b >= _point.Y)
{

Просмотреть файл

@ -60,10 +60,10 @@ namespace Avalonia.Controls
/// </summary>
public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { };
protected AppBuilderBase(IRuntimePlatform platform, Action platformSevices)
protected AppBuilderBase(IRuntimePlatform platform, Action<TAppBuilder> platformSevices)
{
RuntimePlatform = platform;
RuntimePlatformServicesInitializer = platformSevices;
RuntimePlatformServicesInitializer = () => platformSevices((TAppBuilder)this);
}
/// <summary>

Просмотреть файл

@ -17,7 +17,8 @@ namespace Avalonia
/// Initializes a new instance of the <see cref="AppBuilder"/> class.
/// </summary>
public AppBuilder()
: base(new StandardRuntimePlatform(), () => StandardRuntimePlatformServices.Register())
: base(new StandardRuntimePlatform(),
builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType()?.Assembly))
{
}

Просмотреть файл

@ -63,7 +63,10 @@ namespace Avalonia.Skia
private static SKSurface CreateSurface(SKBitmap bitmap)
{
IntPtr length;
return SKSurface.Create(bitmap.Info, bitmap.GetPixels(out length), bitmap.RowBytes);
var rv = SKSurface.Create(bitmap.Info, bitmap.GetPixels(out length), bitmap.RowBytes);
if (rv == null)
throw new Exception("Unable to create Skia surface");
return rv;
}
public BitmapDrawingContext(SKSurface surface) : base(surface.Canvas)