diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ee651dd6..c8001e0b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -39,8 +39,8 @@ android { applicationId = appId minSdk = 21 targetSdk = 34 - versionCode = 37 - versionName = "1.6.0" + versionCode = 38 + versionName = "1.6.1" vectorDrawables.useSupportLibrary = true ksp.arg("room.schemaLocation", "$projectDir/schemas") archivesName = "$applicationId-v$versionCode($versionName)" diff --git a/app/src/main/java/io/github/yamin8000/owl/data/model/Definition.kt b/app/src/main/java/io/github/yamin8000/owl/data/model/Definition.kt index 770a0521..42a6ef4b 100644 --- a/app/src/main/java/io/github/yamin8000/owl/data/model/Definition.kt +++ b/app/src/main/java/io/github/yamin8000/owl/data/model/Definition.kt @@ -21,15 +21,12 @@ package io.github.yamin8000.owl.data.model -import android.os.Parcelable -import androidx.compose.runtime.Immutable -import kotlinx.parcelize.Parcelize +import androidx.compose.runtime.Stable -@Immutable -@Parcelize +@Stable data class Definition( val definition: String, val example: String?, val synonyms: List, val antonyms: List -) : Parcelable +) \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/owl/data/model/Entry.kt b/app/src/main/java/io/github/yamin8000/owl/data/model/Entry.kt index 59205176..51596f29 100644 --- a/app/src/main/java/io/github/yamin8000/owl/data/model/Entry.kt +++ b/app/src/main/java/io/github/yamin8000/owl/data/model/Entry.kt @@ -21,14 +21,10 @@ package io.github.yamin8000.owl.data.model -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize data class Entry( val word: String, val phonetics: List, val meanings: List, val license: License, val sourceUrls: List -) : Parcelable +) diff --git a/app/src/main/java/io/github/yamin8000/owl/data/model/License.kt b/app/src/main/java/io/github/yamin8000/owl/data/model/License.kt index 5ebd44c5..b7205359 100644 --- a/app/src/main/java/io/github/yamin8000/owl/data/model/License.kt +++ b/app/src/main/java/io/github/yamin8000/owl/data/model/License.kt @@ -21,11 +21,7 @@ package io.github.yamin8000.owl.data.model -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize data class License( val name: String, val url: String -) : Parcelable +) diff --git a/app/src/main/java/io/github/yamin8000/owl/data/model/Meaning.kt b/app/src/main/java/io/github/yamin8000/owl/data/model/Meaning.kt index 467d7854..dcbe884d 100644 --- a/app/src/main/java/io/github/yamin8000/owl/data/model/Meaning.kt +++ b/app/src/main/java/io/github/yamin8000/owl/data/model/Meaning.kt @@ -21,13 +21,12 @@ package io.github.yamin8000.owl.data.model -import android.os.Parcelable -import kotlinx.parcelize.Parcelize +import androidx.compose.runtime.Stable -@Parcelize +@Stable data class Meaning( val partOfSpeech: String, val definitions: List, val synonyms: List, val antonyms: List -) : Parcelable +) \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/owl/data/model/Phonetic.kt b/app/src/main/java/io/github/yamin8000/owl/data/model/Phonetic.kt index 1327b542..76861c63 100644 --- a/app/src/main/java/io/github/yamin8000/owl/data/model/Phonetic.kt +++ b/app/src/main/java/io/github/yamin8000/owl/data/model/Phonetic.kt @@ -21,13 +21,9 @@ package io.github.yamin8000.owl.data.model -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize data class Phonetic( val text: String? = null, val audio: String? = null, val sourceUrl: String? = null, val license: License? = null -) : Parcelable +) diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/App.kt b/app/src/main/java/io/github/yamin8000/owl/ui/App.kt index 709279ab..07b5e364 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/App.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/App.kt @@ -23,6 +23,9 @@ package io.github.yamin8000.owl.ui import android.app.Application import android.content.Context +import android.speech.tts.TextToSpeech +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.compositionLocalOf import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStore @@ -31,4 +34,6 @@ internal val Context.settingsDataStore: DataStore by preferencesDat internal val Context.historyDataStore: DataStore by preferencesDataStore(name = "history") internal val Context.favouritesDataStore: DataStore by preferencesDataStore(name = "favourites") +internal val LocalTTS: ProvidableCompositionLocal = compositionLocalOf { null } + internal class App : Application() \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/composable/Composables.kt b/app/src/main/java/io/github/yamin8000/owl/ui/composable/Composables.kt index eca83c56..63124e0b 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/composable/Composables.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/composable/Composables.kt @@ -21,8 +21,6 @@ package io.github.yamin8000.owl.ui.composable -import android.content.Context -import android.speech.tts.TextToSpeech import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -54,7 +52,6 @@ import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -79,12 +76,10 @@ import com.airbnb.lottie.compose.rememberLottieComposition import io.github.yamin8000.owl.R import io.github.yamin8000.owl.util.Constants.DNS_SERVERS import io.github.yamin8000.owl.util.Constants.INTERNET_CHECK_DELAY -import io.github.yamin8000.owl.util.TTS import io.github.yamin8000.owl.util.findActivity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext -import java.util.Locale @OptIn(ExperimentalFoundationApi::class) @Composable @@ -193,7 +188,7 @@ fun ClickableIcon( onClick: () -> Unit ) { val haptic = LocalHapticFeedback.current - val clickWithFeedback = remember { + val clickWithFeedback = remember(onClick) { { haptic.performHapticFeedback(HapticFeedbackType.LongPress) onClick() @@ -212,18 +207,6 @@ fun ClickableIcon( ) } -@Composable -fun TtsAwareContent( - ttsLanguageLocaleTag: String = Locale.US.toLanguageTag(), - content: @Composable (TextToSpeech) -> Unit -) { - val context = LocalContext.current - val ttsHelper = remember { TTS(context, Locale.forLanguageTag(ttsLanguageLocaleTag)) } - val tts: MutableState = remember { mutableStateOf(null) } - LaunchedEffect(Unit) { tts.value = ttsHelper.getTts() } - if (tts.value != null) tts.value?.let { content(it) } -} - @Composable fun InternetAwareComposable( dnsServers: List = DNS_SERVERS, diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/composable/Texts.kt b/app/src/main/java/io/github/yamin8000/owl/ui/composable/Texts.kt index 97bcc935..6f031d29 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/composable/Texts.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/composable/Texts.kt @@ -81,6 +81,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import io.github.yamin8000.owl.R +import io.github.yamin8000.owl.ui.LocalTTS import io.github.yamin8000.owl.ui.theme.DefaultCutShape import io.github.yamin8000.owl.ui.theme.Samim import io.github.yamin8000.owl.ui.theme.defaultGradientBorder @@ -131,9 +132,9 @@ fun PersianText( style: TextStyle = LocalTextStyle.current ) { val context = LocalContext.current - var localStyle by remember { mutableStateOf(style) } - var localFontFamily by remember { mutableStateOf(fontFamily) } - val currentLocale = remember { getCurrentLocale(context) } + var localStyle by remember(style) { mutableStateOf(style) } + var localFontFamily by remember(fontFamily) { mutableStateOf(fontFamily) } + val currentLocale = remember(context) { getCurrentLocale(context) } if (currentLocale.language == Locale("fa").language) { localFontFamily = Samim localStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Rtl) @@ -230,7 +231,7 @@ fun CopyAbleRippleText( content = { val words = sanitizeWords(text.split(regex).toSet()).toList() items(words) { item -> - val onItemClick = remember(onDoubleClick, isDialogShown) { + val onItemClick = remember(onDoubleClick, item) { { onDoubleClick?.invoke(item) isDialogShown = false @@ -310,7 +311,6 @@ fun CopyAbleRippleTextWithIcon( fun SpeakableRippleTextWithIcon( text: String, imageVector: ImageVector, - localeTag: String, title: String? = null, content: @Composable (() -> Unit)? = null, onDoubleClick: ((String) -> Unit)? = null @@ -321,24 +321,21 @@ fun SpeakableRippleTextWithIcon( context.findActivity()?.getSystemService(Context.AUDIO_SERVICE) as AudioManager } - TtsAwareContent( - ttsLanguageLocaleTag = localeTag, - content = { - val onClick = remember(it, audio, content, increaseVolumeText) { - { - if (audio.getStreamVolume(AudioManager.STREAM_MUSIC) == 0) - Toast.makeText(context, increaseVolumeText, Toast.LENGTH_SHORT).show() - it.speak(text) - } - } - CopyAbleRippleTextWithIcon( - text = text, - content = content, - title = title, - imageVector = imageVector, - onDoubleClick = onDoubleClick, - onClick = onClick - ) + val tts = LocalTTS.current + val onClick = remember(tts, audio, text) { + { + if (audio.getStreamVolume(AudioManager.STREAM_MUSIC) == 0) + Toast.makeText(context, increaseVolumeText, Toast.LENGTH_SHORT).show() + tts?.speak(text) + Unit } + } + CopyAbleRippleTextWithIcon( + text = text, + content = content, + title = title, + imageVector = imageVector, + onDoubleClick = onDoubleClick, + onClick = onClick ) } \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/content/MainActivity.kt b/app/src/main/java/io/github/yamin8000/owl/ui/content/MainActivity.kt index 35a3763c..973e1b63 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/content/MainActivity.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/content/MainActivity.kt @@ -25,12 +25,16 @@ import android.annotation.SuppressLint import android.content.Intent import android.os.Build import android.os.Bundle +import android.speech.tts.TextToSpeech import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -45,11 +49,13 @@ import androidx.navigation.compose.rememberNavController import androidx.room.Room import io.github.yamin8000.owl.data.DataStoreRepository import io.github.yamin8000.owl.data.db.AppDatabase +import io.github.yamin8000.owl.ui.LocalTTS import io.github.yamin8000.owl.ui.content.favourites.FavouritesContent import io.github.yamin8000.owl.ui.content.favourites.FavouritesViewModel import io.github.yamin8000.owl.ui.content.history.HistoryContent import io.github.yamin8000.owl.ui.content.history.HistoryViewModel import io.github.yamin8000.owl.ui.content.home.HomeContent +import io.github.yamin8000.owl.ui.content.home.HomeTopBarItem import io.github.yamin8000.owl.ui.content.settings.SettingsContent import io.github.yamin8000.owl.ui.content.settings.SettingsViewModel import io.github.yamin8000.owl.ui.content.settings.ThemeSetting @@ -59,9 +65,11 @@ import io.github.yamin8000.owl.ui.navigation.Nav import io.github.yamin8000.owl.ui.settingsDataStore import io.github.yamin8000.owl.ui.theme.OwlTheme import io.github.yamin8000.owl.util.Constants +import io.github.yamin8000.owl.util.TTS import io.github.yamin8000.owl.util.log import io.github.yamin8000.owl.util.viewModelFactory import kotlinx.coroutines.runBlocking +import java.util.Locale internal class MainActivity : ComponentActivity() { @@ -173,68 +181,93 @@ internal class MainActivity : ComponentActivity() { } }) - val start = "${Nav.Routes.Home}/{${Nav.Arguments.Search}}" - val navController = rememberNavController() - NavHost( - navController = navController, - startDestination = start, - builder = { - composable(start) { - var searchTerm = it.arguments?.getString(Nav.Arguments.Search.toString()) - if (searchTerm == null && outsideInput != null) - searchTerm = outsideInput.toString() - HomeContent( - searchTerm = searchTerm, - onTopBarClick = { item -> navController.navigate(item.route()) }, - ttsLang = settingsViewModel.ttsLang.collectAsState().value, - isVibrating = settingsViewModel.isVibrating.collectAsState().value, - isStartingBlank = settingsViewModel.isStartingBlank.collectAsState().value, - onAddToHistory = historyVM::add, - onAddToFavourite = favouritesVM::add - ) - } + val ttsTag = settingsViewModel.ttsLang.collectAsState().value + val ttsHelper = remember { TTS(context, Locale.forLanguageTag(ttsTag)) } + val tts: MutableState = remember { mutableStateOf(null) } + LaunchedEffect(Unit) { tts.value = ttsHelper.getTts() } - val onBackClick: () -> Unit = { navController.popBackStack() } + CompositionLocalProvider(LocalTTS provides tts.value) { + val start = "${Nav.Routes.Home}/{${Nav.Arguments.Search}}" + val navController = rememberNavController() + val onBackClick: () -> Unit = remember { { navController.popBackStack() } } + NavHost( + navController = navController, + startDestination = start, + builder = { + composable(start) { + var searchTerm = it.arguments?.getString(Nav.Arguments.Search.toString()) + if (searchTerm == null && outsideInput != null) + searchTerm = outsideInput.toString() + val addToHistory: (String) -> Unit = remember { + { item -> historyVM.add(item) } + } + val addToFavourite: (String) -> Unit = remember { + { item -> favouritesVM.add(item) } + } + val onTopBarClick: (HomeTopBarItem) -> Unit = remember { + { item -> navController.navigate(item.route()) } + } + HomeContent( + searchTerm = searchTerm, + isStartingBlank = settingsViewModel.isStartingBlank.collectAsState().value, + isVibrating = settingsViewModel.isVibrating.collectAsState().value, + onTopBarClick = onTopBarClick, + onAddToHistory = addToHistory, + onAddToFavourite = addToFavourite + ) + } - composable(Nav.Routes.About.toString()) { - AboutContent(onBackClick) - } + composable(Nav.Routes.About.toString()) { + AboutContent(onBackClick) + } - composable(Nav.Routes.Favourites.toString()) { - FavouritesContent( - onFavouritesItemClick = { favourite -> navController.navigate("${Nav.Routes.Home}/${favourite}") }, - onBackClick = onBackClick, - favourites = favouritesVM.favourites.collectAsState().value.toList(), - onRemoveAll = favouritesVM::removeAll, - onRemove = favouritesVM::remove - ) - } + composable(Nav.Routes.Favourites.toString()) { + FavouritesContent( + onFavouritesItemClick = { favourite -> navController.navigate("${Nav.Routes.Home}/${favourite}") }, + onBackClick = onBackClick, + favourites = favouritesVM.favourites.collectAsState().value.toList(), + onRemoveAll = favouritesVM::removeAll, + onRemove = favouritesVM::remove + ) + } - composable(Nav.Routes.History.toString()) { - HistoryContent( - onHistoryItemClick = { history -> navController.navigate("${Nav.Routes.Home}/${history}") }, - onBackClick = onBackClick, - history = historyVM.history.collectAsState().value.toList(), - onRemoveAll = historyVM::removeAll, - onRemove = historyVM::remove - ) - } + composable(Nav.Routes.History.toString()) { + val onHistoryItemClick: (String) -> Unit = remember { + { history -> navController.navigate("${Nav.Routes.Home}/${history}") } + } + HistoryContent( + onHistoryItemClick = onHistoryItemClick, + onBackClick = onBackClick, + history = historyVM.history.collectAsState().value.toList(), + onRemoveAll = historyVM::removeAll, + onRemove = historyVM::remove + ) + } - composable(Nav.Routes.Settings.toString()) { - SettingsContent( - isVibrating = settingsViewModel.isVibrating.collectAsState().value, - onVibratingChange = settingsViewModel::updateVibrationSetting, - isStartingBlank = settingsViewModel.isStartingBlank.collectAsState().value, - onStartingBlankChange = settingsViewModel::updateStartingBlank, - themeSetting = settingsViewModel.themeSetting.collectAsState().value, - onSystemThemeChange = onThemeChanged, - onThemeSettingChange = settingsViewModel::updateThemeSetting, - ttsTag = settingsViewModel.ttsLang.collectAsState().value, - onTtsTagChange = settingsViewModel::updateTtsLang, - onBackClick = onBackClick - ) + composable(Nav.Routes.Settings.toString()) { + SettingsContent( + isVibrating = settingsViewModel.isVibrating.collectAsState().value, + onVibratingChange = remember { + { settingsViewModel.updateVibrationSetting(it) } + }, + isStartingBlank = settingsViewModel.isStartingBlank.collectAsState().value, + onStartingBlankChange = remember { + { settingsViewModel.updateStartingBlank(it) } + }, + themeSetting = settingsViewModel.themeSetting.collectAsState().value, + onSystemThemeChange = onThemeChanged, + onThemeSettingChange = remember { + { settingsViewModel.updateThemeSetting(it) } + }, + ttsTag = settingsViewModel.ttsLang.collectAsState().value, + onTtsTagChange = remember { + { settingsViewModel.updateTtsLang(it) } + }, + onBackClick = onBackClick + ) + } } - } - ) + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/content/home/Home.kt b/app/src/main/java/io/github/yamin8000/owl/ui/content/home/Home.kt index 826d1268..e43d71aa 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/content/home/Home.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/content/home/Home.kt @@ -72,7 +72,6 @@ internal fun HomeContent( searchTerm: String?, isStartingBlank: Boolean, isVibrating: Boolean, - ttsLang: String, onTopBarClick: (HomeTopBarItem) -> Unit, onAddToHistory: (String) -> Unit, onAddToFavourite: (String) -> Unit @@ -137,8 +136,6 @@ internal fun HomeContent( } ) - val locale = remember(ttsLang) { vm.getLocale(ttsLang) } - if (vm.isSharing.collectAsState().value) { val temp = vm.searchResult.collectAsState().value.firstOrNull() if (temp != null) { @@ -231,25 +228,30 @@ internal fun HomeContent( ?.firstOrNull { it.text != null } ?.text ?: "" + val onWordChipClick: (String) -> Unit = remember { + { + vm.updateSearchTerm(it) + vm.ioScope.launch { vm.searchForDefinition(it) } + } + } WordDefinitionsList( word = word, - localeTag = locale.toLanguageTag(), listState = listState, meanings = searchResult.value.first().meanings.toPersistentList(), - onWordChipClick = { - vm.updateSearchTerm(it) - vm.ioScope.launch { vm.searchForDefinition(it) } - }, + onWordChipClick = onWordChipClick, wordCard = { WordCard( - localeTag = locale.toLanguageTag(), word = word, pronunciation = phonetic, - onShareWord = vm::startWordSharing, - onAddToFavourite = { - vm.ioScope.launch { - onAddToFavourite(word) - snackbarHostState.showSnackbar(addedToFavourites) + onShareWord = remember { { vm.startWordSharing() } }, + onAddToFavourite = remember(searchResult.value) { + { + vm.ioScope.launch { + onAddToFavourite(word) + snackbarHostState.showSnackbar( + addedToFavourites + ) + } } } ) diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeComposables.kt b/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeComposables.kt index 6a8f22f4..f59234f4 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeComposables.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeComposables.kt @@ -27,6 +27,7 @@ import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -50,11 +51,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.github.yamin8000.owl.R import io.github.yamin8000.owl.data.model.Meaning +import io.github.yamin8000.owl.ui.LocalTTS import io.github.yamin8000.owl.ui.composable.ClickableIcon import io.github.yamin8000.owl.ui.composable.CopyAbleRippleTextWithIcon import io.github.yamin8000.owl.ui.composable.HighlightText import io.github.yamin8000.owl.ui.composable.SpeakableRippleTextWithIcon -import io.github.yamin8000.owl.ui.composable.TtsAwareContent import io.github.yamin8000.owl.ui.theme.DefaultCutShape import io.github.yamin8000.owl.ui.theme.defaultGradientBorder import io.github.yamin8000.owl.util.speak @@ -63,11 +64,10 @@ import kotlinx.collections.immutable.PersistentList @Composable internal fun WordDefinitionsList( word: String, - localeTag: String, listState: ScrollState, meanings: PersistentList, onWordChipClick: (String) -> Unit, - wordCard: @Composable () -> Unit + wordCard: @Composable ColumnScope.() -> Unit ) { Column( verticalArrangement = Arrangement.spacedBy(16.dp), @@ -80,7 +80,6 @@ internal fun WordDefinitionsList( meanings.forEach { meaning -> MeaningCard( word = word, - localeTag = localeTag, meaning = meaning, onWordChipClick = onWordChipClick ) @@ -92,7 +91,6 @@ internal fun WordDefinitionsList( @Composable internal fun WordCard( modifier: Modifier = Modifier, - localeTag: String, word: String, pronunciation: String?, onAddToFavourite: () -> Unit, @@ -118,10 +116,9 @@ internal fun WordCard( verticalArrangement = Arrangement.spacedBy(4.dp), horizontalAlignment = Alignment.Start, content = { - WordText(word, localeTag) + WordText(word) if (pronunciation != null) { PronunciationText( - localeTag = localeTag, pronunciation = pronunciation, word = word ) @@ -155,31 +152,30 @@ internal fun WordCard( @Composable internal fun WordText( - word: String, - localeTag: String + word: String ) { SpeakableRippleTextWithIcon( text = word, - imageVector = Icons.AutoMirrored.TwoTone.ShortText, - localeTag = localeTag + imageVector = Icons.AutoMirrored.TwoTone.ShortText ) } @Composable internal fun PronunciationText( - localeTag: String, pronunciation: String, word: String ) { - TtsAwareContent( - ttsLanguageLocaleTag = localeTag, - content = { ttsEngine -> - CopyAbleRippleTextWithIcon( - text = pronunciation, - imageVector = Icons.TwoTone.RecordVoiceOver, - onClick = { ttsEngine.speak(word) } - ) + val tts = LocalTTS.current + val onClick = remember(tts) { + { + tts?.speak(word) + Unit } + } + CopyAbleRippleTextWithIcon( + text = pronunciation, + imageVector = Icons.TwoTone.RecordVoiceOver, + onClick = onClick ) } @@ -187,15 +183,13 @@ internal fun PronunciationText( internal fun WordExampleText( word: String, example: String, - localeTag: String, onDoubleClick: ((String) -> Unit)? = null ) { SpeakableRippleTextWithIcon( - content = { HighlightText(fullText = example, highlightedText = word) }, text = example, - title = stringResource(R.string.example), imageVector = Icons.AutoMirrored.TwoTone.TextSnippet, - localeTag = localeTag, + title = stringResource(R.string.example), + content = { HighlightText(fullText = example, highlightedText = word) }, onDoubleClick = onDoubleClick ) } @@ -204,15 +198,13 @@ internal fun WordExampleText( internal fun WordDefinitionText( word: String, definition: String, - localeTag: String, onDoubleClick: ((String) -> Unit)? = null ) { SpeakableRippleTextWithIcon( - content = { HighlightText(fullText = definition, highlightedText = word) }, text = definition, - title = stringResource(R.string.definition), imageVector = Icons.AutoMirrored.TwoTone.ShortText, - localeTag = localeTag, + title = stringResource(R.string.definition), + content = { HighlightText(fullText = definition, highlightedText = word) }, onDoubleClick = onDoubleClick ) } @@ -220,14 +212,12 @@ internal fun WordDefinitionText( @Composable internal fun WordTypeText( type: String, - localeTag: String, onDoubleClick: ((String) -> Unit)? = null ) { SpeakableRippleTextWithIcon( text = type, - title = stringResource(R.string.type), imageVector = Icons.TwoTone.Category, - localeTag = localeTag, + title = stringResource(R.string.type), onDoubleClick = onDoubleClick ) } @@ -235,7 +225,6 @@ internal fun WordTypeText( @Composable internal fun MeaningCard( word: String, - localeTag: String, meaning: Meaning, onWordChipClick: (String) -> Unit ) { @@ -250,38 +239,33 @@ internal fun MeaningCard( content = { WordTypeText( meaning.partOfSpeech, - localeTag, onDoubleClick = onWordChipClick ) meaning.definitions.forEach { definition -> WordDefinitionText( word, definition.definition, - localeTag, onDoubleClick = onWordChipClick ) if (definition.example != null) { WordExampleText( word = word, example = definition.example, - localeTag = localeTag, onDoubleClick = onWordChipClick ) } definition.antonyms.forEach { antonym -> SpeakableRippleTextWithIcon( text = antonym, - title = stringResource(R.string.antonym), imageVector = Icons.AutoMirrored.TwoTone.TextSnippet, - localeTag = localeTag + title = stringResource(R.string.antonym) ) } definition.synonyms.forEach { synonym -> SpeakableRippleTextWithIcon( text = synonym, - title = stringResource(R.string.synonym), imageVector = Icons.AutoMirrored.TwoTone.TextSnippet, - localeTag = localeTag + title = stringResource(R.string.synonym) ) } } diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeViewModel.kt b/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeViewModel.kt index e22892bd..74f58bab 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeViewModel.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/content/home/HomeViewModel.kt @@ -50,7 +50,6 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import retrofit2.HttpException -import java.util.Locale import kotlin.coroutines.cancellation.CancellationException internal class HomeViewModel( @@ -311,7 +310,4 @@ internal class HomeViewModel( _isSearching.value = false job?.cancel() } - - fun getLocale(tts: String): Locale = - if (tts.isEmpty()) Locale.US else Locale.forLanguageTag(tts) } \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Composables.kt b/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Composables.kt index 82f889c0..475e6858 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Composables.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Composables.kt @@ -113,7 +113,7 @@ internal fun SwitchWithText( onCheckedChange: (Boolean) -> Unit ) { val hapticFeedback = LocalHapticFeedback.current - val onClick = remember(checked) { + val onClick = remember(checked, onCheckedChange) { { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) onCheckedChange(!checked) diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Settings.kt b/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Settings.kt index e42dec65..dcb7d57e 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Settings.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/Settings.kt @@ -113,45 +113,44 @@ private fun TtsLanguageSetting( currentTtsTag: String, onTtsTagChange: (String) -> Unit ) { - val context = LocalContext.current - var englishLanguages by remember { mutableStateOf(listOf()) } - - LaunchedEffect(currentTtsTag) { - englishLanguages = TTS(context, Locale.forLanguageTag(currentTtsTag)) - .getTts() - ?.availableLanguages - ?.filter { it.language == Locale.ENGLISH.language } - ?: listOf() - } - - - var isDialogShown by remember { mutableStateOf(false) } - val showDialog = remember { { isDialogShown = true } } - val hideDialog = remember { { isDialogShown = false } } - val onLanguageSelect: (String) -> Unit = remember { - { - onTtsTagChange(it) - isDialogShown = false - } - } - - if (isDialogShown) { - TtsLanguagesDialog( - currentTtsTag = currentTtsTag, - languages = englishLanguages, - onDismiss = hideDialog, - onLanguageSelect = onLanguageSelect - ) - } - SettingsItemCard( title = stringResource(R.string.tts_language), content = { + var isDialogShown by remember { mutableStateOf(false) } + val showDialog = remember { { isDialogShown = true } } SettingsItem( onClick = showDialog, content = { Icon(imageVector = Icons.TwoTone.Language, contentDescription = null) PersianText(Locale.forLanguageTag(currentTtsTag).displayName) + + val context = LocalContext.current + var englishLanguages by remember { mutableStateOf(listOf()) } + + LaunchedEffect(currentTtsTag) { + englishLanguages = TTS(context, Locale.forLanguageTag(currentTtsTag)) + .getTts() + ?.availableLanguages + ?.filter { it.language == Locale.ENGLISH.language } + ?: listOf() + } + + val hideDialog = remember { { isDialogShown = false } } + val onLanguageSelect: (String) -> Unit = remember { + { + onTtsTagChange(it) + isDialogShown = false + } + } + + if (isDialogShown) { + TtsLanguagesDialog( + currentTtsTag = currentTtsTag, + languages = englishLanguages, + onDismiss = hideDialog, + onLanguageSelect = onLanguageSelect + ) + } } ) } diff --git a/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/SettingsViewModel.kt b/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/SettingsViewModel.kt index b991a24a..268f54bb 100644 --- a/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/SettingsViewModel.kt +++ b/app/src/main/java/io/github/yamin8000/owl/ui/content/settings/SettingsViewModel.kt @@ -60,29 +60,37 @@ internal class SettingsViewModel( fun updateTtsLang( newTtsLang: String - ) = scope.launch { - _ttsLang.value = newTtsLang - settings.setString(Constants.TTS_LANG, newTtsLang) + ) { + scope.launch { + _ttsLang.value = newTtsLang + settings.setString(Constants.TTS_LANG, newTtsLang) + } } fun updateThemeSetting( newTheme: ThemeSetting - ) = scope.launch { - _themeSetting.value = newTheme - settings.setString(Constants.THEME, newTheme.name) + ) { + scope.launch { + _themeSetting.value = newTheme + settings.setString(Constants.THEME, newTheme.name) + } } fun updateVibrationSetting( newVibrationSetting: Boolean - ) = scope.launch { - _isVibrating.value = newVibrationSetting - settings.setBool(Constants.IS_VIBRATING, newVibrationSetting) + ) { + scope.launch { + _isVibrating.value = newVibrationSetting + settings.setBool(Constants.IS_VIBRATING, newVibrationSetting) + } } fun updateStartingBlank( isStartingWithBlank: Boolean - ) = scope.launch { - _isStartingBlank.value = isStartingWithBlank - settings.setBool(Constants.IS_STARTING_BLANK, isStartingWithBlank) + ) { + scope.launch { + _isStartingBlank.value = isStartingWithBlank + settings.setBool(Constants.IS_STARTING_BLANK, isStartingWithBlank) + } } } \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/38.txt b/fastlane/metadata/android/en-US/changelogs/38.txt new file mode 100644 index 00000000..7e714c79 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/38.txt @@ -0,0 +1,2 @@ +- Fixed some bugs +- Fixed some performance issues related to Compose \ No newline at end of file