@@ -40,6 +40,7 @@ import androidx.compose.material.icons.filled.Repeat
4040import androidx.compose.material.icons.outlined.AutoFixHigh
4141import androidx.compose.material.icons.outlined.FormatQuote
4242import androidx.compose.material.icons.outlined.Group
43+ import androidx.compose.material.icons.outlined.Language
4344import androidx.compose.material.icons.outlined.Lock
4445import androidx.compose.material3.Button
4546import androidx.compose.material3.ButtonDefaults
@@ -79,6 +80,7 @@ import androidx.compose.ui.graphics.SolidColor
7980import androidx.compose.ui.layout.ContentScale
8081import androidx.compose.ui.layout.boundsInWindow
8182import androidx.compose.ui.layout.onGloballyPositioned
83+ import androidx.compose.ui.platform.LocalConfiguration
8284import androidx.compose.ui.platform.LocalDensity
8385import androidx.compose.ui.platform.LocalSoftwareKeyboardController
8486import androidx.compose.ui.res.stringResource
@@ -101,6 +103,7 @@ import pub.hackers.android.ui.theme.AppShapes
101103import pub.hackers.android.ui.theme.LocalAppColors
102104import pub.hackers.android.ui.theme.LocalAppTypography
103105import kotlin.math.roundToInt
106+ import java.util.Locale
104107
105108private data class MentionPopupPositionInputs (
106109 val scrollY : Int ,
@@ -145,6 +148,7 @@ fun ComposeScreen(
145148 val snackbarHostState = remember { SnackbarHostState () }
146149 var showVisibilityMenu by remember { mutableStateOf(false ) }
147150 var showQuotePolicyMenu by remember { mutableStateOf(false ) }
151+ var showLanguageMenu by remember { mutableStateOf(false ) }
148152 val quotePolicyLocked = uiState.visibility != PostVisibility .PUBLIC &&
149153 uiState.visibility != PostVisibility .UNLISTED
150154 val effectiveQuotePolicy = if (quotePolicyLocked) QuotePolicy .SELF else uiState.quotePolicy
@@ -254,7 +258,10 @@ fun ComposeScreen(
254258 attachment.altText.isNotBlank() &&
255259 attachment.error == null
256260 }
257- val postEnabled = uiState.content.isNotBlank() && ! uiState.isPosting && mediaReady
261+ val postEnabled = uiState.content.isNotBlank() &&
262+ uiState.language.isNotBlank() &&
263+ ! uiState.isPosting &&
264+ mediaReady
258265
259266 Scaffold (
260267 contentWindowInsets = WindowInsets (0 ),
@@ -572,6 +579,64 @@ fun ComposeScreen(
572579 horizontalArrangement = Arrangement .End ,
573580 verticalAlignment = Alignment .CenterVertically ,
574581 ) {
582+ val languageOptions = remember(uiState.language, uiState.suggestedLanguages) {
583+ (listOf (uiState.language) + uiState.suggestedLanguages)
584+ .map { it.trim() }
585+ .filter { it.isNotEmpty() }
586+ .distinct()
587+ }
588+ TextButton (
589+ onClick = { showLanguageMenu = true },
590+ enabled = ! uiState.isPosting && languageOptions.isNotEmpty(),
591+ contentPadding = PaddingValues (horizontal = 8 .dp, vertical = 4 .dp),
592+ ) {
593+ Icon (
594+ imageVector = Icons .Outlined .Language ,
595+ contentDescription = stringResource(R .string.compose_language_label),
596+ tint = colors.textSecondary,
597+ )
598+ Spacer (modifier = Modifier .width(4 .dp))
599+ Text (
600+ text = uiState.language.ifBlank {
601+ stringResource(R .string.compose_language_label)
602+ },
603+ color = colors.textSecondary,
604+ style = typography.labelMedium,
605+ maxLines = 1 ,
606+ )
607+ Icon (
608+ imageVector = Icons .Default .KeyboardArrowDown ,
609+ contentDescription = null ,
610+ tint = colors.textSecondary,
611+ modifier = Modifier .size(18 .dp),
612+ )
613+
614+ DropdownMenu (
615+ expanded = showLanguageMenu,
616+ onDismissRequest = { showLanguageMenu = false },
617+ ) {
618+ languageOptions.forEach { language ->
619+ DropdownMenuItem (
620+ text = { Text (languageOptionLabel(language)) },
621+ leadingIcon = {
622+ if (language == uiState.language) {
623+ Icon (
624+ imageVector = Icons .Default .Check ,
625+ contentDescription = null ,
626+ )
627+ }
628+ },
629+ onClick = {
630+ viewModel.updateLanguage(language)
631+ showLanguageMenu = false
632+ },
633+ )
634+ }
635+ }
636+ }
637+
638+ Spacer (modifier = Modifier .width(8 .dp))
639+
575640 Button (
576641 onClick = { viewModel.post() },
577642 enabled = postEnabled,
@@ -609,6 +674,29 @@ fun ComposeScreen(
609674 )
610675 }
611676 }
677+
678+ }
679+
680+ @Composable
681+ private fun languageOptionLabel (language : String ): String {
682+ val configuration = LocalConfiguration .current
683+ val uiLocale = remember(configuration) {
684+ if (configuration.locales.size() > 0 ) {
685+ configuration.locales[0 ]
686+ } else {
687+ Locale .getDefault()
688+ }
689+ }
690+ return remember(language, uiLocale) {
691+ val locale = Locale .forLanguageTag(language)
692+ val nativeName = locale.getDisplayLanguage(locale).ifBlank { language }
693+ val uiName = locale.getDisplayLanguage(uiLocale).ifBlank { language }
694+ if (nativeName.equals(uiName, ignoreCase = true )) {
695+ " $nativeName ($language )"
696+ } else {
697+ " $nativeName ($uiName , $language )"
698+ }
699+ }
612700}
613701
614702@Composable
0 commit comments