Add option to hide the voice search button

Adds a toggle to SearchPreferences (`show_voice_search_button`,
default true) and a new entry in the search page's view options
dialog. When disabled, the voice search button is hidden from
the search row.

For existing users, an entry in AppUpgradeHandler sets the value
to true so the button stays visible after upgrade.
This commit is contained in:
Justin Visser 2026-05-21 13:01:12 +02:00
parent 366483599c
commit bfbceb1999
5 changed files with 66 additions and 7 deletions

View file

@ -105,6 +105,7 @@ class AppPreferencesSerializer
.newBuilder()
.apply {
combinedSearchResults = false
showVoiceSearchButton = true
}.build()
subtitlesPreferences =

View file

@ -21,6 +21,7 @@ import com.github.damontecres.wholphin.preferences.updatePhotoPreferences
import com.github.damontecres.wholphin.preferences.updatePlaybackOverrides
import com.github.damontecres.wholphin.preferences.updatePlaybackPreferences
import com.github.damontecres.wholphin.preferences.updateScreensaverPreferences
import com.github.damontecres.wholphin.preferences.updateSearchPreferences
import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
@ -334,5 +335,11 @@ class AppUpgradeHandler
it.updatePlaybackPreferences { cinemaMode = AppPreference.CinemaMode.defaultValue }
}
}
if (previous.isEqualOrBefore(Version.fromString("0.6.4-8-g0"))) {
appPreferences.updateData {
it.updateSearchPreferences { showVoiceSearchButton = true }
}
}
}
}

View file

@ -138,6 +138,11 @@ class SearchViewModel
.map { it.interfacePreferences.searchPreferences.combinedSearchResults }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
val voiceSearchButtonVisibleFlow: StateFlow<Boolean> =
appPreferences.data
.map { it.interfacePreferences.searchPreferences.showVoiceSearchButton }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true)
val movies = MutableLiveData<SearchResult>(SearchResult.NoQuery)
val series = MutableLiveData<SearchResult>(SearchResult.NoQuery)
val episodes = MutableLiveData<SearchResult>(SearchResult.NoQuery)
@ -275,6 +280,16 @@ class SearchViewModel
}
}
fun setVoiceSearchButtonVisible(visible: Boolean) {
viewModelScope.launchIO {
appPreferences.updateData {
it.updateSearchPreferences {
showVoiceSearchButton = visible
}
}
}
}
private fun searchSeerr(query: String) {
viewModelScope.launchIO {
if (seerrService.active.first()) {
@ -351,6 +366,7 @@ fun SearchPage(
val seerrResults by viewModel.seerrResults.observeAsState(SearchResult.NoQuery)
val combinedResults by viewModel.combinedResults.observeAsState(SearchResult.NoQuery)
val combinedMode by viewModel.combinedModeFlow.collectAsState()
val voiceSearchButtonVisible by viewModel.voiceSearchButtonVisibleFlow.collectAsState()
// val query = rememberTextFieldState()
var query by rememberSaveable { mutableStateOf("") }
@ -508,13 +524,15 @@ fun SearchPage(
.focusRestorer(textFieldFocusRequester)
.focusRequester(focusRequesters[SEARCH_ROW]),
) {
VoiceSearchButton(
onSpeechResult = { spokenText ->
query = spokenText
triggerImmediateSearch(spokenText)
},
voiceInputManager = viewModel.voiceInputManager,
)
if (voiceSearchButtonVisible) {
VoiceSearchButton(
onSpeechResult = { spokenText ->
query = spokenText
triggerImmediateSearch(spokenText)
},
voiceInputManager = viewModel.voiceInputManager,
)
}
SearchEditTextBox(
value = query,
@ -757,6 +775,8 @@ fun SearchPage(
SearchViewOptionsDialog(
combinedResults = combinedMode,
onCombinedResultsChange = viewModel::setCombinedResults,
voiceSearchButtonVisible = voiceSearchButtonVisible,
onVoiceSearchButtonVisibleChange = viewModel::setVoiceSearchButtonVisible,
onDismissRequest = { showViewOptions = false },
)
}
@ -766,6 +786,8 @@ fun SearchPage(
fun SearchViewOptionsDialog(
combinedResults: Boolean,
onCombinedResultsChange: (Boolean) -> Unit,
voiceSearchButtonVisible: Boolean,
onVoiceSearchButtonVisibleChange: (Boolean) -> Unit,
onDismissRequest: () -> Unit,
) {
Dialog(
@ -815,6 +837,31 @@ fun SearchViewOptionsDialog(
onClick = { onCombinedResultsChange(!combinedResults) },
modifier = Modifier.fillMaxWidth(),
)
ListItem(
selected = false,
headlineContent = {
Text(stringResource(R.string.show_voice_search_button))
},
supportingContent = {
Text(
if (voiceSearchButtonVisible) {
stringResource(R.string.show_voice_search_button_on)
} else {
stringResource(R.string.show_voice_search_button_off)
},
)
},
trailingContent = {
Switch(
checked = voiceSearchButtonVisible,
onCheckedChange = onVoiceSearchButtonVisibleChange,
colors = SwitchColors(),
)
},
onClick = { onVoiceSearchButtonVisibleChange(!voiceSearchButtonVisible) },
modifier = Modifier.fillMaxWidth(),
)
}
}
}

View file

@ -168,6 +168,7 @@ message ScreensaverPreferences{
message SearchPreferences {
bool combined_search_results = 1;
bool show_voice_search_button = 2;
}
enum DisplayToggle{

View file

@ -449,6 +449,9 @@
<string name="combined_search_results">Combined search results</string>
<string name="combined_search_results_on">Show all results in a single list</string>
<string name="combined_search_results_off">Show results by category</string>
<string name="show_voice_search_button">Show voice search button</string>
<string name="show_voice_search_button_on">Visible</string>
<string name="show_voice_search_button_off">Hidden</string>
<string name="results">Results</string>
<string name="resolution_switching">Resolution switching</string>
<string name="local">Local</string>