diff --git a/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/AppSettings.kt b/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/AppSettings.kt index 6f2cbecac6..75cdc79cd2 100644 --- a/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/AppSettings.kt +++ b/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/AppSettings.kt @@ -12,5 +12,6 @@ package com.example.executorchllamademo * Holds app-wide settings that are independent of the current module/model. */ data class AppSettings( - val appearanceMode: AppearanceMode = AppearanceMode.SYSTEM + val appearanceMode: AppearanceMode = AppearanceMode.SYSTEM, + val saveChatHistory: Boolean = false ) diff --git a/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/screens/AppSettingsScreen.kt b/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/screens/AppSettingsScreen.kt index 29d53901b0..0c89f0d559 100644 --- a/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/screens/AppSettingsScreen.kt +++ b/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/screens/AppSettingsScreen.kt @@ -30,6 +30,8 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.RadioButton +import androidx.compose.material3.Switch +import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -141,6 +143,42 @@ fun AppSettingsScreen( Spacer(modifier = Modifier.height(8.dp)) + // Save Chat History toggle + Row( + modifier = Modifier + .fillMaxWidth() + .background(appColors.settingsRowBackground, RoundedCornerShape(8.dp)) + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = "Save Chat History", + fontSize = 14.sp, + color = appColors.settingsText + ) + Text( + text = "Persist conversations between sessions", + fontSize = 12.sp, + color = appColors.settingsText.copy(alpha = 0.6f) + ) + } + Switch( + checked = appSettings.saveChatHistory, + onCheckedChange = { enabled -> + appSettings = appSettings.copy(saveChatHistory = enabled) + val prefs = DemoSharedPreferences(context) + prefs.saveAppSettings(appSettings) + }, + colors = SwitchDefaults.colors( + checkedThumbColor = Color.White, + checkedTrackColor = BtnEnabled + ) + ) + } + + Spacer(modifier = Modifier.height(12.dp)) + // Clear Chat button Button( onClick = { showClearChatDialog = true }, @@ -188,6 +226,8 @@ fun AppSettingsScreen( val prefs = DemoSharedPreferences(context) moduleSettings = moduleSettings.copy(isClearChatHistory = true) prefs.saveModuleSettings(moduleSettings) + // Also clear the saved messages immediately + prefs.removeExistingMessages() showClearChatDialog = false } ) { diff --git a/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/viewmodel/ChatViewModel.kt b/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/viewmodel/ChatViewModel.kt index 09d5f8d688..3c907aacec 100644 --- a/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/viewmodel/ChatViewModel.kt +++ b/llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/viewmodel/ChatViewModel.kt @@ -82,10 +82,29 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), L private val contentResolver = application.contentResolver init { - loadSavedMessages() + // Check for clear chat history flag BEFORE loading saved messages + val moduleSettings = demoSharedPreferences.getModuleSettings() + if (moduleSettings.isClearChatHistory) { + // Clear the flag and don't load messages + // Keep isLoadModel flag so model still loads in checkAndLoadSettings() + demoSharedPreferences.removeExistingMessages() + demoSharedPreferences.saveModuleSettings(moduleSettings.copy(isClearChatHistory = false)) + // Don't update currentSettingsFields here - let checkAndLoadSettings() handle it + // so it detects the change and loads the model if needed + } else { + loadSavedMessages() + } } private fun loadSavedMessages() { + val appSettings = demoSharedPreferences.getAppSettings() + // Only load saved messages if saveChatHistory is enabled + if (!appSettings.saveChatHistory) { + // Clear any existing saved messages since saving is disabled + demoSharedPreferences.removeExistingMessages() + return + } + val existingMsgJSON = demoSharedPreferences.getSavedMessages() if (existingMsgJSON.isNotEmpty()) { val gson = Gson() @@ -99,7 +118,14 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), L } fun saveMessages() { - demoSharedPreferences.addMessages(_messages.toList()) + val appSettings = demoSharedPreferences.getAppSettings() + // Only save messages if saveChatHistory is enabled + if (appSettings.saveChatHistory) { + demoSharedPreferences.addMessages(_messages.toList()) + } else { + // Make sure no messages are persisted + demoSharedPreferences.removeExistingMessages() + } } private val systemPromptMessage = "To get started, select your desired model and tokenizer from the top right corner" @@ -109,27 +135,27 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), L val isUpdated = currentSettingsFields != updatedSettingsFields val isLoadModel = updatedSettingsFields.isLoadModel if (isUpdated) { - checkForClearChatHistory(updatedSettingsFields) + val settingsAfterClear = checkForClearChatHistory(updatedSettingsFields) if (isLoadModel) { // Update local copy BEFORE checking media capabilities - val settingsWithLoadFlagCleared = updatedSettingsFields.copy(isLoadModel = false) + val settingsWithLoadFlagCleared = settingsAfterClear.copy(isLoadModel = false) currentSettingsFields = settingsWithLoadFlagCleared demoSharedPreferences.saveModuleSettings(settingsWithLoadFlagCleared) // Update media capabilities after settings are updated - setBackendMode(updatedSettingsFields.backendType) + setBackendMode(settingsAfterClear.backendType) loadLocalModelAndParameters( - updatedSettingsFields.modelFilePath, - updatedSettingsFields.tokenizerFilePath, - updatedSettingsFields.dataPath, - updatedSettingsFields.temperature.toFloat() + settingsAfterClear.modelFilePath, + settingsAfterClear.tokenizerFilePath, + settingsAfterClear.dataPath, + settingsAfterClear.temperature.toFloat() ) } else { - currentSettingsFields = updatedSettingsFields.copy() + currentSettingsFields = settingsAfterClear.copy() // Update media capabilities after settings are updated - setBackendMode(updatedSettingsFields.backendType) + setBackendMode(settingsAfterClear.backendType) if (module == null) { addSystemMessage(systemPromptMessage) } @@ -175,13 +201,18 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), L } } - private fun checkForClearChatHistory(updatedSettingsFields: ModuleSettings) { + private fun checkForClearChatHistory(updatedSettingsFields: ModuleSettings): ModuleSettings { if (updatedSettingsFields.isClearChatHistory) { _messages.clear() demoSharedPreferences.removeExistingMessages() - demoSharedPreferences.saveModuleSettings(updatedSettingsFields.copy(isClearChatHistory = false)) + val clearedSettings = updatedSettingsFields.copy(isClearChatHistory = false) + demoSharedPreferences.saveModuleSettings(clearedSettings) module?.resetContext() + shouldAddSystemPrompt = true + promptID = 0 + return clearedSettings } + return updatedSettingsFields } private fun loadLocalModelAndParameters(