feat(app): shell-only debug launch switches for onboarding and the trust dialog (#7375)

This commit is contained in:
James Rich authored and GitHub committed 2026-09-26 17:40:54 +00:00
1 parent acf608f2e1
commit 640c1ab8c1
11 files changed
+293 -45

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2026 Meshtastic LLC
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!--
The automation entry point: MainActivity honours its launch switches only when started through this alias.
DUMP is held by the shell and never by an installed app, so `adb shell am start` and UiAutomation can use it
and nothing else on the device can.
-->
<activity-alias
android:name="org.meshtastic.app.AutomationLauncher"
android:targetActivity="org.meshtastic.app.MainActivity"
android:permission="android.permission.DUMP"
android:exported="true" />
</application>
</manifest>
@@ -60,6 +60,7 @@ import org.meshtastic.app.node.component.InlineMap
import org.meshtastic.app.node.metrics.getTracerouteMapOverlayInsets
import org.meshtastic.app.ui.MainScreen
import org.meshtastic.core.barcode.rememberBarcodeScanner
import org.meshtastic.core.common.state.LaunchOptions
import org.meshtastic.core.model.DeviceAddress
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.network.repository.UsbRepository
@@ -117,15 +118,21 @@ class MainActivity : AppCompatActivity() {
private val usbRepository: UsbRepository by inject()
private val mapLayersManager: MapLayersManager by inject()
private val launchOptions: LaunchOptions by inject()
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
if (BuildConfig.DEBUG && intent.getBooleanExtra(EXTRA_SKIP_ONBOARDING, false)) {
// MainActivity is exported, so any app can send these extras; only the shell can start the debug alias.
val automationLaunch = BuildConfig.DEBUG && intent.component?.className == AUTOMATION_LAUNCHER
if (automationLaunch && intent.getBooleanExtra(EXTRA_SKIP_ONBOARDING, false)) {
launchOptions.skipOnboarding = true
model.onAppIntroCompleted()
}
launchOptions.skipDeepLinkConfirmation =
automationLaunch && intent.getBooleanExtra(EXTRA_SKIP_CONNECT_CONFIRM, false)
enableEdgeToEdge()
@@ -166,7 +173,7 @@ class MainActivity : AppCompatActivity() {
// once we've decided whether to show the intro or the main screen.
ReportDrawnWhen { true }
if (appIntroCompleted) {
if (appIntroCompleted || launchOptions.skipOnboarding) {
MainScreen()
} else {
val introViewModel = koinViewModel<IntroViewModel>()
@@ -177,7 +184,11 @@ class MainActivity : AppCompatActivity() {
}
// Listen for new intents (e.g. deep links, NFC) without overriding onNewIntent
addOnNewIntentListener { intent -> handleIntent(intent) }
addOnNewIntentListener { intent ->
// The switch covers the launch it came with; a link that arrives later still asks.
launchOptions.skipDeepLinkConfirmation = false
handleIntent(intent)
}
handleIntent(intent)
}
@@ -415,6 +426,10 @@ class MainActivity : AppCompatActivity() {
private companion object {
const val EXTRA_SKIP_ONBOARDING = "skip_onboarding"
const val EXTRA_SKIP_CONNECT_CONFIRM = "skip_connect_confirm"
/** The DUMP-guarded alias in the debug manifest. */
const val AUTOMATION_LAUNCHER = "org.meshtastic.app.AutomationLauncher"
}
}