fix: open Settings immediately on cold start when no dictionaries
Seed and synchronously read hadNoDictionaries before setContent so Settings shows from the first frame until the startup scan confirms.
This commit is contained in:
1 parent
e21a134d65
commit
d96fad9cab
4 files changed
+93
-4
No files matched your search
@@ -26,6 +26,7 @@ import com.example.research.common.ui.AppLocaleProvider
|
|||||||
import com.example.research.common.ui.BackgroundTextMeasurementProvider
|
import com.example.research.common.ui.BackgroundTextMeasurementProvider
|
||||||
import com.example.research.common.ui.theme.ReSearchTheme
|
import com.example.research.common.ui.theme.ReSearchTheme
|
||||||
import com.example.research.core.domain.model.AppTheme
|
import com.example.research.core.domain.model.AppTheme
|
||||||
|
import com.example.research.data.local.preferences.SectionExpansionStates
|
||||||
import com.example.research.feature.search.SearchAction
|
import com.example.research.feature.search.SearchAction
|
||||||
import com.example.research.feature.search.SearchViewModel
|
import com.example.research.feature.search.SearchViewModel
|
||||||
import com.example.research.ui.navigation.AppNavigation
|
import com.example.research.ui.navigation.AppNavigation
|
||||||
@@ -51,6 +52,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var initialSectionExpansion: SectionExpansionStates? = null
|
||||||
|
|
||||||
private val settingsViewModel: SettingsViewModel by viewModels {
|
private val settingsViewModel: SettingsViewModel by viewModels {
|
||||||
val app = application as ReSearchApplication
|
val app = application as ReSearchApplication
|
||||||
val versionName = try {
|
val versionName = try {
|
||||||
@@ -69,6 +72,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
app.dictionaryImportManager,
|
app.dictionaryImportManager,
|
||||||
app.dictionaryProgressStateHolder,
|
app.dictionaryProgressStateHolder,
|
||||||
versionName,
|
versionName,
|
||||||
|
initialSectionExpansion ?: SectionExpansionStates(
|
||||||
|
isThemeExpanded = false,
|
||||||
|
isLanguageExpanded = false,
|
||||||
|
isDictionariesExpanded = true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,6 +91,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
val app = application as ReSearchApplication
|
val app = application as ReSearchApplication
|
||||||
val initialTheme = loadInitialTheme(app)
|
val initialTheme = loadInitialTheme(app)
|
||||||
val initialLanguage = loadInitialLanguage(app)
|
val initialLanguage = loadInitialLanguage(app)
|
||||||
|
val initialHadNoDictionaries = loadInitialHadNoDictionaries(app)
|
||||||
|
initialSectionExpansion = loadInitialSectionExpansion(app)
|
||||||
|
|
||||||
handleIntent(intent)
|
handleIntent(intent)
|
||||||
setContent {
|
setContent {
|
||||||
@@ -104,7 +114,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
) {
|
) {
|
||||||
AppNavigation(
|
AppNavigation(
|
||||||
searchViewModel = searchViewModel,
|
searchViewModel = searchViewModel,
|
||||||
settingsViewModel = settingsViewModel
|
settingsViewModel = settingsViewModel,
|
||||||
|
seedNoDictionaries = initialHadNoDictionaries
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,6 +176,31 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadInitialHadNoDictionaries(app: ReSearchApplication): Boolean {
|
||||||
|
return try {
|
||||||
|
runBlocking {
|
||||||
|
app.preferencesManager.hadNoDictionaries.first()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadInitialSectionExpansion(app: ReSearchApplication): SectionExpansionStates {
|
||||||
|
return try {
|
||||||
|
runBlocking {
|
||||||
|
app.preferencesManager.sectionExpansionStates()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.util.Log.e("MainActivity", "Failed to load section expansion states: ${e.message}")
|
||||||
|
SectionExpansionStates(
|
||||||
|
isThemeExpanded = false,
|
||||||
|
isLanguageExpanded = false,
|
||||||
|
isDictionariesExpanded = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
handleIntent(intent)
|
handleIntent(intent)
|
||||||
|
|||||||
@@ -10,12 +10,19 @@ import androidx.datastore.preferences.core.stringSetPreferencesKey
|
|||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import com.example.research.core.domain.model.DictionarySource
|
import com.example.research.core.domain.model.DictionarySource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
|
|
||||||
|
data class SectionExpansionStates(
|
||||||
|
val isThemeExpanded: Boolean,
|
||||||
|
val isLanguageExpanded: Boolean,
|
||||||
|
val isDictionariesExpanded: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
class PreferencesManager(private val context: Context) {
|
class PreferencesManager(private val context: Context) {
|
||||||
private object PreferencesKeys {
|
private object PreferencesKeys {
|
||||||
val THEME_MODE = stringPreferencesKey("theme_mode")
|
val THEME_MODE = stringPreferencesKey("theme_mode")
|
||||||
@@ -25,6 +32,7 @@ class PreferencesManager(private val context: Context) {
|
|||||||
val IS_LANGUAGE_EXPANDED = booleanPreferencesKey("is_language_expanded")
|
val IS_LANGUAGE_EXPANDED = booleanPreferencesKey("is_language_expanded")
|
||||||
val IS_DICTIONARIES_EXPANDED = booleanPreferencesKey("is_dictionaries_expanded")
|
val IS_DICTIONARIES_EXPANDED = booleanPreferencesKey("is_dictionaries_expanded")
|
||||||
val DISABLED_DICTIONARY_PATHS = stringSetPreferencesKey("disabled_dictionary_paths")
|
val DISABLED_DICTIONARY_PATHS = stringSetPreferencesKey("disabled_dictionary_paths")
|
||||||
|
val HAD_NO_DICTIONARIES = booleanPreferencesKey("had_no_dictionaries")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val json = Json { ignoreUnknownKeys = true }
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
@@ -67,6 +75,26 @@ class PreferencesManager(private val context: Context) {
|
|||||||
preferences[PreferencesKeys.DISABLED_DICTIONARY_PATHS] ?: emptySet()
|
preferences[PreferencesKeys.DISABLED_DICTIONARY_PATHS] ?: emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val hadNoDictionaries: Flow<Boolean> = context.dataStore.data
|
||||||
|
.map { preferences ->
|
||||||
|
preferences[PreferencesKeys.HAD_NO_DICTIONARIES] ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setHadNoDictionaries(value: Boolean) {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[PreferencesKeys.HAD_NO_DICTIONARIES] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun sectionExpansionStates(): SectionExpansionStates =
|
||||||
|
context.dataStore.data.first().let { preferences ->
|
||||||
|
SectionExpansionStates(
|
||||||
|
isThemeExpanded = preferences[PreferencesKeys.IS_THEME_EXPANDED] ?: false,
|
||||||
|
isLanguageExpanded = preferences[PreferencesKeys.IS_LANGUAGE_EXPANDED] ?: false,
|
||||||
|
isDictionariesExpanded = preferences[PreferencesKeys.IS_DICTIONARIES_EXPANDED] ?: true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun setDictionaryActive(path: String, isActive: Boolean) {
|
suspend fun setDictionaryActive(path: String, isActive: Boolean) {
|
||||||
context.dataStore.edit { preferences ->
|
context.dataStore.edit { preferences ->
|
||||||
val disabledPaths = preferences[PreferencesKeys.DISABLED_DICTIONARY_PATHS]
|
val disabledPaths = preferences[PreferencesKeys.DISABLED_DICTIONARY_PATHS]
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ enum class Screen {
|
|||||||
@Composable
|
@Composable
|
||||||
fun AppNavigation(
|
fun AppNavigation(
|
||||||
searchViewModel: SearchViewModel,
|
searchViewModel: SearchViewModel,
|
||||||
settingsViewModel: SettingsViewModel
|
settingsViewModel: SettingsViewModel,
|
||||||
|
seedNoDictionaries: Boolean = false
|
||||||
) {
|
) {
|
||||||
val screenStack = rememberSaveable(
|
val screenStack = rememberSaveable(
|
||||||
saver = listSaver(
|
saver = listSaver(
|
||||||
@@ -84,8 +85,9 @@ fun AppNavigation(
|
|||||||
val isOperationActive = stickyPipelineActive
|
val isOperationActive = stickyPipelineActive
|
||||||
val hasConfirmedNoDictionaries =
|
val hasConfirmedNoDictionaries =
|
||||||
settingsState.hasCompletedStartupScan && settingsState.dictionaries.isEmpty()
|
settingsState.hasCompletedStartupScan && settingsState.dictionaries.isEmpty()
|
||||||
|
val hasSeededNoDictionaries = seedNoDictionaries && !settingsState.hasCompletedStartupScan
|
||||||
NavigationState(
|
NavigationState(
|
||||||
shouldShowSettings = hasConfirmedNoDictionaries || isOperationActive,
|
shouldShowSettings = hasConfirmedNoDictionaries || hasSeededNoDictionaries || isOperationActive,
|
||||||
showBackButtonInSettings = settingsState.dictionaries.isNotEmpty() && !isOperationActive,
|
showBackButtonInSettings = settingsState.dictionaries.isNotEmpty() && !isOperationActive,
|
||||||
isOperationActive = isOperationActive,
|
isOperationActive = isOperationActive,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.example.research.core.domain.usecase.DictionarySourceValidator
|
|||||||
import com.example.research.core.domain.usecase.ManageDictionarySourcesUseCase
|
import com.example.research.core.domain.usecase.ManageDictionarySourcesUseCase
|
||||||
import com.example.research.core.util.OperationResult
|
import com.example.research.core.util.OperationResult
|
||||||
import com.example.research.data.local.preferences.PreferencesManager
|
import com.example.research.data.local.preferences.PreferencesManager
|
||||||
|
import com.example.research.data.local.preferences.SectionExpansionStates
|
||||||
import com.example.research.data.repository.LocalDictionaryRepository
|
import com.example.research.data.repository.LocalDictionaryRepository
|
||||||
import com.example.research.feature.download.DownloadManager
|
import com.example.research.feature.download.DownloadManager
|
||||||
import com.example.research.feature.download.model.DownloadState
|
import com.example.research.feature.download.model.DownloadState
|
||||||
@@ -60,6 +61,7 @@ class SettingsViewModel(
|
|||||||
private val dictionaryImportManager: DictionaryImportManager,
|
private val dictionaryImportManager: DictionaryImportManager,
|
||||||
progressStateHolder: DictionaryProgressStateHolder,
|
progressStateHolder: DictionaryProgressStateHolder,
|
||||||
appVersion: String,
|
appVersion: String,
|
||||||
|
initialSectionExpansion: SectionExpansionStates,
|
||||||
) : AndroidViewModel(application) {
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
private val manageDictionarySourcesUseCase = ManageDictionarySourcesUseCase(
|
private val manageDictionarySourcesUseCase = ManageDictionarySourcesUseCase(
|
||||||
@@ -67,7 +69,14 @@ class SettingsViewModel(
|
|||||||
validator = DictionarySourceValidator(),
|
validator = DictionarySourceValidator(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(SettingsUiState(appVersion = appVersion))
|
private val _uiState = MutableStateFlow(
|
||||||
|
SettingsUiState(
|
||||||
|
appVersion = appVersion,
|
||||||
|
isThemeExpanded = initialSectionExpansion.isThemeExpanded,
|
||||||
|
isLanguageExpanded = initialSectionExpansion.isLanguageExpanded,
|
||||||
|
isDictionariesExpanded = initialSectionExpansion.isDictionariesExpanded,
|
||||||
|
)
|
||||||
|
)
|
||||||
val uiState: StateFlow<SettingsUiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<SettingsUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
val progressSnapshot = progressStateHolder.progressSnapshot
|
val progressSnapshot = progressStateHolder.progressSnapshot
|
||||||
@@ -89,9 +98,20 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
setupStateObservation()
|
setupStateObservation()
|
||||||
|
observeDictionariesForSeedFlag()
|
||||||
initialize()
|
initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observeDictionariesForSeedFlag() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
localDictionaryRepository.dictionaries.collect { dictionaries ->
|
||||||
|
if (hasCompletedStartupScan.value) {
|
||||||
|
preferencesManager.setHadNoDictionaries(dictionaries.isEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupStateObservation() {
|
private fun setupStateObservation() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
var wasIndexing = false
|
var wasIndexing = false
|
||||||
@@ -245,6 +265,9 @@ class SettingsViewModel(
|
|||||||
android.util.Log.w("SettingsViewModel", "Failed to load language preference: ${e.message}")
|
android.util.Log.w("SettingsViewModel", "Failed to load language preference: ${e.message}")
|
||||||
}
|
}
|
||||||
hasCompletedStartupScan.value = true
|
hasCompletedStartupScan.value = true
|
||||||
|
preferencesManager.setHadNoDictionaries(
|
||||||
|
localDictionaryRepository.dictionaries.value.isEmpty()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user