b3775b2f98
Boot-time startup so LedGrab has display capture and control without user interaction on rooted TV boxes. Also folds in a batch of review findings from the Android package audit. Autostart - BootReceiver fires on BOOT_COMPLETED / LOCKED_BOOT_COMPLETED / MY_PACKAGE_REPLACED, gated by AutostartPrefs and Root.looksRooted(). Dispatches CaptureService.createRootIntent via ContextCompat.startForegroundService. Unrooted devices are a no-op because MediaProjection consent cannot be bypassed silently. - AutostartPrefs: thin SharedPreferences wrapper, defaults to enabled. Exposed as a CheckBox on the stopped panel; greyed out when not rooted. - Manifest: RECEIVE_BOOT_COMPLETED, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, WAKE_LOCK permissions + the new BootReceiver. - MainActivity prompts for battery-optimization exemption on first opt-in so Doze/App Standby doesn't kill the FG service on phones. Service stability - onStartCommand now flips isRunning only after startForeground succeeds (was stuck=true forever if the FG transition threw) and resets on exception. Returns START_REDELIVER_INTENT for root mode so the OS can restart the service with the original intent (no consent token to invalidate); MediaProjection mode keeps START_NOT_STICKY. - Watchdog coroutine monitors RootScreenrecord.framesDelivered. Respawns the pipeline on stall (reusing the existing Python bridge — no server restart), caps at 3 consecutive restarts before giving up. - RootScreenrecord.framesDelivered is now an AtomicInteger, exposed as a public property for the watchdog. - ScreenCapture takes an onProjectionStopped lambda; when the user taps the system Cast/Screen-capture stop banner, the whole service is torn down instead of leaving a stale FG notification. - MainActivity's two startForegroundService calls switch to ContextCompat.startForegroundService, clearing pre-existing NewApi lint errors (minSdk=24 < API 26 native method). Build - versionCode derived from git rev-list --count HEAD (or the ANDROID_VERSION_CODE env var for CI). Was pinned to 1 — sideload upgrades were silently refusing to install. - New i18n strings (autostart_label, autostart_unavailable, version_prefix) in en/ru/zh; version_text now uses the resource instead of string concat. TODO.md: new "Android Autostart on Boot" section tracking done/pending items; real-hardware verification on a Magisk'd TV box is the remaining checkbox.
192 lines
7.1 KiB
XML
192 lines
7.1 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<FrameLayout
|
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
xmlns:tools="http://schemas.android.com/tools"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="match_parent"
|
|
android:background="@drawable/bg_main">
|
|
|
|
<!-- STOPPED STATE -->
|
|
<LinearLayout
|
|
android:id="@+id/stopped_panel"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="match_parent"
|
|
android:orientation="vertical"
|
|
android:gravity="center"
|
|
android:paddingStart="160dp"
|
|
android:paddingEnd="160dp">
|
|
|
|
<ImageView
|
|
android:layout_width="72dp"
|
|
android:layout_height="72dp"
|
|
android:src="@drawable/ic_launcher"
|
|
android:contentDescription="@null"
|
|
android:layout_marginBottom="24dp" />
|
|
|
|
<TextView
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:text="@string/app_name"
|
|
android:textColor="@color/teal_accent"
|
|
android:textSize="64sp"
|
|
android:textStyle="bold"
|
|
android:letterSpacing="0.08"
|
|
android:layout_marginBottom="12dp"
|
|
android:fontFamily="sans-serif-light" />
|
|
|
|
<TextView
|
|
android:id="@+id/status_text"
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:text="@string/tagline"
|
|
android:textColor="@color/text_secondary"
|
|
android:textSize="28sp"
|
|
android:layout_marginBottom="64dp" />
|
|
|
|
<Button
|
|
android:id="@+id/toggle_button"
|
|
style="@style/Widget.LedGrab.Button.Primary"
|
|
android:layout_width="320dp"
|
|
android:layout_height="72dp"
|
|
android:text="@string/btn_start"
|
|
android:textSize="22sp"
|
|
android:focusable="true"
|
|
android:focusableInTouchMode="true" />
|
|
|
|
<CheckBox
|
|
android:id="@+id/autostart_check"
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:layout_marginTop="32dp"
|
|
android:text="@string/autostart_label"
|
|
android:textColor="@color/text_secondary"
|
|
android:textSize="20sp"
|
|
android:buttonTint="@color/teal_accent"
|
|
android:focusable="true"
|
|
android:focusableInTouchMode="true" />
|
|
</LinearLayout>
|
|
|
|
<!-- Version at bottom -->
|
|
<TextView
|
|
android:id="@+id/version_text"
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:layout_gravity="bottom|center_horizontal"
|
|
android:layout_marginBottom="32dp"
|
|
android:textColor="@color/text_hint"
|
|
android:textSize="18sp"
|
|
tools:text="v0.1.0" />
|
|
|
|
<!-- RUNNING STATE -->
|
|
<LinearLayout
|
|
android:id="@+id/running_panel"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="match_parent"
|
|
android:orientation="horizontal"
|
|
android:gravity="center_vertical"
|
|
android:paddingStart="120dp"
|
|
android:paddingEnd="120dp"
|
|
android:paddingTop="80dp"
|
|
android:paddingBottom="80dp"
|
|
android:visibility="gone">
|
|
|
|
<!-- Left: status + URL + stop -->
|
|
<LinearLayout
|
|
android:layout_width="0dp"
|
|
android:layout_height="wrap_content"
|
|
android:layout_weight="1"
|
|
android:orientation="vertical"
|
|
android:gravity="start|center_vertical"
|
|
android:paddingEnd="64dp">
|
|
|
|
<LinearLayout
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:orientation="horizontal"
|
|
android:gravity="center_vertical"
|
|
android:layout_marginBottom="32dp">
|
|
|
|
<View
|
|
android:layout_width="18dp"
|
|
android:layout_height="18dp"
|
|
android:background="@drawable/bg_status_dot"
|
|
android:layout_marginEnd="16dp" />
|
|
|
|
<TextView
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:text="@string/status_running"
|
|
android:textColor="@color/green_status"
|
|
android:textSize="28sp"
|
|
android:textStyle="bold"
|
|
android:letterSpacing="0.05" />
|
|
</LinearLayout>
|
|
|
|
<TextView
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:text="@string/label_web_ui"
|
|
android:textColor="@color/text_secondary"
|
|
android:textSize="22sp"
|
|
android:layout_marginBottom="8dp" />
|
|
|
|
<TextView
|
|
android:id="@+id/url_text"
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:textColor="@color/teal_accent"
|
|
android:textSize="30sp"
|
|
android:maxLines="1"
|
|
android:textStyle="bold"
|
|
android:background="@drawable/bg_url_chip"
|
|
android:paddingStart="24dp"
|
|
android:paddingEnd="24dp"
|
|
android:paddingTop="12dp"
|
|
android:paddingBottom="12dp"
|
|
android:layout_marginBottom="56dp"
|
|
tools:text="http://192.168.1.5:8080" />
|
|
|
|
<Button
|
|
android:id="@+id/stop_button_running"
|
|
style="@style/Widget.LedGrab.Button.Secondary"
|
|
android:layout_width="240dp"
|
|
android:layout_height="64dp"
|
|
android:text="@string/btn_stop"
|
|
android:textSize="20sp"
|
|
android:focusable="true"
|
|
android:focusableInTouchMode="true" />
|
|
</LinearLayout>
|
|
|
|
<!-- Right: QR code -->
|
|
<LinearLayout
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:orientation="vertical"
|
|
android:gravity="center">
|
|
|
|
<FrameLayout
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:background="@drawable/bg_qr_container"
|
|
android:padding="20dp"
|
|
android:layout_marginBottom="20dp">
|
|
|
|
<ImageView
|
|
android:id="@+id/qr_image"
|
|
android:layout_width="280dp"
|
|
android:layout_height="280dp"
|
|
android:contentDescription="@string/qr_description"
|
|
android:scaleType="fitXY" />
|
|
</FrameLayout>
|
|
|
|
<TextView
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="wrap_content"
|
|
android:text="@string/scan_to_configure"
|
|
android:textColor="@color/text_secondary"
|
|
android:textSize="22sp"
|
|
android:gravity="center" />
|
|
</LinearLayout>
|
|
</LinearLayout>
|
|
</FrameLayout>
|