feat(android): reliable boot-launch incl. Android TV (1.7.11)

The player has a launcher (category.HOME) + a boot receiver, but auto-start was
unreliable where you can't set a home launcher (Android TV) and on Android 14+,
where USE_FULL_SCREEN_INTENT is auto-revoked for non-calling apps so the boot
full-screen launcher silently no-ops.

Boot launch:
- BootReceiver now does a direct background startActivity when 'display over other
  apps' (SYSTEM_ALERT_WINDOW) is granted — a real exception to the bg-activity-launch
  restriction, and the one path that works on Android TV. Full-screen-intent
  notification kept as a fallback (locked screen / no overlay).
- Boot notification moved to a dedicated HIGH-importance channel (full-screen
  intents are only honored from one), and it auto-dismisses once the UI is up.

Setup screen — new permission rows so operators can grant what boot-launch needs:
- Launch on Boot (USE_FULL_SCREEN_INTENT, shown on Android 14+)
- Background Activity (battery-optimization exemption)
- Display Over Apps (SYSTEM_ALERT_WINDOW)
Made the screen scrollable and ~50% smaller text/buttons so all rows + Continue
fit on one screen (incl. landscape signage). Install-Unknown-Apps subtitle now
states updates are signature-verified, so it doesn't read as 'install anything'.

Verified end-to-end on an Android 16 emulator: after reboot the app auto-launched
(Direct launch via overlay) and the boot notice cleared itself; all rows toggle.
This commit is contained in:
ScreenTinker 2026-06-09 17:44:49 -05:00
parent acd93377e7
commit d9d7a8ae0f
8 changed files with 370 additions and 57 deletions

View file

@ -1 +1 @@
1.7.10 1.7.11

View file

@ -11,8 +11,8 @@ android {
applicationId = "com.remotedisplay.player" applicationId = "com.remotedisplay.player"
minSdk = 26 minSdk = 26
targetSdk = 34 targetSdk = 34
versionCode = 13 versionCode = 14
versionName = "1.7.10" versionName = "1.7.11"
} }
signingConfigs { signingConfigs {

View file

@ -14,6 +14,8 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application <application
android:name=".RemoteDisplayApp" android:name=".RemoteDisplayApp"

View file

@ -100,6 +100,9 @@ class MainActivity : AppCompatActivity() {
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
// The display is up now — clear the boot "Starting display…" notification.
(getSystemService(Context.NOTIFICATION_SERVICE) as? android.app.NotificationManager)?.cancel(999)
// Fullscreen immersive // Fullscreen immersive
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
window.decorView.systemUiVisibility = ( window.decorView.systemUiVisibility = (

View file

@ -10,6 +10,9 @@ class RemoteDisplayApp : Application() {
companion object { companion object {
const val CHANNEL_ID = "remote_display_service" const val CHANNEL_ID = "remote_display_service"
const val CHANNEL_NAME = "ScreenTinker Service" const val CHANNEL_NAME = "ScreenTinker Service"
// Separate HIGH-importance channel for the boot full-screen-intent launch.
// A full-screen intent is only honored from a high-importance channel.
const val BOOT_CHANNEL_ID = "remote_display_boot"
} }
override fun onCreate() { override fun onCreate() {
@ -19,16 +22,19 @@ class RemoteDisplayApp : Application() {
private fun createNotificationChannel() { private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel( val manager = getSystemService(NotificationManager::class.java)
CHANNEL_ID, manager.createNotificationChannel(
CHANNEL_NAME, NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW).apply {
NotificationManager.IMPORTANCE_LOW
).apply {
description = "ScreenTinker background service" description = "ScreenTinker background service"
setShowBadge(false) setShowBadge(false)
} }
val manager = getSystemService(NotificationManager::class.java) )
manager.createNotificationChannel(channel) manager.createNotificationChannel(
NotificationChannel(BOOT_CHANNEL_ID, "ScreenTinker Startup", NotificationManager.IMPORTANCE_HIGH).apply {
description = "Launches the display on boot"
setShowBadge(false)
}
)
} }
} }
} }

View file

@ -2,11 +2,15 @@ package com.remotedisplay.player
import android.Manifest import android.Manifest
import android.accessibilityservice.AccessibilityServiceInfo import android.accessibilityservice.AccessibilityServiceInfo
import android.annotation.SuppressLint
import android.app.NotificationManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.PowerManager
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.View import android.view.View
@ -26,8 +30,15 @@ class SetupActivity : AppCompatActivity() {
private lateinit var notificationStatus: TextView private lateinit var notificationStatus: TextView
private lateinit var enableAccessibilityBtn: Button private lateinit var enableAccessibilityBtn: Button
private lateinit var enableInstallBtn: Button private lateinit var enableInstallBtn: Button
private lateinit var fullscreenStatus: TextView
private lateinit var enableFullscreenBtn: Button
private lateinit var batteryStatus: TextView
private lateinit var enableBatteryBtn: Button
private lateinit var overlayStatus: TextView
private lateinit var enableOverlayBtn: Button
private lateinit var continueBtn: Button private lateinit var continueBtn: Button
@SuppressLint("BatteryLife")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -40,6 +51,9 @@ class SetupActivity : AppCompatActivity() {
setContentView(R.layout.activity_setup) setContentView(R.layout.activity_setup)
// App's UI is up — clear the boot "Starting display…" notification.
getSystemService(NotificationManager::class.java)?.cancel(999)
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
window.decorView.systemUiVisibility = ( window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
@ -75,11 +89,60 @@ class SetupActivity : AppCompatActivity() {
enableInstallBtn.setOnClickListener { enableInstallBtn.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startActivity(Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply { startActivity(Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
data = android.net.Uri.parse("package:$packageName") data = Uri.parse("package:$packageName")
}) })
} }
} }
fullscreenStatus = findViewById(R.id.fullscreenStatus)
enableFullscreenBtn = findViewById(R.id.enableFullscreenBtn)
batteryStatus = findViewById(R.id.batteryStatus)
enableBatteryBtn = findViewById(R.id.enableBatteryBtn)
overlayStatus = findViewById(R.id.overlayStatus)
enableOverlayBtn = findViewById(R.id.enableOverlayBtn)
// Display-over-other-apps: alternate boot-launch path. With this granted the
// boot receiver can directly start the activity from the background, which
// works where you can't set a launcher (e.g. Android TV).
enableOverlayBtn.setOnClickListener {
startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
data = Uri.parse("package:$packageName")
})
}
// Launch-on-boot needs USE_FULL_SCREEN_INTENT, which Android 14+ auto-revokes
// for non-calling apps — so the boot full-screen launcher silently fails until
// the user grants it. Older versions auto-grant it, so only show the row where
// it can actually be off.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// USE_FULL_SCREEN_INTENT is auto-granted before Android 14 — hide the row.
findViewById<View>(R.id.fullscreenRow).visibility = View.GONE
} else {
enableFullscreenBtn.setOnClickListener {
try {
startActivity(Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT).apply {
data = Uri.parse("package:$packageName")
})
} catch (e: Exception) {
startActivity(Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
})
}
}
}
// Battery-optimization exemption keeps the boot receiver from being deferred
// and the app from being killed in standby (esp. on OEM / TV boxes).
enableBatteryBtn.setOnClickListener {
try {
startActivity(Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:$packageName")
})
} catch (e: Exception) {
startActivity(Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS))
}
}
continueBtn.setOnClickListener { continueBtn.setOnClickListener {
prefs.edit().putBoolean("setup_complete", true).apply() prefs.edit().putBoolean("setup_complete", true).apply()
proceedToNext() proceedToNext()
@ -130,6 +193,27 @@ class SetupActivity : AppCompatActivity() {
if (hasNotif) View.GONE else View.VISIBLE if (hasNotif) View.GONE else View.VISIBLE
} }
// Launch on boot (full-screen intent — only restrictable on Android 14+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val canFsi = getSystemService(NotificationManager::class.java).canUseFullScreenIntent()
fullscreenStatus.text = if (canFsi) "ON" else "OFF"
fullscreenStatus.setTextColor(if (canFsi) 0xFF22C55E.toInt() else 0xFFEF4444.toInt())
enableFullscreenBtn.visibility = if (canFsi) View.GONE else View.VISIBLE
}
// Battery optimization exemption
val ignoringBattery = (getSystemService(Context.POWER_SERVICE) as PowerManager)
.isIgnoringBatteryOptimizations(packageName)
batteryStatus.text = if (ignoringBattery) "ON" else "OFF"
batteryStatus.setTextColor(if (ignoringBattery) 0xFF22C55E.toInt() else 0xFFEF4444.toInt())
enableBatteryBtn.visibility = if (ignoringBattery) View.GONE else View.VISIBLE
// Display over other apps
val canOverlay = Settings.canDrawOverlays(this)
overlayStatus.text = if (canOverlay) "ON" else "OFF"
overlayStatus.setTextColor(if (canOverlay) 0xFF22C55E.toInt() else 0xFFEF4444.toInt())
enableOverlayBtn.visibility = if (canOverlay) View.GONE else View.VISIBLE
// Update continue button text // Update continue button text
val allGood = accessibilityEnabled && canInstall val allGood = accessibilityEnabled && canInstall
continueBtn.text = if (allGood) "Continue to Setup" else "Continue Anyway" continueBtn.text = if (allGood) "Continue to Setup" else "Continue Anyway"

