Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Android.Content;
using Android.Widget;

namespace FluidSharp.Views.Android.Diagnostics;

public class FrameDiagnosticsView : LinearLayout
{
public EditText FrameDiagnosticsField { get; private set; }
public Button ActionButton { get; private set; }

public FrameDiagnosticsView(Context context) : base(context)
{
Orientation = Orientation.Vertical;
SetPadding(32, 32, 32, 32);

var label = new TextView(context)
{
Text = "Diagnostics:"
};
AddView(label);

FrameDiagnosticsField = new EditText(context)
{
Id = Resources.GetIdentifier("FrameDiagnosticsField", "id", context.PackageName)
};
AddView(FrameDiagnosticsField);
}
}
61 changes: 61 additions & 0 deletions FluidSharp/Configuration/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Diagnostics.Metrics;
using System.Threading.Tasks;
using FluidSharp.Diagnostics;
using FluidSharp.Layouts;

namespace FluidSharp.Configuration;

public sealed class Config
{
private static Config _instance = new Config();

public static Config Instance => _instance;

private Config() { }

private TestSessionsClient _testSessionsClient;
public bool IsDiagnosticsEnabled { get; set; }

public static void EnableDiagnostics(
string snapshotsBackendUri,
string apiKey,
string deviceKey,
float screenScale,
Margins margin,
Action<Exception> onError)
{
var instance = new Config();
instance.IsDiagnosticsEnabled = true;
instance._testSessionsClient = new TestSessionsClient(snapshotsBackendUri, apiKey, margin, screenScale);

_ = Task.Run<Task>(async () =>
{
var lastVisualFrameTimestamp = DateTime.MinValue;
while (instance.IsDiagnosticsEnabled)
{
var visualFrame = VisualFrameProvider.LastVisualFrame;

if (visualFrame is null || visualFrame.Timestamp <= lastVisualFrameTimestamp)
{
await Task.Delay(1000);
continue;
}

try
{
await instance._testSessionsClient.PostSessionTreeAsync(
deviceKey, visualFrame);
}
catch (Exception e)
{
onError(e);
}

await Task.Delay(1000);
}
});

_instance = instance;
}
}
66 changes: 66 additions & 0 deletions FluidSharp/Diagnostics/TestSessionClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using FluidSharp.Layouts;

namespace FluidSharp.Diagnostics;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;

public class TestSessionsClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
private readonly string _apiKey;
private readonly float _screenScale;
private readonly Margins _margins;

public TestSessionsClient(string baseUrl, string apiKey, Margins margins, float screenScale)
{
_baseUrl = baseUrl;
_apiKey = apiKey;
_screenScale = screenScale;
_margins = margins;
_httpClient = new HttpClient();
}

private void AddApiKeyHeader(HttpRequestMessage request)
{
request.Headers.Remove("x-api-key");
request.Headers.Add("x-api-key", _apiKey);
}

public async Task<DeviceSession?> PostSessionTreeAsync(string deviceKey, object tree)
{
var treeJson = JsonContent.Create(
new
{
tree,
ratio = _screenScale,
margin = new
{
near = _margins.Near,
far = _margins.Far,
top = _margins.Top,
bottom = _margins.Bottom,
}
});

var request = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/test-sessions/{deviceKey}")
{
Content = treeJson
};
AddApiKeyHeader(request);
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var session = await response.Content.ReadFromJsonAsync<DeviceSession>();
return session;
}
}

public class DeviceSession
{
public string deviceKey { get; set; } = string.Empty;
public JsonElement tree { get; set; }
}
87 changes: 87 additions & 0 deletions FluidSharp/Diagnostics/VisualFrame.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluidSharp.Widgets;
using SkiaSharp;

namespace FluidSharp.Diagnostics;

public static class VisualFrameProvider
{
public static VisualFrame LastVisualFrame;

public static VisualFrame CreateVisualFrame()
{
LastVisualFrame = new VisualFrame
{
Timestamp = DateTime.UtcNow
};
return LastVisualFrame;
}
}


public class VisualFrameChild
{
public SKRectDto Rect { get; set; }
public VisualFrame.VisualFrameElement Element { get; set; }
}

public class SKRectDto
{
public float Left { get; set; }
public float Top { get; set; }
public float Right { get; set; }
public float Bottom { get; set; }

public static SKRectDto FromSKRect(SKRect rect) => new SKRectDto
{
Left = rect.Left,
Top = rect.Top,
Right = rect.Right,
Bottom = rect.Bottom
};
}

