From dda03b9c05f9b5f4fdc780f98bdc6117bd40cd25 Mon Sep 17 00:00:00 2001 From: binhmod <165454425+binhmod@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:06:05 +0700 Subject: [PATCH 1/5] Replace shared isStarting flag with start method enum --- .../sds100/keymapper/base/expertmode/ExpertModeSetupState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt index f920a4aad3..103b476b17 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt @@ -9,5 +9,5 @@ data class ExpertModeSetupState( val stepContent: StepContent, val isSetupAssistantChecked: Boolean, val isSetupAssistantButtonEnabled: Boolean, - val isStarting: Boolean, + val startingMethod: SystemBridgeStartMethod?, ) From 0c5ddbe2a3872330f8a5575b1b1b6df8ba455942 Mon Sep 17 00:00:00 2001 From: binhmod <165454425+binhmod@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:20:29 +0700 Subject: [PATCH 2/5] hmm, moment fix --- .../base/expertmode/ExpertModeViewModel.kt | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt index 87dfc97116..d4d8aa702b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt @@ -42,6 +42,19 @@ class ExpertModeViewModel @Inject constructor( private const val WARNING_COUNT_DOWN_SECONDS = 5 } + private val startingMethod = + mutableStateOf(null) + + init { + viewModelScope.launch { + useCase.isSystemBridgeConnected.collect { connected -> + if (connected) { + startingMethod.value = null + } + } + } +} + @OptIn(ExperimentalCoroutinesApi::class) val warningState: StateFlow = useCase.isWarningUnderstood @@ -55,14 +68,15 @@ class ExpertModeViewModel @Inject constructor( ) val setupState: StateFlow> = - combine( - useCase.isSystemBridgeConnected, - useCase.isRootGranted, - useCase.shizukuSetupState, - useCase.isNotificationPermissionGranted, - useCase.isSystemBridgeStarting, - ::buildSetupState, - ).stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading) + combine( + useCase.isSystemBridgeConnected, + useCase.isRootGranted, + useCase.shizukuSetupState, + useCase.isNotificationPermissionGranted, + flow { emit(startingMethod.value) }, +) { a, b, c, d, e -> + buildSetupState(a, b, c, d, e) +}.stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading) val autoStartBootEnabled: StateFlow = useCase.isAutoStartBootEnabled @@ -105,8 +119,9 @@ class ExpertModeViewModel @Inject constructor( } fun onRootButtonClick() { - useCase.startSystemBridgeWithRoot() - } + startingMethod.value = SystemBridgeStartMethod.ROOT + useCase.startSystemBridgeWithRoot() +} fun onShizukuButtonClick() { viewModelScope.launch { @@ -120,13 +135,14 @@ class ExpertModeViewModel @Inject constructor( useCase.openShizukuApp() } - ShizukuSetupState.STARTED -> { - useCase.requestShizukuPermission() - } + ShizukuSetupState.STARTED -> { + useCase.requestShizukuPermission() + } - ShizukuSetupState.PERMISSION_GRANTED -> { - useCase.startSystemBridgeWithShizuku() - } + ShizukuSetupState.PERMISSION_GRANTED -> { + startingMethod.value = SystemBridgeStartMethod.SHIZUKU + useCase.startSystemBridgeWithShizuku() + } } } } @@ -186,11 +202,11 @@ sealed class ExpertModeWarningState { sealed class ExpertModeState { data class Stopped( - val isRootGranted: Boolean, - val shizukuSetupState: ShizukuSetupState, - val isNotificationPermissionGranted: Boolean, - val isStarting: Boolean, - ) : ExpertModeState() + val isRootGranted: Boolean, + val shizukuSetupState: ShizukuSetupState, + val isNotificationPermissionGranted: Boolean, + val startingMethod: SystemBridgeStartMethod?, +) : ExpertModeState() data class Started(val isDefaultUsbModeCompatible: Boolean) : ExpertModeState() } From a29930e8cdfbe3459d2c695f1f5a72c2e0fe2dcf Mon Sep 17 00:00:00 2001 From: binhmod <165454425+binhmod@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:30:19 +0700 Subject: [PATCH 3/5] Refactor ExpertModeViewModel to use MutableStateFlow --- .../base/expertmode/ExpertModeViewModel.kt | 123 +++++++++--------- 1 file changed, 65 insertions(+), 58 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt index d4d8aa702b..77ba6f1366 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt @@ -1,8 +1,5 @@ package io.github.sds100.keymapper.base.expertmode -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -17,6 +14,7 @@ import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -43,17 +41,17 @@ class ExpertModeViewModel @Inject constructor( } private val startingMethod = - mutableStateOf(null) + MutableStateFlow(null) init { - viewModelScope.launch { - useCase.isSystemBridgeConnected.collect { connected -> - if (connected) { - startingMethod.value = null + viewModelScope.launch { + useCase.isSystemBridgeConnected.collect { connected -> + if (connected) { + startingMethod.value = null + } } } } -} @OptIn(ExperimentalCoroutinesApi::class) val warningState: StateFlow = @@ -62,33 +60,41 @@ class ExpertModeViewModel @Inject constructor( .stateIn( viewModelScope, SharingStarted.Eagerly, - ExpertModeWarningState.CountingDown( - WARNING_COUNT_DOWN_SECONDS, - ), + ExpertModeWarningState.CountingDown(WARNING_COUNT_DOWN_SECONDS), ) val setupState: StateFlow> = - combine( - useCase.isSystemBridgeConnected, - useCase.isRootGranted, - useCase.shizukuSetupState, - useCase.isNotificationPermissionGranted, - flow { emit(startingMethod.value) }, -) { a, b, c, d, e -> - buildSetupState(a, b, c, d, e) -}.stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading) + combine( + useCase.isSystemBridgeConnected, + useCase.isRootGranted, + useCase.shizukuSetupState, + useCase.isNotificationPermissionGranted, + startingMethod, + useCase.isSystemBridgeStarting, + ) { connected, rootGranted, shizukuState, notifGranted, method, isStarting -> + buildSetupState( + connected, + rootGranted, + shizukuState, + notifGranted, + method, + isStarting, + ) + }.stateIn( + viewModelScope, + SharingStarted.Eagerly, + State.Loading, + ) val autoStartBootEnabled: StateFlow = useCase.isAutoStartBootEnabled .stateIn(viewModelScope, SharingStarted.Eagerly, false) - var showInfoCard by mutableStateOf(!useCase.isInfoDismissed()) + var showInfoCard: Boolean = !useCase.isInfoDismissed() private set fun hideInfoCard() { showInfoCard = false - // Save that they've dismissed the card so it is not shown by default the next - // time they visit the PRO mode page. useCase.dismissInfo() } @@ -102,10 +108,13 @@ class ExpertModeViewModel @Inject constructor( } else { flow { repeat(WARNING_COUNT_DOWN_SECONDS) { - emit(ExpertModeWarningState.CountingDown(WARNING_COUNT_DOWN_SECONDS - it)) + emit( + ExpertModeWarningState.CountingDown( + WARNING_COUNT_DOWN_SECONDS - it, + ), + ) delay(1000L) } - emit(ExpertModeWarningState.Idle) } } @@ -119,30 +128,20 @@ class ExpertModeViewModel @Inject constructor( } fun onRootButtonClick() { - startingMethod.value = SystemBridgeStartMethod.ROOT - useCase.startSystemBridgeWithRoot() -} + startingMethod.value = SystemBridgeStartMethod.ROOT + useCase.startSystemBridgeWithRoot() + } fun onShizukuButtonClick() { viewModelScope.launch { - val shizukuState = useCase.shizukuSetupState.first() - when (shizukuState) { - ShizukuSetupState.NOT_FOUND -> { - // Do nothing - } - - ShizukuSetupState.INSTALLED -> { - useCase.openShizukuApp() + when (useCase.shizukuSetupState.first()) { + ShizukuSetupState.NOT_FOUND -> Unit + ShizukuSetupState.INSTALLED -> useCase.openShizukuApp() + ShizukuSetupState.STARTED -> useCase.requestShizukuPermission() + ShizukuSetupState.PERMISSION_GRANTED -> { + startingMethod.value = SystemBridgeStartMethod.SHIZUKU + useCase.startSystemBridgeWithShizuku() } - - ShizukuSetupState.STARTED -> { - useCase.requestShizukuPermission() - } - - ShizukuSetupState.PERMISSION_GRANTED -> { - startingMethod.value = SystemBridgeStartMethod.SHIZUKU - useCase.startSystemBridgeWithShizuku() - } } } } @@ -155,7 +154,10 @@ class ExpertModeViewModel @Inject constructor( fun onSetupWithKeyMapperClick() { viewModelScope.launch { - navigate("setup_expert_mode_with_key_mapper", NavDestination.ExpertModeSetup) + navigate( + "setup_expert_mode_with_key_mapper", + NavDestination.ExpertModeSetup, + ) } } @@ -172,21 +174,23 @@ class ExpertModeViewModel @Inject constructor( isRootGranted: Boolean, shizukuSetupState: ShizukuSetupState, isNotificationPermissionGranted: Boolean, + startingMethod: SystemBridgeStartMethod?, isSystemBridgeStarting: Boolean, ): State { - if (isSystemBridgeConnected) { - return State.Data( + return if (isSystemBridgeConnected) { + State.Data( ExpertModeState.Started( isDefaultUsbModeCompatible = - useCase.isCompatibleUsbModeSelected().valueOrNull() ?: false, + useCase.isCompatibleUsbModeSelected().valueOrNull() ?: false, ), ) } else { - return State.Data( + State.Data( ExpertModeState.Stopped( isRootGranted = isRootGranted, shizukuSetupState = shizukuSetupState, isNotificationPermissionGranted = isNotificationPermissionGranted, + startingMethod = startingMethod, isStarting = isSystemBridgeStarting, ), ) @@ -202,11 +206,14 @@ sealed class ExpertModeWarningState { sealed class ExpertModeState { data class Stopped( - val isRootGranted: Boolean, - val shizukuSetupState: ShizukuSetupState, - val isNotificationPermissionGranted: Boolean, - val startingMethod: SystemBridgeStartMethod?, -) : ExpertModeState() - - data class Started(val isDefaultUsbModeCompatible: Boolean) : ExpertModeState() + val isRootGranted: Boolean, + val shizukuSetupState: ShizukuSetupState, + val isNotificationPermissionGranted: Boolean, + val startingMethod: SystemBridgeStartMethod?, + val isStarting: Boolean, + ) : ExpertModeState() + + data class Started( + val isDefaultUsbModeCompatible: Boolean, + ) : ExpertModeState() } From c993c70d03ab5e5e0349b349b7343a4209b71559 Mon Sep 17 00:00:00 2001 From: binhmod <165454425+binhmod@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:12:46 +0700 Subject: [PATCH 4/5] Refactor showInfoCard to use mutableStateOf --- .../keymapper/base/expertmode/ExpertModeViewModel.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt index 77ba6f1366..8448f9d64b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt @@ -1,5 +1,8 @@ package io.github.sds100.keymapper.base.expertmode +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -90,7 +93,7 @@ class ExpertModeViewModel @Inject constructor( useCase.isAutoStartBootEnabled .stateIn(viewModelScope, SharingStarted.Eagerly, false) - var showInfoCard: Boolean = !useCase.isInfoDismissed() + var showInfoCard by mutableStateOf(!useCase.isInfoDismissed()) private set fun hideInfoCard() { @@ -191,7 +194,7 @@ class ExpertModeViewModel @Inject constructor( shizukuSetupState = shizukuSetupState, isNotificationPermissionGranted = isNotificationPermissionGranted, startingMethod = startingMethod, - isStarting = isSystemBridgeStarting, + isStarting = startingMethod != null || isSystemBridgeStarting, ), ) } From d339a2bcab957a775bec906f5c7a8f40c92d8878 Mon Sep 17 00:00:00 2001 From: binhmod <165454425+binhmod@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:28:18 +0700 Subject: [PATCH 5/5] Update loading state based on starting method --- .../keymapper/base/expertmode/ExpertModeScreen.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt index 496c22c917..44b3bba36f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt @@ -345,7 +345,9 @@ private fun LoadedContent( ), onButtonClick = onRootButtonClick, enabled = state.isNotificationPermissionGranted, - isLoading = state.isStarting, + isLoading = + state.isStarting && + state.startingMethod == SystemBridgeStartMethod.ROOT, ) Spacer(modifier = Modifier.height(8.dp)) @@ -389,7 +391,9 @@ private fun LoadedContent( buttonText = shizukuButtonText, onButtonClick = onShizukuButtonClick, enabled = state.isNotificationPermissionGranted, - isLoading = state.isStarting, + isLoading = + state.isStarting && + state.startingMethod == SystemBridgeStartMethod.SHIZUKU, ) } @@ -422,7 +426,9 @@ private fun LoadedContent( enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && state.isNotificationPermissionGranted, - isLoading = state.isStarting, + isLoading = + state.isStarting && + state.startingMethod == SystemBridgeStartMethod.ADB, ) } } @@ -744,6 +750,7 @@ private fun Preview() { isRootGranted = false, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = true, + startingMethod = null, isStarting = false, ), ), @@ -823,6 +830,7 @@ private fun PreviewNotificationPermissionNotGranted() { isRootGranted = true, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = false, + startingMethod = null, isStarting = false, ), ),