Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9fd4b19
Add AppState class. Add method to launch permissions intent based on …
rocodes Sep 3, 2025
5d7a485
add button strings for continue and exit
rocodes Sep 11, 2025
61f4891
Add single SlideShowPage that shows content based on AppState and use…
rocodes Sep 3, 2025
f951b0e
Delete individual slideshow page files
rocodes Sep 11, 2025
97ac7ae
AppState, ConfigurationManager, and SlideShowManager fixups: add AdbC…
rocodes Sep 11, 2025
46d8902
Add AdbState class. Convert AdbManager to Kotlin.
rocodes Sep 12, 2025
8fd07ca
Handle connected, error, and pairing states in scan screen via adbState.
rocodes Sep 13, 2025
4559329
Check appstate on wifi disconnect or reconnect. Exclude NeedWirelessD…
rocodes Sep 19, 2025
9735264
Add accompanist and experimental permissions API to gradle
rocodes Sep 25, 2025
f4aec42
Use config manager to determine current slide. Use lazy viewmodel in
rocodes Sep 12, 2025
820ddb6
Support autoconnect through slideshow page. Use renamed NeedWirelessD…
rocodes Sep 11, 2025
5f8e200
Support autoconnect from MainActivity and from SlideshowActivity.
rocodes Sep 12, 2025
1cfc369
Adjust checkState logic so that only Android 14+ requires adb if
rocodes Sep 26, 2025
59769fd
Skip toast message when ADB is in AdbState.READY, since it will proce…
rocodes Sep 26, 2025
e63efa7
Stop adb pairing service if the slideshow activity is exiting
rocodes Sep 26, 2025
5d8cfaf
(chore) suppress indicators permissions warning.
rocodes Sep 14, 2025
5f3f356
Add "waiting for adb pairing" button text
rocodes Sep 28, 2025
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
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ dependencies {
implementation(libs.compose.material.icons.extended)
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.snakeyaml)
implementation(libs.google.accompanist.permissions)

// libadb-android and its dependency
implementation(libs.libadb.android)
Expand Down
101 changes: 36 additions & 65 deletions app/src/main/java/org/osservatorionessuno/bugbane/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.pager.HorizontalPager
Expand All @@ -16,11 +15,10 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.launch
import io.github.muntashirakon.adb.PRNGFixes
import androidx.lifecycle.lifecycleScope
import androidx.work.*
import kotlinx.coroutines.Dispatchers
import org.osservatorionessuno.libmvt.common.IndicatorsUpdates
import org.osservatorionessuno.bugbane.workers.IndicatorsUpdateWorker
import java.util.concurrent.TimeUnit
Expand All @@ -30,64 +28,53 @@ import org.osservatorionessuno.bugbane.components.NavigationTabs
import org.osservatorionessuno.bugbane.screens.ScanScreen
import org.osservatorionessuno.bugbane.screens.AcquisitionsScreen
import org.osservatorionessuno.bugbane.ui.theme.Theme
import org.osservatorionessuno.bugbane.utils.AppState
import org.osservatorionessuno.bugbane.utils.ConfigurationViewModel
import org.osservatorionessuno.bugbane.utils.SlideshowManager
import org.osservatorionessuno.bugbane.utils.AdbViewModel
import org.osservatorionessuno.bugbane.utils.AdbPairingService
import org.osservatorionessuno.bugbane.utils.ConfigurationManager
import org.osservatorionessuno.bugbane.utils.ViewModelFactory