View file

@ -5,6 +5,7 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.provider.Settings
import android.util.Log import android.util.Log
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import android.app.NotificationManager import android.app.NotificationManager
@ -33,18 +34,31 @@ class BootReceiver : BroadcastReceiver() {
Log.e("BootReceiver", "Failed to start service: ${e.message}") Log.e("BootReceiver", "Failed to start service: ${e.message}")
} }
// Use a full-screen intent to launch the activity (bypasses Android 12+ restrictions)
try {
val launchIntent = Intent(context, MainActivity::class.java).apply { val launchIntent = Intent(context, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
} }
// Primary: with "display over other apps" granted, a direct background
// startActivity is permitted — the most reliable launch, and the one that
// works on Android TV where you can't set a home launcher.
if (Settings.canDrawOverlays(context)) {
try {
context.startActivity(launchIntent)
Log.i("BootReceiver", "Direct launch (overlay permission)")
} catch (e: Exception) {
Log.e("BootReceiver", "Direct launch failed: ${e.message}")
}
}
// Fallback: full-screen-intent notification (covers a locked screen / when
// the overlay permission isn't granted).
try {
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
context, 0, launchIntent, context, 0, launchIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
) )
val notification = NotificationCompat.Builder(context, RemoteDisplayApp.CHANNEL_ID) val notification = NotificationCompat.Builder(context, RemoteDisplayApp.BOOT_CHANNEL_ID)
.setContentTitle("ScreenTinker") .setContentTitle("ScreenTinker")
.setContentText("Starting display...") .setContentText("Starting display...")
.setSmallIcon(android.R.drawable.ic_media_play) .setSmallIcon(android.R.drawable.ic_media_play)

View file

