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 coroutineScope = rememberCoroutineScope()
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
// Inference engine state
|
// App core states
|
||||||
val engineState by mainViewModel.engineState.collectAsState()
|
val engineState by mainViewModel.engineState.collectAsState()
|
||||||
|
val showUserOnboarding by mainViewModel.showUserOnboarding.collectAsState()
|
||||||
|
|
||||||
// Model state
|
// Model state
|
||||||
val modelScreenUiMode by modelsViewModel.modelScreenUiMode.collectAsState()
|
val modelScreenUiMode by modelsViewModel.modelScreenUiMode.collectAsState()
|
||||||
|
|
@ -323,7 +324,7 @@ fun AppContent(
|
||||||
toggleMenu = modelsViewModel::toggleFilterMenu
|
toggleMenu = modelsViewModel::toggleFilterMenu
|
||||||
),
|
),
|
||||||
importing = BottomBarConfig.Models.Managing.ImportConfig(
|
importing = BottomBarConfig.Models.Managing.ImportConfig(
|
||||||
showTooltip = true,
|
showTooltip = showUserOnboarding,
|
||||||
isMenuVisible = showImportModelMenu,
|
isMenuVisible = showImportModelMenu,
|
||||||
toggleMenu = { show -> modelsManagementViewModel.toggleImportMenu(show) },
|
toggleMenu = { show -> modelsManagementViewModel.toggleImportMenu(show) },
|
||||||
importFromLocal = {
|
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.core.longPreferencesKey
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
@ -23,11 +25,11 @@ class UserPreferences @Inject constructor (
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Performance monitoring preferences
|
|
||||||
private const val DATASTORE_SETTINGS = "settings"
|
private const val DATASTORE_SETTINGS = "settings"
|
||||||
private val Context.settingsDataStore: DataStore<Preferences>
|
private val Context.settingsDataStore: DataStore<Preferences>
|
||||||
by preferencesDataStore(name = DATASTORE_SETTINGS)
|
by preferencesDataStore(name = DATASTORE_SETTINGS)
|
||||||
|
|
||||||
|
// Preferences keys
|
||||||
private val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled")
|
private val PERFORMANCE_MONITORING_ENABLED = booleanPreferencesKey("performance_monitoring_enabled")
|
||||||
private val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature")
|
private val USE_FAHRENHEIT_TEMPERATURE = booleanPreferencesKey("use_fahrenheit_temperature")
|
||||||
private val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms")
|
private val MONITORING_INTERVAL_MS = longPreferencesKey("monitoring_interval_ms")
|
||||||
|
|
@ -45,16 +47,15 @@ class UserPreferences @Inject constructor (
|
||||||
/**
|
/**
|
||||||
* Gets whether performance monitoring is enabled.
|
* Gets whether performance monitoring is enabled.
|
||||||
*/
|
*/
|
||||||
fun isPerformanceMonitoringEnabled(): Flow<Boolean> {
|
fun isPerformanceMonitoringEnabled(): Flow<Boolean> =
|
||||||
return context.settingsDataStore.data.map { preferences ->
|
context.settingsDataStore.data.map { preferences ->
|
||||||
preferences[PERFORMANCE_MONITORING_ENABLED] != false
|
preferences[PERFORMANCE_MONITORING_ENABLED] != false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether performance monitoring is enabled.
|
* Sets whether performance monitoring is enabled.
|
||||||
*/
|
*/
|
||||||
suspend fun setPerformanceMonitoringEnabled(enabled: Boolean) {
|
suspend fun setPerformanceMonitoringEnabled(enabled: Boolean) = withContext(Dispatchers.IO) {
|
||||||
context.settingsDataStore.edit { preferences ->
|
context.settingsDataStore.edit { preferences ->
|
||||||
preferences[PERFORMANCE_MONITORING_ENABLED] = enabled
|
preferences[PERFORMANCE_MONITORING_ENABLED] = enabled
|
||||||
}
|
}
|
||||||
|
|
@ -63,16 +64,15 @@ class UserPreferences @Inject constructor (
|
||||||
/**
|
/**
|
||||||
* Gets whether temperature should be displayed in Fahrenheit.
|
* Gets whether temperature should be displayed in Fahrenheit.
|
||||||
*/
|
*/
|
||||||
fun usesFahrenheitTemperature(): Flow<Boolean> {
|
fun usesFahrenheitTemperature(): Flow<Boolean> =
|
||||||
return context.settingsDataStore.data.map { preferences ->
|
context.settingsDataStore.data.map { preferences ->
|
||||||
preferences[USE_FAHRENHEIT_TEMPERATURE] == true
|
preferences[USE_FAHRENHEIT_TEMPERATURE] == true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether temperature should be displayed in Fahrenheit.
|
* 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 ->
|
context.settingsDataStore.edit { preferences ->
|
||||||
preferences[USE_FAHRENHEIT_TEMPERATURE] = useFahrenheit
|
preferences[USE_FAHRENHEIT_TEMPERATURE] = useFahrenheit
|
||||||
}
|
}
|
||||||
|
|
@ -83,16 +83,15 @@ class UserPreferences @Inject constructor (
|
||||||
*
|
*
|
||||||
* TODO-han.yin: replace with Enum value instead of millisecond value
|
* TODO-han.yin: replace with Enum value instead of millisecond value
|
||||||
*/
|
*/
|
||||||
fun getMonitoringInterval(): Flow<Long> {
|
fun getMonitoringInterval(): Flow<Long> =
|
||||||
return context.settingsDataStore.data.map { preferences ->
|
context.settingsDataStore.data.map { preferences ->
|
||||||
preferences[MONITORING_INTERVAL_MS] ?: DEFAULT_MONITORING_INTERVAL_MS
|
preferences[MONITORING_INTERVAL_MS] ?: DEFAULT_MONITORING_INTERVAL_MS
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the monitoring interval in milliseconds.
|
* Sets the monitoring interval in milliseconds.
|
||||||
*/
|
*/
|
||||||
suspend fun setMonitoringInterval(intervalMs: Long) {
|
suspend fun setMonitoringInterval(intervalMs: Long) = withContext(Dispatchers.IO) {
|
||||||
context.settingsDataStore.edit { preferences ->
|
context.settingsDataStore.edit { preferences ->
|
||||||
preferences[MONITORING_INTERVAL_MS] = intervalMs
|
preferences[MONITORING_INTERVAL_MS] = intervalMs
|
||||||
}
|
}
|
||||||
|
|
@ -101,16 +100,15 @@ class UserPreferences @Inject constructor (
|
||||||
/**
|
/**
|
||||||
* Gets the current theme mode.
|
* Gets the current theme mode.
|
||||||
*/
|
*/
|
||||||
fun getThemeMode(): Flow<Int> {
|
fun getThemeMode(): Flow<Int> =
|
||||||
return context.settingsDataStore.data.map { preferences ->
|
context.settingsDataStore.data.map { preferences ->
|
||||||
preferences[THEME_MODE] ?: THEME_MODE_AUTO
|
preferences[THEME_MODE] ?: THEME_MODE_AUTO
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the theme mode.
|
* Sets the theme mode.
|
||||||
*/
|
*/
|
||||||
suspend fun setThemeMode(mode: Int) {
|
suspend fun setThemeMode(mode: Int) = withContext(Dispatchers.IO) {
|
||||||
context.settingsDataStore.edit { preferences ->
|
context.settingsDataStore.edit { preferences ->
|
||||||
preferences[THEME_MODE] = mode
|
preferences[THEME_MODE] = mode
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,65 @@
|
||||||
package com.example.llama.viewmodel
|
package com.example.llama.viewmodel
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.llama.data.source.prefs.AppPreferences
|
||||||
import com.example.llama.engine.InferenceService
|
import com.example.llama.engine.InferenceService
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@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 (
|
class MainViewModel @Inject constructor (
|
||||||
|
private val appPreferences: AppPreferences,
|
||||||
private val inferenceService: InferenceService,
|
private val inferenceService: InferenceService,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val engineState = inferenceService.engineState
|
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
|
* Unload the current model and release the resources
|
||||||
*/
|
*/
|
||||||
suspend fun unloadModel() = inferenceService.unloadModel()
|
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 android.llama.cpp.TierDetection
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.example.llama.data.source.prefs.UserPreferences
|
|
||||||
import com.example.llama.data.repo.ModelRepository
|
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.BatteryMetrics
|
||||||
import com.example.llama.monitoring.MemoryMetrics
|
import com.example.llama.monitoring.MemoryMetrics
|
||||||
import com.example.llama.monitoring.PerformanceMonitor
|
import com.example.llama.monitoring.PerformanceMonitor
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue