MVVM-ify logic to display app intro (#2748)

This commit is contained in:
Phil Oliver
2025-08-16 14:58:21 -04:00
committed by GitHub
parent acc3e3f636
commit 8429f35c1e
3 changed files with 35 additions and 23 deletions

View File

@@ -20,6 +20,7 @@ package com.geeksville.mesh
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.graphics.Color
@@ -39,12 +40,10 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalView
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.model.BluetoothViewModel
@@ -71,7 +70,7 @@ class MainActivity :
// This is aware of the Activity lifecycle and handles binding to the mesh service.
@Inject internal lateinit var meshServiceClient: MeshServiceClient
private var showAppIntro by mutableStateOf(false)
@Inject internal lateinit var uiPrefs: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
@@ -85,15 +84,13 @@ class MainActivity :
}
super.onCreate(savedInstanceState)
val prefs = UIViewModel.getPreferences(this)
if (savedInstanceState == null) {
val lang = prefs.getString("lang", LanguageUtils.SYSTEM_DEFAULT)
if (lang != LanguageUtils.SYSTEM_MANAGED) LanguageUtils.migrateLanguagePrefs(prefs)
val lang = uiPrefs.getString("lang", LanguageUtils.SYSTEM_DEFAULT)
if (lang != LanguageUtils.SYSTEM_MANAGED) LanguageUtils.migrateLanguagePrefs(uiPrefs)
info("in-app language is ${LanguageUtils.getLocale()}")
if (!prefs.getBoolean("app_intro_completed", false)) {
showAppIntro = true
} else {
if (uiPrefs.getBoolean("app_intro_completed", false)) {
(application as GeeksvilleApplication).askToRate(this)
}
}
@@ -115,11 +112,11 @@ class MainActivity :
SideEffect { AppCompatDelegate.setDefaultNightMode(theme) }
}
val showAppIntro by model.showAppIntro.collectAsStateWithLifecycle()
if (showAppIntro) {
AppIntroductionScreen(
onDone = {
prefs.edit { putBoolean("app_intro_completed", true) }
showAppIntro = false
model.onAppIntroCompleted()
(application as GeeksvilleApplication).askToRate(this@MainActivity)
},
)
@@ -246,11 +243,7 @@ class MainActivity :
chooseLangDialog()
}
MainMenuAction.SHOW_INTRO -> {
showAppIntro = true
}
else -> {}
else -> warn("Unexpected action: $action")
}
}
@@ -273,8 +266,7 @@ class MainActivity :
getString(R.string.theme_system) to AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM,
)
val prefs = UIViewModel.getPreferences(this)
val theme = prefs.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
val theme = uiPrefs.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
debug("Theme from prefs: $theme")
model.showAlert(
title = getString(R.string.choose_theme),

View File

@@ -18,7 +18,6 @@
package com.geeksville.mesh.model
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.RemoteException
@@ -64,6 +63,7 @@ import com.geeksville.mesh.repository.radio.MeshActivity
import com.geeksville.mesh.repository.radio.RadioInterfaceService
import com.geeksville.mesh.service.MeshServiceNotifications
import com.geeksville.mesh.service.ServiceAction
import com.geeksville.mesh.ui.MainMenuAction
import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.util.getShortDate
import com.geeksville.mesh.util.positionToMeter
@@ -86,6 +86,7 @@ import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.BufferedWriter
@@ -679,9 +680,6 @@ constructor(
}
companion object {
fun getPreferences(context: Context): SharedPreferences =
context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
const val HAS_SHOWN_NOT_PAIRED_WARNING_PREF = "has_shown_not_paired_warning"
}
@@ -990,4 +988,25 @@ constructor(
fun setNodeFilterText(text: String) {
nodeFilterText.value = text
}
// region Main menu actions logic
private val _showAppIntro: MutableStateFlow<Boolean> =
MutableStateFlow(preferences.getBoolean("app_intro_completed", false).not())
val showAppIntro: StateFlow<Boolean> = _showAppIntro.asStateFlow()
fun onMainMenuAction(action: MainMenuAction) {
when (action) {
MainMenuAction.SHOW_INTRO -> _showAppIntro.update { true }
else -> Unit
}
}
// endregion
fun onAppIntroCompleted() {
preferences.edit { putBoolean("app_intro_completed", true) }
_showAppIntro.update { false }
}
}

View File

@@ -359,6 +359,7 @@ fun MainScreen(
MainMenuAction.DEBUG -> navController.navigate(Route.DebugPanel)
MainMenuAction.RADIO_CONFIG -> navController.navigate(RadioConfigRoutes.RadioConfig())
MainMenuAction.QUICK_CHAT -> navController.navigate(ContactsRoutes.QuickChat)
MainMenuAction.SHOW_INTRO -> uIViewModel.onMainMenuAction(action)
else -> onAction(action)
}
} else if (action is NodeMenuAction) {