@ -6,31 +6,42 @@
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:background="#111827" android:background="#111827"
android:padding="48dp" android:padding="12dp"
android:keepScreenOn="true"> android:keepScreenOn="true">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="RemoteDisplay Setup" android:text="RemoteDisplay Setup"
android:textColor="#3B82F6" android:textColor="#3B82F6"
android:textSize="32sp" android:textSize="12sp"
android:textStyle="bold" android:textStyle="bold"
android:layout_marginBottom="8dp" /> android:layout_marginBottom="3dp" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Enable these permissions for full remote control" android:text="Enable these permissions for full remote control"
android:textColor="#94A3B8" android:textColor="#94A3B8"
android:textSize="16sp" android:textSize="9sp"
android:layout_marginBottom="40dp" /> android:layout_marginBottom="8dp" />
<LinearLayout <LinearLayout
android:layout_width="500dp" android:layout_width="420dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:layout_marginBottom="32dp"> android:layout_marginBottom="8dp">
<!-- Accessibility Service --> <!-- Accessibility Service -->
<LinearLayout <LinearLayout
@ -38,7 +49,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_marginBottom="16dp"> android:layout_marginBottom="5dp">
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
@ -51,7 +62,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Accessibility Service" android:text="Accessibility Service"
android:textColor="#F1F5F9" android:textColor="#F1F5F9"
android:textSize="18sp" android:textSize="11sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
@ -59,7 +70,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Required for remote control (Home, Back, touch, gestures)" android:text="Required for remote control (Home, Back, touch, gestures)"
android:textColor="#64748B" android:textColor="#64748B"
android:textSize="13sp" /> android:textSize="8sp" />
</LinearLayout> </LinearLayout>
<TextView <TextView
@ -68,20 +79,24 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="OFF" android:text="OFF"
android:textColor="#EF4444" android:textColor="#EF4444"
android:textSize="14sp" android:textSize="9sp"
android:textStyle="bold" android:textStyle="bold"
android:layout_marginEnd="12dp" /> android:layout_marginEnd="12dp" />
<Button <Button
android:id="@+id/enableAccessibilityBtn" android:id="@+id/enableAccessibilityBtn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="Enable" android:text="Enable"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="14sp" android:textSize="9sp"
android:background="@drawable/button_primary" android:background="@drawable/button_primary"
android:paddingStart="20dp" android:paddingStart="12dp"
android:paddingEnd="20dp" /> android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</LinearLayout> </LinearLayout>
<!-- Display Over Other Apps --> <!-- Display Over Other Apps -->
@ -90,7 +105,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_marginBottom="16dp"> android:layout_marginBottom="5dp">
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
@ -103,15 +118,15 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Install Unknown Apps" android:text="Install Unknown Apps"
android:textColor="#F1F5F9" android:textColor="#F1F5F9"
android:textSize="18sp" android:textSize="11sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Required for automatic OTA updates" android:text="OTA updates — only our signature-verified builds install"
android:textColor="#64748B" android:textColor="#64748B"
android:textSize="13sp" /> android:textSize="8sp" />
</LinearLayout> </LinearLayout>
<TextView <TextView
@ -120,20 +135,24 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="OFF" android:text="OFF"
android:textColor="#EF4444" android:textColor="#EF4444"
android:textSize="14sp" android:textSize="9sp"
android:textStyle="bold" android:textStyle="bold"
android:layout_marginEnd="12dp" /> android:layout_marginEnd="12dp" />
<Button <Button
android:id="@+id/enableInstallBtn" android:id="@+id/enableInstallBtn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="Enable" android:text="Enable"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="14sp" android:textSize="9sp"
android:background="@drawable/button_primary" android:background="@drawable/button_primary"
android:paddingStart="20dp" android:paddingStart="12dp"
android:paddingEnd="20dp" /> android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</LinearLayout> </LinearLayout>
<!-- Notification Permission (Android 13+) --> <!-- Notification Permission (Android 13+) -->
@ -143,7 +162,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_marginBottom="16dp" android:layout_marginBottom="5dp"
android:visibility="gone"> android:visibility="gone">
<LinearLayout <LinearLayout
@ -157,7 +176,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Notifications" android:text="Notifications"
android:textColor="#F1F5F9" android:textColor="#F1F5F9"
android:textSize="18sp" android:textSize="11sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
@ -165,7 +184,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Required for background service" android:text="Required for background service"
android:textColor="#64748B" android:textColor="#64748B"
android:textSize="13sp" /> android:textSize="8sp" />
</LinearLayout> </LinearLayout>
<TextView <TextView
@ -174,33 +193,215 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="OFF" android:text="OFF"
android:textColor="#EF4444" android:textColor="#EF4444"
android:textSize="14sp" android:textSize="9sp"
android:textStyle="bold" android:textStyle="bold"
android:layout_marginEnd="12dp" /> android:layout_marginEnd="12dp" />
<Button <Button
android:id="@+id/enableNotificationBtn" android:id="@+id/enableNotificationBtn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="Enable" android:text="Enable"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="14sp" android:textSize="9sp"
android:background="@drawable/button_primary" android:background="@drawable/button_primary"
android:paddingStart="20dp" android:paddingStart="12dp"
android:paddingEnd="20dp" /> android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</LinearLayout>
<!-- Launch on boot / full-screen (Android 14+ restricts USE_FULL_SCREEN_INTENT) -->
<LinearLayout
android:id="@+id/fullscreenRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="5dp"
android:visibility="visible">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Launch on Boot"
android:textColor="#F1F5F9"
android:textSize="11sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Required to auto-start the display after power-on"
android:textColor="#64748B"
android:textSize="8sp" />
</LinearLayout>
<TextView
android:id="@+id/fullscreenStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OFF"
android:textColor="#EF4444"
android:textSize="9sp"
android:textStyle="bold"
android:layout_marginEnd="12dp" />
<Button
android:id="@+id/enableFullscreenBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="Enable"
android:textColor="#FFFFFF"
android:textSize="9sp"
android:background="@drawable/button_primary"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</LinearLayout>
<!-- Battery optimization exemption (boot + run reliability on OEM/TV boxes) -->
<LinearLayout
android:id="@+id/batteryRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="5dp"
android:visibility="visible">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Background Activity"
android:textColor="#F1F5F9"
android:textSize="11sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Keep running and auto-start reliably"
android:textColor="#64748B"
android:textSize="8sp" />
</LinearLayout>
<TextView
android:id="@+id/batteryStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OFF"
android:textColor="#EF4444"
android:textSize="9sp"
android:textStyle="bold"
android:layout_marginEnd="12dp" />
<Button
android:id="@+id/enableBatteryBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="Enable"
android:textColor="#FFFFFF"
android:textSize="9sp"
android:background="@drawable/button_primary"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</LinearLayout>
<!-- Display over other apps: alternate boot-launch path (works where you
can't set a launcher, e.g. Android TV). -->
<LinearLayout
android:id="@+id/overlayRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="5dp"
android:visibility="visible">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Display Over Apps"
android:textColor="#F1F5F9"
android:textSize="11sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Lets the display launch itself on boot (TV boxes)"
android:textColor="#64748B"
android:textSize="8sp" />
</LinearLayout>
<TextView
android:id="@+id/overlayStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OFF"
android:textColor="#EF4444"
android:textSize="9sp"
android:textStyle="bold"
android:layout_marginEnd="12dp" />
<Button
android:id="@+id/enableOverlayBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="Enable"
android:textColor="#FFFFFF"
android:textSize="9sp"
android:background="@drawable/button_primary"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<Button <Button
android:id="@+id/continueBtn" android:id="@+id/continueBtn"
android:layout_width="500dp" android:layout_width="420dp"
android:layout_height="48dp" android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="Continue to Setup" android:text="Continue to Setup"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="16sp" android:textSize="11sp"
android:textStyle="bold" android:textStyle="bold"
android:background="@drawable/button_primary" /> android:background="@drawable/button_primary"
android:paddingTop="10dp"
android:paddingBottom="10dp" />
<TextView <TextView
android:id="@+id/skipText" android:id="@+id/skipText"
@ -208,8 +409,11 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Skip (remote control features will be limited)" android:text="Skip (remote control features will be limited)"
android:textColor="#64748B" android:textColor="#64748B"
android:textSize="13sp" android:textSize="8sp"
android:layout_marginTop="16dp" android:layout_marginTop="6dp"
android:padding="8dp" /> android:padding="8dp" />
</LinearLayout> </LinearLayout>
</ScrollView>
</LinearLayout>