Skip to content
Merged
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
9 changes: 8 additions & 1 deletion src/OpenIPC.Viewer.Android/Properties/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@
android:roundIcon="@mipmap/icon"
android:allowBackup="true"
android:supportsRtl="true"
android:name=".MainApplication">
android:name=".MainApplication"
android:usesCleartextTraffic="true">
<!-- usesCleartextTraffic: OpenIPC cameras speak plain HTTP on the LAN
(Majestic config.json + image.jpg snapshots, no TLS). Android blocks
cleartext HTTP by default for targetSdk>=28, which silently killed every
camera HTTP call on-device — the Majestic panel never loaded and
snapshots failed, while RTSP video (not HTTP) still worked. LAN devices
have no certs, so cleartext is required. -->
<!-- Phase 14.6 — share sheet. FileProvider grants the receiving app a
scoped, temporary read URI to a snapshot instead of a file:// path. -->
<provider android:name="androidx.core.content.FileProvider"
Expand Down
21 changes: 17 additions & 4 deletions src/OpenIPC.Viewer.App/Views/Pages/SingleCameraPage.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,24 @@ public SingleCameraPage()
// panel, and a Grid alone would just run the panel off the bottom. Switch the
// video row to a fixed 16:9 preview and let PageScroll scroll; on wider
// viewports keep the fill layout (star row, scrolling disabled).
private void OnPageSizeChanged(object? sender, SizeChangedEventArgs e)
private void OnPageSizeChanged(object? sender, SizeChangedEventArgs e) =>
ApplyResponsiveLayout(e.NewSize.Width);

private void ApplyResponsiveLayout(double width)
{
var width = e.NewSize.Width;
// Width can be 0 before the first arrange (e.g. when called from Loaded).
// A mobile head is always a phone-width viewport, so fall back to the
// mobile layout there — this guarantees the page scrolls and the Majestic
// panel below the video is reachable even if SizeChanged hasn't fired yet.
if (width <= 0)
return;
width = OpenIPC.Viewer.App.Services.OverlayDialogPresenter.IsMobile ? 400 : 1000;

var videoRow = RootGrid.RowDefinitions[1];
if (width < MobileBreakpoint)
{
videoRow.Height = new GridLength(System.Math.Round(width * 9.0 / 16.0));
var videoHeight = System.Math.Round(width * 9.0 / 16.0);
if (videoHeight < 160) videoHeight = 200; // sane floor before real measure
videoRow.Height = new GridLength(videoHeight);
PageScroll.VerticalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Auto;
}
else
Expand All @@ -89,6 +97,11 @@ private async void OnTalkReleased(object? sender, PointerEventArgs e)

private async void OnLoaded(object? sender, RoutedEventArgs e)
{
// Engage the responsive layout up front — SizeChanged only fires on a
// change, which on some Android layouts may not happen before the page
// is shown, leaving it in the desktop (fill, no-scroll) state.
ApplyResponsiveLayout(Bounds.Width);

if (Vm is { } vm)
await vm.ActivateAsync(CancellationToken.None);
}
Expand Down
Loading