public class VisualFrame
{
public DateTime Timestamp { get; set; }
public List<VisualFrameChild> Children { get; } = new();

public void AddChild(SKRect rect, Widget element)
{
Children.Add(
new VisualFrameChild() {
Rect = SKRectDto.FromSKRect(rect),
Element = new VisualFrameElement(element)
});
}

public override string ToString()
{
return System.Text.Json.JsonSerializer.Serialize(
new
{
Timestamp,
Children =
Children
.Select(x => new
{
Rect = new
{
Left = x.Rect.Left,
Right = x.Rect.Right,
Top = x.Rect.Top,
Bottom = x.Rect.Bottom
},
x.Element.widget.TestId,
x.Element.widget.Tag,
x.Element.widget.TestText,
x.Element.widget.GetType().Name
})
.ToList()
});
}

public record VisualFrameElement(Widget widget);
}
2 changes: 1 addition & 1 deletion FluidSharp/FluidSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\SkiaSharp.TextBlocks\source\SkiaSharp.TextBlocks\SkiaSharp.TextBlocks.csproj" />
<ProjectReference Include="..\..\fluid-sharp-deps\skia-sharp-text-blocks\source\SkiaSharp.TextBlocks\SkiaSharp.TextBlocks.csproj" />
</ItemGroup>

</Project>
19 changes: 17 additions & 2 deletions FluidSharp/Layouts/LayoutSurface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using FluidSharp.Diagnostics;

namespace FluidSharp
{
Expand All @@ -34,12 +35,21 @@ public class LayoutSurface
public virtual void SetCanvas(SKCanvas canvas) { Canvas = canvas; }
protected void SetThisCanvas(SKCanvas canvas) { Canvas = canvas; }

public LayoutSurface(Device device, MeasureCache measureCache, SKCanvas canvas, VisualState visualState)
private readonly VisualFrame _visualFrame;


public LayoutSurface(Device device, MeasureCache measureCache,
SKCanvas canvas, VisualState visualState,
bool shouldCreateVisualFrame = true)
{
Device = device;
MeasureCache = measureCache;
Canvas = canvas;
VisualState = visualState;
if (shouldCreateVisualFrame)
{
_visualFrame = VisualFrameProvider.CreateVisualFrame();
}
}

public virtual SKRect Paint(Widget widget, SKRect rect)
Expand All @@ -58,7 +68,11 @@ public virtual SKRect Paint(Widget widget, SKRect rect)
}
}

var result = widget.PaintInternal(this, rect);
var result = Configuration.Config.Instance.IsDiagnosticsEnabled
? widget.PaintInternalWithDiagnostics(this, rect)
: widget.PaintInternal(this, rect);



if (Canvas != canvas)
{
Expand Down Expand Up @@ -204,5 +218,6 @@ public void DebugSpacing(SKRect dest, Func<string> text, SKColor color)

}

public void TrackVisualFrame(SKRect rect, Widget widget) => _visualFrame?.AddChild(rect, widget);
}
}
34 changes: 26 additions & 8 deletions FluidSharp/Widgets/Widget.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
using FluidSharp.Layouts;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Config = FluidSharp.Configuration.Config;

namespace FluidSharp.Widgets
{
public abstract class Widget
{


public string? TestId { get; set; }
public string? TestText { get; set; }
public virtual string? Tag { get; set; }

public static Action<Widget>? WidgetAllocated;

#if DEBUG
public string? DebugTag;
public bool IsNew = true;
#endif

public abstract SKSize Measure(MeasureCache measureCache, SKSize boundaries);
public bool ShouldBeTrackedForDiagnostics =>
Config.Instance.IsDiagnosticsEnabled &&
(!string.IsNullOrWhiteSpace(TestId)
|| !string.IsNullOrWhiteSpace(TestText)
|| !string.IsNullOrWhiteSpace(Tag));

public abstract SKSize Measure(MeasureCache measureCache,
SKSize boundaries);

public SKRect PaintInternalWithDiagnostics(LayoutSurface layoutsurface,
SKRect rect)
{
if (Config.Instance.IsDiagnosticsEnabled && ShouldBeTrackedForDiagnostics)
{
layoutsurface.TrackVisualFrame(rect, this);
}

public abstract SKRect PaintInternal(LayoutSurface layoutsurface, SKRect rect);
return PaintInternal(layoutsurface, rect);
}

public abstract SKRect PaintInternal(LayoutSurface layoutsurface,
SKRect rect);


public Widget()
Expand Down