Add language dropdown to compose view (#2651)

* Add UI for selecting post language

* Apply selected language when sending status

* Save/restore post language with drafts

* Fall back to english if the configured language isn't found in the locale list (no-NB)

* Remove comment about no_NB

* Move language dropdown to top of compose view

* Preserve language when redrafting

* Set default language to target post's language when replying

* Add Tusky license header to new source file

* Tweak language dropdown button width
This commit is contained in:
Levi Bard 2022-08-31 18:53:57 +02:00 committed by GitHub
commit 0041acf2d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1140 additions and 28 deletions

View file

@ -35,6 +35,7 @@ import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.PopupMenu
@ -65,6 +66,7 @@ import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.BuildConfig
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.adapter.EmojiAdapter
import com.keylesspalace.tusky.adapter.LocaleAdapter
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog
import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog
@ -244,6 +246,7 @@ class ComposeActivity :
binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt)
}
setupLanguageSpinner(getInitialLanguage(composeOptions?.language))
setupComposeField(preferences, viewModel.startingText)
setupContentWarningField(composeOptions?.contentWarning)
setupPollView()
@ -476,6 +479,40 @@ class ComposeActivity :
binding.addPollTextActionTextView.setOnClickListener { openPollDialog() }
}
private fun setupLanguageSpinner(initialLanguage: String?) {
val locales = Locale.getAvailableLocales()
.filter { it.country.isNullOrEmpty() && it.script.isNullOrEmpty() && it.variant.isNullOrEmpty() } // Only "base" languages, "en" but not "en_DK"
var currentLocaleIndex = locales.indexOfFirst { it.language == initialLanguage }
if (currentLocaleIndex < 0) {
Log.e(TAG, "Error looking up language tag '$initialLanguage', falling back to english")
currentLocaleIndex = locales.indexOfFirst { it.language == "en" }
}
val context = this
binding.composePostLanguageButton.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
viewModel.postLanguage = (parent.adapter.getItem(position) as Locale).language
}
override fun onNothingSelected(parent: AdapterView<*>) {
parent.setSelection(locales.indexOfFirst { it.language == getInitialLanguage() })
}
}
binding.composePostLanguageButton.apply {
adapter = LocaleAdapter(context, android.R.layout.simple_spinner_dropdown_item, locales)
setSelection(currentLocaleIndex)
}
}
private fun getInitialLanguage(language: String? = null): String {
return if (language.isNullOrEmpty()) {
// Setting the application ui preference sets the default locale
Locale.getDefault().language
} else {
language
}
}
private fun setupActionBar() {
setSupportActionBar(binding.toolbar)
supportActionBar?.run {
@ -793,6 +830,10 @@ class ComposeActivity :
return length
}
@VisibleForTesting
val selectedLanguage: String?
get() = viewModel.postLanguage
private fun updateVisibleCharactersLeft() {
val remainingLength = maximumTootCharacters - calculateTextLength()
binding.composeCharactersLeftView.text = String.format(Locale.getDefault(), "%d", remainingLength)
@ -1128,7 +1169,8 @@ class ComposeActivity :
var scheduledAt: String? = null,
var sensitive: Boolean? = null,
var poll: NewPoll? = null,
var modifiedInitialState: Boolean? = null
var modifiedInitialState: Boolean? = null,
var language: String? = null,
) : Parcelable
companion object {

View file

@ -68,6 +68,7 @@ class ComposeViewModel @Inject constructor(
private var replyingStatusAuthor: String? = null
private var replyingStatusContent: String? = null
internal var startingText: String? = null
internal var postLanguage: String? = null
private var draftId: Int = 0
private var scheduledTootId: String? = null
private var startingContentWarning: String = ""
@ -261,7 +262,8 @@ class ComposeViewModel @Inject constructor(
mediaDescriptions = mediaDescriptions,
poll = poll.value,
failedToSend = false,
scheduledAt = scheduledAt.value
scheduledAt = scheduledAt.value,
language = postLanguage,
)
}
@ -308,7 +310,8 @@ class ComposeViewModel @Inject constructor(
draftId = draftId,
idempotencyKey = randomAlphanumericString(16),
retries = 0,
mediaProcessed = mediaProcessed
mediaProcessed = mediaProcessed,
language = postLanguage,
)
serviceClient.sendToot(tootToSend)
@ -426,6 +429,7 @@ class ComposeViewModel @Inject constructor(
draftId = composeOptions?.draftId ?: 0
scheduledTootId = composeOptions?.scheduledTootId
startingText = composeOptions?.content
postLanguage = composeOptions?.language
val tootVisibility = composeOptions?.visibility ?: Status.Visibility.UNKNOWN
if (tootVisibility.num != Status.Visibility.UNKNOWN.num) {