update in-app language picker (#538)

pull/547/head
Andre K 2022-12-10 11:03:14 -03:00 zatwierdzone przez GitHub
rodzic 8dca9ea8b6
commit c9a81c72e0
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
16 zmienionych plików z 141 dodań i 183 usunięć

Wyświetl plik

@ -37,7 +37,7 @@ android {
storePassword keystoreProperties['storePassword']
}
}
compileSdkVersion 32
compileSdkVersion 33
defaultConfig {
applicationId "com.geeksville.mesh"
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
@ -65,7 +65,7 @@ android {
defaultConfig {
// We have to list all translated languages here, because some of our libs have bogus languages that google play
// doesn't like and we need to strip them (gr)
resConfigs "cs", "de", "el", "en", "es", "fi", "fr", "ga", "ht", "it", "ja", "ko-rKR", "nl", "no", "pl", "pt", "pt-rBR", "ro", "ru", "sk", "sl", "sq", "sv", "tr", "zh"
resConfigs "cs", "de", "el", "en", "es", "fi", "fr", "fr-rHT", "ga", "hu", "it", "ja", "ko", "nl", "nb", "pl", "pt", "pt-rBR", "ro", "ru", "sk", "sl", "sq", "sv", "tr", "zh", "uk"
ndk {
// abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
@ -125,7 +125,11 @@ protobuf {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.5.0'
def appcompat_version = '1.6.0-rc01'
implementation "androidx.appcompat:appcompat:$appcompat_version"
// For loading and tinting drawables on older versions of the platform
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.fragment:fragment-ktx:1.5.2'
implementation 'androidx.cardview:cardview:1.0.0'

Wyświetl plik

@ -81,11 +81,8 @@
android:roundIcon="@mipmap/ic_launcher2_round"
android:supportsRtl="true"
android:hardwareAccelerated="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.mixpanel.android.MPConfig.DisableViewCrawler"
android:value="true" />
android:theme="@style/AppTheme"
android:localeConfig="@xml/locales_config">
<!-- Default crash collection and analytics off until we (possibly) turn it on in application.onCreate -->
<meta-data
@ -112,6 +109,15 @@
</intent-filter>
</service>
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
<!-- zxing for QR Code scanning: lock portrait orientation -->
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"

Wyświetl plik

@ -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])
}
}
}

Wyświetl plik

@ -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()

Wyświetl plik

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

Wyświetl plik

@ -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 }
}
}

Wyświetl plik

@ -68,7 +68,7 @@
<string name="modem_config_slow_short">단거리 (저속)</string>
<string name="modem_config_slow_medium">중거리 (저속)</string>
<string name="modem_config_slow_long">장거리 (저속)</string>
<string name="preferences_language">언어 (restart needed)</string>
<string name="preferences_language">언어</string>
<string name="preferences_system_default">시스템 기본값</string>
<string name="debug_panel">디버그 패널</string>
<string name="debug_last_messages">500 last messages</string>

Wyświetl plik

@ -126,7 +126,7 @@
<string name="why_background_required">W przypadku tej funkcji musisz przyznać opcję uprawnień lokalizacji „Zezwalaj przez cały czas”.</string>
<string name="cancel_no_radio">Anuluj (brak dostępu do radia)</string>
<string name="allow_will_show">Zezwól (pokaże okno dialogowe)</string>
<string name="preferences_language">Język (wymagany restart)</string>
<string name="preferences_language">Język</string>
<string name="preferences_system_default">Domyślny systemu</string>
<string name="preferences_map_style">Typ map</string>
<string name="resend">Ponów</string>

Wyświetl plik

@ -8,7 +8,7 @@
<string name="unset">Não definido</string>
<string name="connection_status">Status da conexão</string>
<string name="application_icon">icone da aplicação</string>
<string name="unknown_username">Nome de usuário desconhecido</string>
<string name="unknown_username">Nome desconhecido</string>
<string name="user_avatar">Avatar do usuário</string>
<string name="sample_message">ei, encontrei o esconderijo, está aqui ao lado do tigre grande. estou um pouco assustado.</string>
<string name="send_text">Enviar Texto</string>

Wyświetl plik

@ -7,7 +7,7 @@
<string name="unset">Não Definido</string>
<string name="connection_status">Estado da Conexão</string>
<string name="application_icon">icone da aplicação</string>
<string name="unknown_username">Nome Desconhecido</string>
<string name="unknown_username">Nome desconhecido</string>
<string name="user_avatar">Avatar do Usuário</string>
<string name="sample_message">Hey, encontrei o cache, está aqui ao lado do grande tigre. Estou um pouco assustado.</string>
<string name="send_text">Enviar Texto</string>

Wyświetl plik

@ -124,7 +124,7 @@
<string name="download_region_dialog_title">Завантажити регіон</string>
<string name="download_region_connection_alert">Ви не підключені до Інтернету, ви не можете завантажити офлайн-карту</string>
<string name="download_failed">Не вдалося завантажити пакет стилів</string>
<string name="preferences_language">Мова (потрібне перезавантаження)</string>
<string name="preferences_language">Мова</string>
<string name="preferences_system_default">Системні налаштунки за умовчанням</string>
<string name="preferences_map_style">Джерело карти</string>
<string name="resend">Перенадіслати</string>

