From 3b702579a1f638f74a6aee9f2008cb4912cb0b00 Mon Sep 17 00:00:00 2001 From: Yu-ga <74749461+yuga-hashimoto@users.noreply.github.com> Date: Sat, 24 Oct 2026 00:54:17 +0900 Subject: [PATCH] fix(chat): make the favorite star work in the voice assistant model picker The voice assistant settings opened the shared model picker without passing favoriteModelKeys or onToggleFavorite. Both had defaults (an empty set and a no-op), so that picker drew a star on every row that did nothing when tapped, while the chat and schedule pickers toggled fine. Thread the favorites from SettingsViewModel through SettingsNavGraph and VoiceSettingsScreen into the picker, and drop the defaults so a caller that forgets them no longer compiles. Add an instrumented test that tapping the star toggles the favorite without selecting the model. Co-Authored-By: Claude Sonnet 5.5 --- ...elAndRuntimePickerSheetInstrumentedTest.kt | 36 +++++++++++++++++++ .../chat/ModelAndRuntimePickerSheet.kt | 6 ++-- .../feature/settings/VoiceSettingsScreen.kt | 8 +++++ .../andcode/ui/navigation/SettingsNavGraph.kt | 2 ++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheetInstrumentedTest.kt b/app/src/androidTest/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheetInstrumentedTest.kt index 85fb0d129..111f1c11c 100644 --- a/app/src/androidTest/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheetInstrumentedTest.kt +++ b/app/src/androidTest/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheetInstrumentedTest.kt @@ -9,13 +9,18 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasScrollAction import androidx.compose.ui.test.hasScrollToIndexAction import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onAllNodesWithContentDescription +import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToIndex import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipe import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.yugahashimoto.andcode.R import com.yugahashimoto.andcode.core.api.OpenCodeModel import com.yugahashimoto.andcode.core.api.OpenCodeProvider +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -38,6 +43,8 @@ class ModelAndRuntimePickerSheetInstrumentedTest { selectedProviderId = testProvider.id, selectedModelId = null, onSelectModel = { _, _ -> }, + favoriteModelKeys = emptySet(), + onToggleFavorite = { _, _ -> }, onDismiss = { dismissed = true }, ) } @@ -53,6 +60,35 @@ class ModelAndRuntimePickerSheetInstrumentedTest { composeRule.runOnIdle { assertFalse(dismissed) } } + @Test + fun tappingStarTogglesFavoriteWithoutSelectingModel() { + val toggled = mutableListOf>() + val selected = mutableListOf>() + + composeRule.setContent { + ModelAndRuntimePickerSheet( + runtimeTargets = emptyList(), + selectedRuntimeId = null, + onSelectRuntime = {}, + providers = listOf(testProvider), + selectedProviderId = testProvider.id, + selectedModelId = null, + onSelectModel = { providerId, modelId -> selected += providerId to modelId }, + favoriteModelKeys = emptySet(), + onToggleFavorite = { providerId, modelId -> toggled += providerId to modelId }, + onDismiss = {}, + ) + } + + val star = composeRule.activity.getString(R.string.cd_favorite) + composeRule.onAllNodesWithContentDescription(star)[0].performClick() + + composeRule.runOnIdle { + assertEquals(1, toggled.size) + assertTrue(selected.isEmpty()) + } + } + private companion object { val testProvider = OpenCodeProvider( diff --git a/app/src/main/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheet.kt b/app/src/main/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheet.kt index 72772d6f2..75cb069be 100644 --- a/app/src/main/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheet.kt +++ b/app/src/main/java/com/yugahashimoto/andcode/feature/chat/ModelAndRuntimePickerSheet.kt @@ -74,10 +74,12 @@ fun ModelAndRuntimePickerSheet( selectedProviderId: String?, selectedModelId: String?, onSelectModel: (String, String) -> Unit, - favoriteModelKeys: Set = emptySet(), + // Required, with no default: every row draws a star. A caller that forgot these used to get an + // empty set and a no-op, so its stars rendered but did nothing when tapped. + favoriteModelKeys: Set, + onToggleFavorite: (String, String) -> Unit, recentModelKeys: List = emptyList(), hiddenModelKeys: Set = emptySet(), - onToggleFavorite: (String, String) -> Unit = { _, _ -> }, showLocalSuffix: Boolean = true, onConnectProvider: (() -> Unit)? = null, onDismiss: () -> Unit, diff --git a/app/src/main/java/com/yugahashimoto/andcode/feature/settings/VoiceSettingsScreen.kt b/app/src/main/java/com/yugahashimoto/andcode/feature/settings/VoiceSettingsScreen.kt index 66d6f50e7..1f54e94ca 100644 --- a/app/src/main/java/com/yugahashimoto/andcode/feature/settings/VoiceSettingsScreen.kt +++ b/app/src/main/java/com/yugahashimoto/andcode/feature/settings/VoiceSettingsScreen.kt @@ -105,6 +105,7 @@ fun VoiceSettingsScreen( workspaces: List = emptyList(), assistantProviderId: String? = null, assistantModelId: String? = null, + favoriteModelKeys: Set = emptySet(), assistantWorkspacePath: String = "", onTtsChange: (Boolean) -> Unit, onTtsProviderChange: (String) -> Unit = {}, @@ -129,6 +130,7 @@ fun VoiceSettingsScreen( onWakeWordModelRemove: () -> Unit = {}, onAssistantRuntimeChange: (String) -> Unit = {}, onAssistantModelChange: (String, String) -> Unit = { _, _ -> }, + onToggleFavoriteModel: (String, String) -> Unit = { _, _ -> }, onAssistantWorkspaceChange: (String) -> Unit = {}, onBack: () -> Unit, ) { @@ -302,9 +304,11 @@ fun VoiceSettingsScreen( assistantRuntimeId = assistantRuntimeId, assistantProviderId = assistantProviderId, assistantModelId = assistantModelId, + favoriteModelKeys = favoriteModelKeys, assistantWorkspacePath = assistantWorkspacePath, onRuntimeChange = onAssistantRuntimeChange, onModelChange = onAssistantModelChange, + onToggleFavoriteModel = onToggleFavoriteModel, onWorkspaceChange = onAssistantWorkspaceChange, ) } @@ -587,9 +591,11 @@ private fun AssistantTargetSection( assistantRuntimeId: String?, assistantProviderId: String?, assistantModelId: String?, + favoriteModelKeys: Set, assistantWorkspacePath: String, onRuntimeChange: (String) -> Unit, onModelChange: (String, String) -> Unit, + onToggleFavoriteModel: (String, String) -> Unit, onWorkspaceChange: (String) -> Unit, ) { var showPicker by remember { mutableStateOf(false) } @@ -648,6 +654,8 @@ private fun AssistantTargetSection( providers = targetProviders, selectedProviderId = assistantProviderId, selectedModelId = assistantModelId, + favoriteModelKeys = favoriteModelKeys, + onToggleFavorite = onToggleFavoriteModel, showLocalSuffix = false, onSelectModel = { providerId, modelId -> onModelChange(providerId, modelId) diff --git a/app/src/main/java/com/yugahashimoto/andcode/ui/navigation/SettingsNavGraph.kt b/app/src/main/java/com/yugahashimoto/andcode/ui/navigation/SettingsNavGraph.kt index f43ddcb97..a09d40019 100644 --- a/app/src/main/java/com/yugahashimoto/andcode/ui/navigation/SettingsNavGraph.kt +++ b/app/src/main/java/com/yugahashimoto/andcode/ui/navigation/SettingsNavGraph.kt @@ -268,6 +268,8 @@ fun NavGraphBuilder.settingsNavGraph( onAssistantModelChange = { providerId, modelId -> settingsViewModel.setAssistantModel(providerId, modelId) }, + favoriteModelKeys = settingsState.favoriteModelKeys, + onToggleFavoriteModel = settingsViewModel::toggleFavoriteModel, onAssistantWorkspaceChange = { path -> settingsViewModel.setAssistantWorkspacePath(path) },