Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.DialogFragment
Expand All @@ -37,11 +34,10 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.ichi2.anki.AndroidTtsVoice
import com.ichi2.anki.R
import com.ichi2.anki.databinding.DialogTtsVoicesBinding
import com.ichi2.anki.databinding.DialogTtsVoicesVoiceBinding
import com.ichi2.anki.dialogs.viewmodel.TtsVoicesViewModel
import com.ichi2.anki.libanki.TtsVoice
import com.ichi2.anki.localizedErrorMessage
Expand All @@ -50,6 +46,7 @@ import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.anki.utils.openUrl
import com.ichi2.themes.Themes
import com.ichi2.utils.UiUtil.makeFullscreen
import dev.androidbroadcast.vbpd.viewBinding
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import timber.log.Timber
Expand All @@ -72,76 +69,54 @@ import timber.log.Timber
*
* @see [TtsVoicesViewModel]
*/
class TtsVoicesDialogFragment : DialogFragment() {
class TtsVoicesDialogFragment : DialogFragment(R.layout.dialog_tts_voices) {
private val viewModel: TtsVoicesViewModel by this.viewModels()

private lateinit var progressBar: LinearProgressIndicator
private lateinit var layout: View
private lateinit var spokenTextEditText: EditText
private lateinit var recyclerView: RecyclerView
private lateinit var internetRequiredChip: Chip
private val binding by viewBinding(DialogTtsVoicesBinding::bind)

private lateinit var voicesAdapter: TtsVoiceAdapter

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?,
): View {
) {
voicesAdapter = TtsVoiceAdapter()
Themes.setTheme(requireContext()) // (re)-enable selectableItemBackground on theme change

layout =
inflater.inflate(R.layout.dialog_tts_voices, null).apply {
recyclerView =
findViewById<RecyclerView>(R.id.files).apply {
this.adapter = voicesAdapter
}
spokenTextEditText =
findViewById<EditText>(R.id.spoken_text).apply {
// setup the initial value from the UI
viewModel.setSpokenText(text.toString())
doOnTextChanged { text, _, _, _ -> viewModel.setSpokenText(text.toString()) }
}
findViewById<MaterialButton>(R.id.back_button).apply {
setOnClickListener {
this@TtsVoicesDialogFragment.dismiss()
}
}
findViewById<Button>(R.id.options_buttons).apply {
setOnClickListener { openTtsSettings() }
}
internetRequiredChip =
findViewById<Chip>(R.id.toggle_internet_required).apply {
setOnCheckedChangeListener { _, value ->
viewModel.showInternetEnabled.value = value
chipBackgroundColor =
if (value) {
// TODO: This should be RMaterial.attr.colorSecondaryContainer
// but this shows as Purple after Themes.setTheme
ColorStateList.valueOf(requireContext().getColor(R.color.text_input_background))
} else {
ColorStateList.valueOf(Color.TRANSPARENT)
}
}
viewModel.showInternetEnabled.value = this.isChecked
binding.files.adapter = voicesAdapter
binding.spokenTextEditText.apply {
// setup the initial value from the UI
viewModel.setSpokenText(text.toString())
doOnTextChanged { text, _, _, _ -> viewModel.setSpokenText(text.toString()) }
}
binding.backButton.setOnClickListener { dismiss() }
binding.optionsButtons.setOnClickListener { openTtsSettings() }
binding.toggleInternetRequired.apply {
setOnCheckedChangeListener { _, value ->
viewModel.showInternetEnabled.value = value
chipBackgroundColor =
if (value) {
// TODO: This should be RMaterial.attr.colorSecondaryContainer
// but this shows as Purple after Themes.setTheme
ColorStateList.valueOf(requireContext().getColor(R.color.text_input_background))
} else {
ColorStateList.valueOf(Color.TRANSPARENT)
}
findViewById<Chip>(R.id.only_show_uninstalled).apply {
setOnCheckedChangeListener { _, value ->
viewModel.showNotInstalled.value = value
chipBackgroundColor =
if (value) {
ColorStateList.valueOf(requireContext().getColor(R.color.text_input_background))
} else {
ColorStateList.valueOf(Color.TRANSPARENT)
}
}
viewModel.showInternetEnabled.value = this.isChecked
}
binding.onlyShowUninstalled.apply {
setOnCheckedChangeListener { _, value ->
viewModel.showNotInstalled.value = value
chipBackgroundColor =
if (value) {
ColorStateList.valueOf(requireContext().getColor(R.color.text_input_background))
} else {
ColorStateList.valueOf(Color.TRANSPARENT)
}
viewModel.showNotInstalled.value = this.isChecked
}
progressBar = findViewById(R.id.progress)
}

return layout
viewModel.showNotInstalled.value = this.isChecked
}
}

fun openTtsSettings() {
Expand All @@ -164,7 +139,7 @@ class TtsVoicesDialogFragment : DialogFragment() {

viewModel.availableVoicesFlow.observe {
if (it is TtsVoicesViewModel.VoiceLoadingState.Failure) {
progressBar.visibility = View.VISIBLE
binding.progress.visibility = View.VISIBLE
AlertDialog
.Builder(requireContext())
.setMessage(it.exception.localizedMessage)
Expand All @@ -175,13 +150,13 @@ class TtsVoicesDialogFragment : DialogFragment() {
if (it is TtsVoicesViewModel.VoiceLoadingState.Success) {
Timber.v("loaded new voice collection")
voicesAdapter.submitList(it.voices)
progressBar.visibility = View.GONE
binding.progress.visibility = View.GONE
}
}

viewModel.showNotInstalled.observe { showInstalled ->
// if the user is showing installed, it shows ALL uninstalled voices
internetRequiredChip.isEnabled = !showInstalled
binding.toggleInternetRequired.isEnabled = !showInstalled
}

viewModel.uninstalledVoicePlayed.observe { voice ->
Expand Down Expand Up @@ -232,38 +207,34 @@ class TtsVoicesDialogFragment : DialogFragment() {
// inner allows access to viewModel/openTtsSettings
inner class TtsVoiceAdapter : ListAdapter<AndroidTtsVoice, TtsVoiceAdapter.TtsViewHolder>(TtsVoiceDiffCallback()) {
inner class TtsViewHolder(
private val voiceView: View,
) : RecyclerView.ViewHolder(voiceView) {
private val textViewTop = voiceView.findViewById<TextView>(R.id.mtrl_list_item_secondary_text)
private val textViewBottom = voiceView.findViewById<TextView>(R.id.mtrl_list_item_text)
private val actionButton = voiceView.findViewById<MaterialButton>(R.id.action_button)
private val localOrOffline = voiceView.findViewById<MaterialButton>(R.id.local_or_network)

private val binding: DialogTtsVoicesVoiceBinding,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(voice: AndroidTtsVoice) {
textViewTop.text = voice.normalizedLocale.displayName
textViewBottom.text = voice.tryDisplayLocalizedName()
binding.textViewTop.text = voice.normalizedLocale.displayName
binding.textViewBottom.text = voice.tryDisplayLocalizedName()

localOrOffline.setIconResource(
binding.localOrNetwork.setIconResource(
if (voice.isNetworkConnectionRequired) R.drawable.baseline_wifi_24 else R.drawable.baseline_offline_pin_24,
)
if (voice.unavailable()) {
actionButton.setOnClickListener { openTtsSettings() }
actionButton.setIconResource(R.drawable.ic_file_download_white)
binding.actionButton.setOnClickListener { openTtsSettings() }
binding.actionButton.setIconResource(R.drawable.ic_file_download_white)
} else {
actionButton.setOnClickListener { viewModel.copyToClipboard(voice) }
actionButton.setIconResource(R.drawable.baseline_content_copy_24)
binding.actionButton.setOnClickListener { viewModel.copyToClipboard(voice) }
binding.actionButton.setIconResource(R.drawable.baseline_content_copy_24)
}

voiceView.setOnClickListener { viewModel.playVoice(voice) }
binding.root.setOnClickListener { viewModel.playVoice(voice) }
}
}

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): TtsViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.dialog_tts_voices_voice, parent, false)
return TtsViewHolder(v)
val layoutInflater = LayoutInflater.from(parent.context)
val binding = DialogTtsVoicesVoiceBinding.inflate(layoutInflater, parent, false)
return TtsViewHolder(binding)
}

override fun onBindViewHolder(
Expand Down
2 changes: 1 addition & 1 deletion AnkiDroid/src/main/res/layout/dialog_tts_voices.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
app:shapeAppearanceOverlay="?attr/shapeAppearanceCornerLarge">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/spoken_text"
android:id="@+id/spoken_text_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_action_mic"
Expand Down
8 changes: 4 additions & 4 deletions AnkiDroid/src/main/res/layout/dialog_tts_voices_voice.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
app:layout_constraintBottom_toBottomOf="parent"/>

<com.ichi2.ui.FixedTextView
android:id="@+id/mtrl_list_item_text"
android:id="@+id/text_view_bottom"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
Expand All @@ -79,14 +79,14 @@
android:textFontWeight="400"
app:layout_constraintEnd_toStartOf="@id/action_button"
app:layout_constraintStart_toEndOf="@id/local_or_network"
app:layout_constraintTop_toBottomOf="@+id/mtrl_list_item_secondary_text"
app:layout_constraintTop_toBottomOf="@+id/text_view_top"
tools:text="mtrl_list_item_text" />

<com.ichi2.ui.FixedTextView
android:id="@+id/mtrl_list_item_secondary_text"
android:id="@+id/text_view_top"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/mtrl_list_item_text"
android:layout_below="@id/text_view_bottom"
android:layout_gravity="center_vertical"
android:maxLines="2"
android:paddingEnd="16dp"
Expand Down