Wyświetl plik

@ -1,94 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="language_entries" translatable="false">
<!-- zz -->
<item>@string/preferences_system_default</item>
<!-- en -->
<item>English</item>
<!-- cs -->
<item>Čeština</item>
<!-- zh -->
<item>Chinese 中文 </item>
<!-- de -->
<item>Deutsch</item>
<!-- es -->
<item>Español</item>
<!-- fr -->
<item>Français</item>
<!-- ga -->
<item>Gaeilge</item>
<!-- el -->
<item>Greek ελληνικά</item>
<!-- ht -->
<item>Haiti</item>
<!-- it -->
<item>Italiano</item>
<!-- ja -->
<item>Japanese 日本語</item>
<!-- ko-rKR -->
<item>Korean 한국어</item>
<!-- hu -->
<item>Magyar</item>
<!-- nl -->
<item>Nederlands</item>
<!-- no -->
<item>Norge</item>
<!-- pl -->
<item>Polski</item>
<!-- pt -->
<item>Português</item>
<!-- pt_BR -->
<item>Português do Brasil</item>
<!-- ro -->
<item>Română</item>
<!-- ru -->
<item>Russian Pусский</item>
<!-- sq -->
<item>Shqip</item>
<!-- sk -->
<item>Slovenský</item>
<!-- sl -->
<item>Slovenščina</item>
<!-- fi -->
<item>Suomi</item>
<!-- sv -->
<item>Svenska</item>
<!-- tr -->
<item>Türkçe</item>
<!-- uk -->
<item>Українська</item>
</string-array>
<string-array name="language_values" translatable="false">
<item>zz</item>
<item>en</item>
<item>cs</item>
<item>zh</item>
<item>de</item>
<item>es</item>
<item>fr</item>
<item>ga</item>
<item>el</item>
<item>ht</item>
<item>it</item>
<item>ja</item>
<item>ko_KR</item>
<item>hu</item>
<item>nl</item>
<item>no</item>
<item>pl</item>
<item>pt</item>
<item>pt_BR</item>
<item>ro</item>
<item>ru</item>
<item>sq</item>
<item>sk</item>
<item>sl</item>
<item>fi</item>
<item>sv</item>
<item>tr</item>
<item>uk</item>
</string-array>
<string-array name="map_styles">
<item>OpenStreetMap</item>
<item>USGS TOPO</item>

Wyświetl plik

@ -1,4 +1,8 @@
<resources>
// Language tags native names (not available via .getDisplayLanguage)
<string name="fr_HT" translatable="false">Kreyòl ayisyen</string>
<string name="pt_BR" translatable="false">Português do Brasil</string>
<string name="app_name" translatable="false">Meshtastic</string>
<string name="action_settings">Settings</string>
<string name="channel_name">Channel Name</string>
@ -129,7 +133,7 @@
<string name="download_region_dialog_title">Download Region</string>
<string name="download_region_connection_alert">You are not connected to the internet, you cannot download an offline map</string>
<string name="download_failed">Unable to download style pack</string>
<string name="preferences_language">Language (restart needed)</string>
<string name="preferences_language">Language</string>
<string name="preferences_system_default">System default</string>
<string name="preferences_map_style">Map Source</string>
<string name="resend">Resend</string>

Wyświetl plik

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en"/> <!-- English (ultimate fallback locale) -->
<locale android:name="cs"/> <!-- Czech -->
<locale android:name="zh"/> <!-- Chinese -->
<locale android:name="de"/> <!-- German -->
<locale android:name="es"/> <!-- Spanish (Spain) -->
<locale android:name="fr"/> <!-- French (France) -->
<locale android:name="ga"/> <!-- Irish -->
<locale android:name="el"/> <!-- Greek -->
<locale android:name="fr-HT"/> <!-- Haitian Creole -->
<locale android:name="it"/> <!-- Italian -->
<locale android:name="ja"/> <!-- Japanese -->
<locale android:name="ko"/> <!-- Korean -->
<locale android:name="hu"/> <!-- Hungarian -->
<locale android:name="nl"/> <!-- Dutch -->
<locale android:name="nb"/> <!-- Norwegian -->
<locale android:name="pl"/> <!-- Polish -->
<locale android:name="pt"/> <!-- Portuguese -->
<locale android:name="pt-BR"/> <!-- Portuguese (Brazil) -->
<locale android:name="ro"/> <!-- Romanian -->
<locale android:name="ru"/> <!-- Russian -->
<locale android:name="sq"/> <!-- Albanian -->
<locale android:name="sk"/> <!-- Slovak -->
<locale android:name="sl"/> <!-- Slovenian -->
<locale android:name="fi"/> <!-- Finnish -->
<locale android:name="sv"/> <!-- Swedish -->
<locale android:name="tr"/> <!-- Turkish -->
<locale android:name="uk"/> <!-- Ukrainian -->
</locale-config>