UI: adds AppPreferences to track user onboarding status
This commit is contained in:
parent
a9b84b9db3
commit
c87ff9c1b3
|
|
@ -94,8 +94,9 @@ fun AppContent(
|
|||
val coroutineScope = rememberCoroutineScope()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
// Inference engine state
|
||||
// App core states
|
||||
val engineState by mainViewModel.engineState.collectAsState()
|
||||
val showUserOnboarding by mainViewModel.showUserOnboarding.collectAsState()
|
||||
|
||||
// Model state
|
||||
val modelScreenUiMode by modelsViewModel.modelScreenUiMode.collectAsState()
|
||||
|
|
@ -323,7 +324,7 @@ fun AppContent(
|
|||
toggleMenu = modelsViewModel::toggleFilterMenu
|
||||
),
|
||||
importing = BottomBarConfig.Models.Managing.ImportConfig(
|
||||
showTooltip = true,
|
||||
showTooltip = showUserOnboarding,
|
||||
isMenuVisible = showImportModelMenu,
|
||||
toggleMenu = { show -> modelsManagementViewModel.toggleImportMenu(show) },
|
||||
importFromLocal = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package com.example.llama.data.source.prefs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Manages internal preferences for the application.
|
||||
*/
|
||||
@Singleton
|
||||
class AppPreferences @Inject constructor (
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
companion object {
|
||||
private const val DATASTORE_APP = "app"
|
||||
private val Context.appDataStore: DataStore<Preferences>
|
||||
by preferencesDataStore(name = DATASTORE_APP)
|
||||
|
||||
// Preference keys
|
||||
private val USER_HAS_IMPORTED_FIRST_MODEL = booleanPreferencesKey("user_has_imported_first_model")
|
||||
private val USER_HAS_CHATTED_WITH_MODEL = booleanPreferencesKey("user_has_chatted_with_model")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the user has imported his first model
|
||||
*/
|
||||
fun userHasImportedFirstModel(): Flow<Boolean> =
|
||||
context.appDataStore.data.map { preferences ->
|
||||
preferences[USER_HAS_IMPORTED_FIRST_MODEL] == true
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the user has completed importing the first model.
|
||||
*/
|
||||
suspend fun setUserHasImportedFirstModel(done: Boolean) = withContext(Dispatchers.IO) {
|
||||
context.appDataStore.edit { preferences ->
|
||||
preferences[USER_HAS_IMPORTED_FIRST_MODEL] = done
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the user has chatted with a model
|
||||
*/
|
||||
fun userHasChattedWithModel(): Flow<Boolean> =
|
||||
context.appDataStore.data.map { preferences ->
|
||||
preferences[USER_HAS_CHATTED_WITH_MODEL] == true
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the user has completed chatting with a model.
|
||||
*/
|
||||
suspend fun setUserHasChattedWithModel(done: Boolean) = withContext(Dispatchers.IO) {
|
||||
context.appDataStore.edit { preferences ->
|
||||
preferences[USER_HAS_CHATTED_WITH_MODEL] = done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,10 @@ import androidx.datastore.preferences.core.intPreferencesKey
|
|||
import androidx.datastore.preferences.core.longPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -23,11 +25,11 @@ class UserPreferences @Inject constructor (
|
|||
) {
|
||||
|
||||
companion object {
|
||||
// Performance monitoring preferences
|
||||
private const val DATASTORE_SETTINGS = "settings"
|
||||
private val Context.settingsDataStore: DataStore<Preferences>
|
||||
by preferencesDataStore(name = DATASTORE_SETTINGS)
|
||||
|
||||
// Preferences keys
|
||||
private val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled")
|
||||
private val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature")
|
||||
private val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms")
|
||||
|
|
@ -45,16 +47,15 @@ class UserPreferences @Inject constructor (
|
|||
/**
|
||||
* Gets whether performance monitoring is enabled.
|
||||
*/
|
||||
fun isPerformanceMonitoringEnabled(): Flow<Boolean> {
|
||||
return context.settingsDataStore.data.map { preferences ->
|
||||
fun isPerformanceMonitoringEnabled(): Flow<Boolean> =
|
||||
context.settingsDataStore.data.map { preferences ->
|
||||
preferences[PERFORMANCE_MONITORING_ENABLED] != false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether performance monitoring is enabled.
|
||||
*/
|
||||
suspend fun setPerformanceMonitoringEnabled(enabled: Boolean) {
|
||||
suspend fun setPerformanceMonitoringEnabled(enabled: Boolean) = withContext(Dispatchers.IO) {
|
||||
context.settingsDataStore.edit { preferences ->
|
||||
preferences[PERFORMANCE_MONITORING_ENABLED] = enabled
|
||||
}
|
||||
|
|
@ -63,16 +64,15 @@ class UserPreferences @Inject constructor (
|
|||
/**
|
||||
* Gets whether temperature should be displayed in Fahrenheit.
|
||||
*/
|
||||
fun usesFahrenheitTemperature(): Flow<Boolean> {
|
||||
return context.settingsDataStore.data.map { preferences ->
|
||||
fun usesFahrenheitTemperature(): Flow<Boolean> =
|
||||
context.settingsDataStore.data.map { preferences ->
|
||||
preferences[USE_FAHRENHEIT_TEMPERATURE] == true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether temperature should be displayed in Fahrenheit.
|
||||
*/
|
||||
suspend fun setUseFahrenheitTemperature(useFahrenheit: Boolean) {
|
||||
suspend fun setUseFahrenheitTemperature(useFahrenheit: Boolean) = withContext(Dispatchers.IO) {
|
||||
context.settingsDataStore.edit { preferences ->
|
||||
preferences[USE_FAHRENHEIT_TEMPERATURE] = useFahrenheit
|
||||
}
|
||||
|
|
@ -83,16 +83,15 @@ class UserPreferences @Inject constructor (
|
|||
*
|
||||
* TODO-han.yin: replace with Enum value instead of millisecond value
|
||||
*/
|
||||
fun getMonitoringInterval(): Flow<Long> {
|
||||
return context.settingsDataStore.data.map { preferences ->
|
||||
fun getMonitoringInterval(): Flow<Long> =
|
||||
context.settingsDataStore.data.map { preferences ->
|
||||
preferences[MONITORING_INTERVAL_MS] ?: DEFAULT_MONITORING_INTERVAL_MS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the monitoring interval in milliseconds.
|
||||
*/
|
||||
suspend fun setMonitoringInterval(intervalMs: Long) {
|
||||
suspend fun setMonitoringInterval(intervalMs: Long) = withContext(Dispatchers.IO) {
|
||||
context.settingsDataStore.edit { preferences ->
|
||||
preferences[MONITORING_INTERVAL_MS] = intervalMs
|
||||
}
|
||||
|
|
@ -101,16 +100,15 @@ class UserPreferences @Inject constructor (
|
|||
/**
|
||||
* Gets the current theme mode.
|
||||
*/
|
||||
fun getThemeMode(): Flow<Int> {
|
||||
return context.settingsDataStore.data.map { preferences ->
|
||||
fun getThemeMode(): Flow<Int> =
|
||||
context.settingsDataStore.data.map { preferences ->
|
||||
preferences[THEME_MODE] ?: THEME_MODE_AUTO
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme mode.
|
||||
*/
|
||||
suspend fun setThemeMode(mode: Int) {
|
||||
suspend fun setThemeMode(mode: Int) = withContext(Dispatchers.IO) {
|
||||
context.settingsDataStore.edit { preferences ->
|
||||
preferences[THEME_MODE] = mode
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,65 @@
|
|||
package com.example.llama.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.llama.data.source.prefs.AppPreferences
|
||||
import com.example.llama.engine.InferenceService
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
/**
|
||||
* Main ViewModel that expose the core states of [InferenceEngine]
|
||||
* Main ViewModel that expose the core states of [InferenceService] and [AppPreferences]
|
||||
*/
|
||||
class MainViewModel @Inject constructor (
|
||||
private val appPreferences: AppPreferences,
|
||||
private val inferenceService: InferenceService,
|
||||
) : ViewModel() {
|
||||
|
||||
val engineState = inferenceService.engineState
|
||||
|
||||
// App preferences
|
||||
private val _showModelImportTooltip = MutableStateFlow(true)
|
||||
val showModelImportTooltip: StateFlow<Boolean> = _showModelImportTooltip.asStateFlow()
|
||||
|
||||
private val _showChatTooltip = MutableStateFlow(true)
|
||||
val showChatTooltip: StateFlow<Boolean> = _showChatTooltip.asStateFlow()
|
||||
|
||||
|
||||
/**
|
||||
* Unload the current model and release the resources
|
||||
*/
|
||||
suspend fun unloadModel() = inferenceService.unloadModel()
|
||||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
appPreferences.userHasImportedFirstModel().collect {
|
||||
_showModelImportTooltip.value = !it
|
||||
}
|
||||
}
|
||||
launch {
|
||||
appPreferences.userHasChattedWithModel().collect {
|
||||
_showChatTooltip.value = !it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun waiveModelImportTooltip() {
|
||||
android.util.Log.w("JOJO", "WAIVE IMPORT TOOLTIP!")
|
||||
viewModelScope.launch {
|
||||
appPreferences.setUserHasImportedFirstModel(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun waiveChatTooltip() {
|
||||
viewModelScope.launch {
|
||||
appPreferences.setUserHasChattedWithModel(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import android.llama.cpp.LLamaTier
|
|||
import android.llama.cpp.TierDetection
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.llama.data.source.prefs.UserPreferences
|
||||
import com.example.llama.data.repo.ModelRepository
|
||||
import com.example.llama.data.source.prefs.UserPreferences
|
||||
import com.example.llama.monitoring.BatteryMetrics
|
||||
import com.example.llama.monitoring.MemoryMetrics
|
||||
import com.example.llama.monitoring.PerformanceMonitor
|
||||
|
|
|
|||
Loading…
Reference in New Issue