update in-app language picker (#538)

This commit is contained in:
Andre K 2022-12-10 11:03:14 -03:00 committed by GitHub
parent 8dca9ea8b6
commit c9a81c72e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 141 additions and 183 deletions

View file

@ -1,49 +0,0 @@
package com.geeksville.mesh
import android.content.Context
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.model.UIViewModel
import java.util.*
open class BaseActivity: AppCompatActivity(), Logging {
override fun attachBaseContext(newBase: Context) {
val res = newBase.resources
val config = res.configuration
// get chosen language from preference
val prefs = UIViewModel.getPreferences(newBase)
val langCode: String = prefs.getString("lang","zz") ?: ""
debug("langCode is $langCode")
if (Build.VERSION.SDK_INT >= 17) {
val locale = if (langCode == "zz")
Locale.getDefault()
else
createLocale(langCode)
config.setLocale(locale)
if(Build.VERSION.SDK_INT > 24) {
//Using createNewConfigurationContext will cause CompanionDeviceManager to crash
applyOverrideConfiguration(config)
super.attachBaseContext(newBase)
}else {
super.attachBaseContext(newBase.createConfigurationContext(config))
}
} else {
super.attachBaseContext(newBase)
}
}
private fun createLocale(language: String): Locale {
val langArray = language.split("_")
return if (langArray.size == 2) {
Locale(langArray[0], langArray[1])
} else {
Locale(langArray[0])
}
}
}

View file

@ -20,6 +20,7 @@ import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
@ -42,6 +43,7 @@ import com.geeksville.mesh.repository.radio.SerialInterface
import com.geeksville.mesh.service.*
import com.geeksville.mesh.ui.*
import com.geeksville.mesh.util.Exceptions
import com.geeksville.mesh.util.LanguageUtils
import com.geeksville.mesh.util.exceptionReporter
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
@ -106,7 +108,7 @@ eventually:
*/
@AndroidEntryPoint
class MainActivity : BaseActivity(), Logging {
class MainActivity : AppCompatActivity(), Logging {
private lateinit var binding: ActivityMainBinding
@ -200,6 +202,11 @@ class MainActivity : BaseActivity(), Logging {
if (!prefs.getBoolean("app_intro_completed", false)) {
startActivity(Intent(this, AppIntroduction::class.java))
}
// First run: migrate in-app language prefs to appcompat
if (prefs.getString("lang", LanguageUtils.SYSTEM_DEFAULT) != LanguageUtils.SYSTEM_MANAGED) {
LanguageUtils.migrateLanguagePrefs(prefs)
}
info("in-app language is ${LanguageUtils.getLocale()}")
binding = ActivityMainBinding.inflate(layoutInflater)
@ -843,7 +850,6 @@ class MainActivity : BaseActivity(), Logging {
editor.putInt("theme", 0)
editor.apply()
delegate.applyDayNight()
dialog.dismiss()
}
1 -> {
@ -851,15 +857,13 @@ class MainActivity : BaseActivity(), Logging {
editor.putInt("theme", 1)
editor.apply()
delegate.applyDayNight()
dialog.dismiss()
}
2 -> {
else -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
editor.putInt("theme", 2)
editor.apply()
delegate.applyDayNight()
dialog.dismiss()
}
@ -875,34 +879,23 @@ class MainActivity : BaseActivity(), Logging {
/// If nothing is found set FOLLOW SYSTEM option
when (prefs.getInt("theme", 2)) {
0 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
delegate.applyDayNight()
}
1 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
delegate.applyDayNight()
}
2 -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
delegate.applyDayNight()
}
0 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
1 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
else -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
}
private fun chooseLangDialog() {
/// Prepare dialog and its items
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(getString(R.string.preferences_language))
val languageLabels by lazy { resources.getStringArray(R.array.language_entries) }
val languageValues by lazy { resources.getStringArray(R.array.language_values) }
val languageTags = LanguageUtils.getLanguageTags(this)
val languageLabels = languageTags.map { it.first }.toTypedArray()
val languageValues = languageTags.map { it.second }
/// Load preferences and its value
val prefs = UIViewModel.getPreferences(this)
val editor: SharedPreferences.Editor = prefs.edit()
val lang = prefs.getString("lang", "zz")
val lang = LanguageUtils.getLocale()
debug("Lang from prefs: $lang")
builder.setSingleChoiceItems(
@ -911,8 +904,7 @@ class MainActivity : BaseActivity(), Logging {
) { dialog, which ->
val selectedLang = languageValues[which]
debug("Set lang pref to $selectedLang")
editor.putString("lang", selectedLang)
editor.apply()
LanguageUtils.setLocale(selectedLang)
dialog.dismiss()
}
val dialog = builder.create()

View file

@ -1,6 +0,0 @@
package com.geeksville.mesh.ui
import com.geeksville.mesh.android.Logging
object UILog : Logging

View file

@ -0,0 +1,66 @@
package com.geeksville.mesh.util
import android.content.Context
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.edit
import androidx.core.os.LocaleListCompat
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.R
import org.xmlpull.v1.XmlPullParser
import java.util.Locale
object LanguageUtils : Logging {
const val SYSTEM_DEFAULT = "zz"
const val SYSTEM_MANAGED = "appcompat"
fun getLocale(): String {
return AppCompatDelegate.getApplicationLocales().toLanguageTags().ifEmpty { SYSTEM_DEFAULT }
}
fun setLocale(lang: String) {
AppCompatDelegate.setApplicationLocales(
if (lang == SYSTEM_DEFAULT) LocaleListCompat.getEmptyLocaleList()
else LocaleListCompat.forLanguageTags(lang)
)
}
fun migrateLanguagePrefs(prefs: SharedPreferences) {
val currentLang = prefs.getString("lang", SYSTEM_DEFAULT) ?: SYSTEM_DEFAULT
debug("Migrating in-app language prefs: $currentLang")
prefs.edit { putString("lang", SYSTEM_MANAGED) }
setLocale(currentLang)
}
/**
* Build a list from locales_config.xml
* of native language names paired to its Locale tag (ex: "English", "en")
*/
fun getLanguageTags(context: Context): List<Pair<String, String>> {
val languageTags = mutableListOf(SYSTEM_DEFAULT)
try {
context.resources.getXml(R.xml.locales_config).use {
while (it.eventType != XmlPullParser.END_DOCUMENT) {
if (it.eventType == XmlPullParser.START_TAG && it.name == "locale") {
languageTags += it.getAttributeValue(0)
}
it.next()
}
}
} catch (e: Exception) {
errormsg("Error parsing locale_config.xml ${e.message}")
}
fun getDisplayLanguage(tag: String): String {
val loc = Locale(tag)
return when (tag) {
SYSTEM_DEFAULT -> context.getString(R.string.preferences_system_default)
"fr-HT" -> context.getString(R.string.fr_HT)
"pt-BR" -> context.getString(R.string.pt_BR)
else -> loc.getDisplayLanguage(loc)
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(loc) else it.toString() }
}
}
return languageTags.map { getDisplayLanguage(it) to it }
}
}