private const val TAG = "MainActivity"
class MainActivity : ComponentActivity() {
private val viewModel: AdbViewModel by viewModels()
private var setLacksPermissionsCallback: ((Boolean) -> Unit)? = null

private val configViewModel : ConfigurationViewModel by lazy {
ViewModelFactory.get(application)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
PRNGFixes.apply()

// Observers
viewModel.watchConnectAdb().observe(this) { isConnected ->
if (!isConnected) {
setLacksPermissionsCallback?.invoke(true)
}
}

viewModel.watchAskPairAdb().observe(this) { resetPairing ->
if (resetPairing) {
setLacksPermissionsCallback?.invoke(true)
}
}

viewModel.watchCommandOutput().observe(this) { output ->
// TODO: blibla
Toast.makeText(this@MainActivity, output.toString(), Toast.LENGTH_SHORT).show()
Log.d("COMMAND OUTPUT", output.toString())
}

// Try auto-connecting
viewModel.autoConnect()

// Fetch indicators on first launch and schedule daily background updates
setupIndicatorsUpdates()

if (!ConfigurationManager.isNotificationPermissionGranted(this) || !ConfigurationManager.isWirelessDebuggingEnabled(
this
)
) {
setLacksPermissionsCallback?.invoke(true)
}

if (!SlideshowManager.hasSeenHomepage(this)) {
// On first start, run the SlideshowActivity manually
SlideshowActivity.start(this)
}

enableEdgeToEdge()
setContent {
Theme {
MainContent { callback ->
setLacksPermissionsCallback = callback
val appState = configViewModel.configurationState.collectAsStateWithLifecycle()
val appProgress: State<SlideshowManager.AppProgress> = configViewModel.appManager.appProgress.collectAsStateWithLifecycle()

if (appProgress.value.hasCompletedOnboarding) {
MainContent()
} else {
// Avoid flicker before the slideshow while compose is calculating the appstate
Box(modifier = Modifier.fillMaxSize())

LaunchedEffect(appState.value) {
// Permissions slideshow
val startPage = (appState.value.step)
val intent = Intent(this@MainActivity, SlideshowActivity::class.java)
.putExtra("startPage", startPage)
startActivity(intent)
}
}
}
}

configViewModel.adbManager.watchCommandOutput().observe(this) { output ->
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be removed since it's useless, wdyt?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I think it is a leftover from the libadb demo app we started with!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry I was wrong, that is the listener on the output of the QuickForensics result.
maybe we can just clean that out

// TODO
Toast.makeText(applicationContext, output, Toast.LENGTH_SHORT).show()
Log.d(TAG, "Command output: $output")
}
}

private fun setupIndicatorsUpdates() {
Expand Down Expand Up @@ -124,34 +111,20 @@ class MainActivity : ComponentActivity() {
Log.i("MainActivity", "Scheduled daily indicator update worker")
}
}

@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun MainContent(onSetLacksPermissionsCallback: ((Boolean) -> Unit) -> Unit) {
fun MainContent() {
val context = androidx.compose.ui.platform.LocalContext.current
val configuration = LocalConfiguration.current
val pagerState = rememberPagerState(pageCount = { 2 })
val coroutineScope = rememberCoroutineScope()
var lacksPermissions by remember { mutableStateOf(false) }


// Detect if we're in landscape mode
val isLandscape = configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE

// Sync tab selection with pager
val selectedTabIndex by remember { derivedStateOf { pagerState.currentPage } }

// Function to set lacks permissions state
fun setLacksPermissions(lacks: Boolean) {
lacksPermissions = lacks
}

// Provide the callback to the parent
LaunchedEffect(Unit) {
onSetLacksPermissionsCallback { lacks ->
setLacksPermissions(lacks)
}
}

Scaffold(
topBar = {
if (isLandscape) {
Expand Down Expand Up @@ -200,13 +173,11 @@ fun MainContent(onSetLacksPermissionsCallback: ((Boolean) -> Unit) -> Unit) {
modifier = Modifier.fillMaxSize()
) { pageIndex ->
when (pageIndex) {
0 -> ScanScreen(
lacksPermissions = lacksPermissions,
onLacksPermissionsChange = { setLacksPermissions(it) }
)
0 -> ScanScreen()
1 -> AcquisitionsScreen()
}
}
}
}
}
}

Loading