mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-07-23 19:50:18 +00:00
Added language selection (bitfireAT/davx5#137)
* added language selection * PermissionsFragment: Hide notification permission switch on Android < 13 * Retrieve locales with a function * Added locales flavoring Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> * Moved locales functions to `locales.gradle` Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> * Added `locales_config.xml` generation Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> * Now gets generated automatically Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> * Added `android:localeConfig` Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> * Updated submodules Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> * Using `logger` instead of `println` Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> Signed-off-by: Arnau Mora <arnyminer.z@gmail.com> Co-authored-by: Ricki Hirner <hirner@bitfire.at> Co-authored-by: Arnau Mora <arnyminer.z@gmail.com>
This commit is contained in:
parent
0521f4bcf0
commit
1f818ee3b4
|
@ -8,6 +8,8 @@ apply plugin: 'dagger.hilt.android.plugin'
|
|||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
apply from: 'locales.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
buildToolsVersion '33.0.0'
|
||||
|
@ -33,6 +35,16 @@ android {
|
|||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
def locales = getLocales(variant.flavorName)
|
||||
|
||||
variant.buildConfigField "String[]", "TRANSLATION_ARRAY", "new String[]{\"" + locales.join("\",\"") + "\"}"
|
||||
variant.mergedFlavor.resourceConfigurations.clear()
|
||||
variant.mergedFlavor.resourceConfigurations.addAll(locales)
|
||||
|
||||
generateLocalesConfig(variant.flavorName, locales)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
@ -62,6 +74,9 @@ android {
|
|||
}
|
||||
|
||||
sourceSets {
|
||||
ose.res.srcDirs += "build/generated/res/locale-ose"
|
||||
|
||||
androidTest.java.srcDirs = [ "src/androidTest/java" ]
|
||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
|
||||
|
@ -110,7 +125,7 @@ dependencies {
|
|||
implementation "com.google.dagger:hilt-android:${versions.hilt}"
|
||||
kapt "com.google.dagger:hilt-android-compiler:${versions.hilt}"
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.0-rc01'
|
||||
implementation 'androidx.browser:browser:1.4.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.concurrent:concurrent-futures-ktx:1.1.0'
|
||||
|
|
105
app/locales.gradle
Normal file
105
app/locales.gradle
Normal file
|
@ -0,0 +1,105 @@
|
|||
import groovy.xml.MarkupBuilder
|
||||
|
||||
import static groovy.io.FileType.DIRECTORIES
|
||||
|
||||
/**
|
||||
* Obtains a list of all the available locales for an specific flavor
|
||||
* @since 20221123
|
||||
* @param flavorDir The base directory of the flavor inside `/app/src`
|
||||
* @return A list with the language codes of the locales available.
|
||||
*/
|
||||
Set<String> getLocalesForFlavor(File flavorDir) {
|
||||
def dir = new File(flavorDir, "res")
|
||||
|
||||
if (!flavorDir.exists()) {
|
||||
logger.warn("Tried to get locales for non-existing flavor. Directory: $flavorDir")
|
||||
return new LinkedHashSet()
|
||||
}
|
||||
if (!dir.exists()) {
|
||||
logger.warn("Tried to get locales for a flavor without strings. Directory: $dir")
|
||||
return new LinkedHashSet()
|
||||
}
|
||||
|
||||
// Initialize the list English, since it's available by default
|
||||
Set<String> locales = new LinkedHashSet(['en'])
|
||||
|
||||
// Get all directories inside resources
|
||||
logger.trace("Getting locales values directories from $dir")
|
||||
dir.traverse(type: DIRECTORIES, maxDepth: 0) { file ->
|
||||
// Get only values directories
|
||||
def fileName = file.name
|
||||
if (!fileName.startsWith("values-")) return
|
||||
|
||||
// Take only the values directories that contain strings
|
||||
def stringsFile = new File(file, "strings.xml")
|
||||
if (!stringsFile.exists()) return
|
||||
|
||||
// Add to the list the locale of the strings file
|
||||
def langCode = fileName.substring(fileName.indexOf('-') + 1)
|
||||
locales.add(langCode)
|
||||
}
|
||||
|
||||
// Log the available locales
|
||||
logger.info('Supported locales: ' + locales.join(", "))
|
||||
|
||||
// Return the built list
|
||||
return locales
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of all the available locales
|
||||
* @since 20220928
|
||||
* @return A list with the language codes of the locales available.
|
||||
*/
|
||||
Set<String> getLocales(String flavor) {
|
||||
// Get all the flavor directories
|
||||
def dir = new File(projectDir, "src")
|
||||
|
||||
// Get a list of locales for the base flavor
|
||||
def mainDir = new File(dir, 'main')
|
||||
logger.trace("Getting main locales ($mainDir)...")
|
||||
def mainLocales = getLocalesForFlavor(mainDir)
|
||||
|
||||
// Get the current flavor
|
||||
def flavorDir = new File(dir, flavor)
|
||||
logger.trace("Getting locales for flavor $flavor ($flavorDir)...")
|
||||
def flavorLocales = getLocalesForFlavor(flavorDir)
|
||||
|
||||
// Build the locales list
|
||||
// We use Set for avoiding duplicates
|
||||
Set<String> locales = new LinkedHashSet()
|
||||
locales.addAll(mainLocales)
|
||||
locales.addAll(flavorLocales)
|
||||
|
||||
// Log the available locales
|
||||
logger.trace("Supported locales for flavor $flavor: " + locales.join(', '))
|
||||
|
||||
return locales
|
||||
}
|
||||
|
||||
def generateLocalesConfig(String flavor, Set<String> locales) {
|
||||
def outputDir = new File(projectDir, "build/generated/res/locale-$flavor/xml")
|
||||
mkdir outputDir
|
||||
|
||||
logger.trace("Generating locales_config.xml...")
|
||||
new File(outputDir, "locales_config.xml").withWriter { writer ->
|
||||
def destXml = new MarkupBuilder(new IndentPrinter(writer, " ", true, true))
|
||||
destXml.setDoubleQuotes(true)
|
||||
def destXmlMkp = destXml.getMkp()
|
||||
destXmlMkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
|
||||
destXmlMkp.comment("Generated at ${new Date()}")
|
||||
destXmlMkp.yield "\r\n"
|
||||
|
||||
destXml."locale-config"(['xmlns:android': "http://schemas.android.com/apk/res/android"]) {
|
||||
locales.forEach { locale ->
|
||||
destXml."locale"("android:name": locale)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export getLocales and generateLocalesConfig
|
||||
ext {
|
||||
getLocales = this.&getLocales
|
||||
generateLocalesConfig = this.&generateLocalesConfig
|
||||
}
|
|
@ -48,7 +48,8 @@
|
|||
android:theme="@style/AppTheme"
|
||||
android:resizeableActivity="true"
|
||||
tools:ignore="UnusedAttribute"
|
||||
android:supportsRtl="true">
|
||||
android:supportsRtl="true"
|
||||
android:localeConfig="@xml/locales_config">
|
||||
|
||||
<!-- required for Hilt/WorkManager integration -->
|
||||
<provider
|
||||
|
@ -300,6 +301,17 @@
|
|||
android:resource="@xml/debug_paths" />
|
||||
</provider>
|
||||
|
||||
|
||||
<!-- Support language selection on Android 12 and lower -->
|
||||
<service
|
||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||
android:enabled="false"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="autoStoreLocales"
|
||||
android:value="true" />
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
<!-- package visiblity – which apps do we need to see? -->
|
||||
|
|
|
@ -34,6 +34,9 @@ object Settings {
|
|||
const val PREFERRED_THEME = "preferred_theme"
|
||||
const val PREFERRED_THEME_DEFAULT = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
|
||||
const val LANGUAGE = "language"
|
||||
const val LANGUAGE_SYSTEM = "language_system"
|
||||
|
||||
const val PREFERRED_TASKS_PROVIDER = "preferred_tasks_provider"
|
||||
|
||||
/** whether detected collections are selected for synchronization for default */
|
||||
|
|
|
@ -15,6 +15,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.annotation.UiThread
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.preference.*
|
||||
import at.bitfire.cert4android.CustomCertManager
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
|
@ -32,6 +33,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -227,6 +229,38 @@ class AppSettingsActivity: AppCompatActivity() {
|
|||
false
|
||||
}
|
||||
}
|
||||
findPreference<ListPreference>(Settings.LANGUAGE)!!.apply {
|
||||
val languageOptions = mutableListOf<Locale?>(null)
|
||||
for (language in BuildConfig.TRANSLATION_ARRAY) {
|
||||
languageOptions.add(Locale.forLanguageTag(language))
|
||||
}
|
||||
this.entries = languageOptions.map { language -> language?.displayLanguage ?: context.getText(R.string.app_settings_language_system_default) }.toTypedArray()
|
||||
this.entryValues = languageOptions.map { language -> language?.language ?: Settings.LANGUAGE_SYSTEM }.toTypedArray()
|
||||
|
||||
val appCompatLocales = AppCompatDelegate.getApplicationLocales()
|
||||
var currentLocale: Locale? = null
|
||||
if(!appCompatLocales.isEmpty) {
|
||||
for (i in 0 until appCompatLocales.size()) {
|
||||
val locale = appCompatLocales[i] ?: continue
|
||||
if (languageOptions.contains(appCompatLocales[i]!!)) {
|
||||
currentLocale = locale
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
setValueIndex(entryValues.indexOf(currentLocale?.language ?: Settings.LANGUAGE_SYSTEM))
|
||||
summary = entry
|
||||
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
if(newValue.toString() == Settings.LANGUAGE_SYSTEM)
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
|
||||
else {
|
||||
val newLanguage = Locale.forLanguageTag(newValue.toString())
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.create(newLanguage))
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// integration settings
|
||||
findPreference<Preference>(Settings.PREFERRED_TASKS_PROVIDER)!!.apply {
|
||||
|
|
5
app/src/main/res/drawable/ic_language.xml
Normal file
5
app/src/main/res/drawable/ic_language.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/>
|
||||
</vector>
|
|
@ -206,6 +206,8 @@
|
|||
<string name="app_settings_notification_settings">Notification settings</string>
|
||||
<string name="app_settings_notification_settings_summary">Manage notification channels and their settings</string>
|
||||
<string name="app_settings_theme_title">Select theme</string>
|
||||
<string name="app_settings_language_title">Select language</string>
|
||||
<string name="app_settings_language_system_default">System default</string>
|
||||
<string-array name="app_settings_theme_names">
|
||||
<item>System default</item>
|
||||
<item>Light</item>
|
||||
|
|
|
@ -95,6 +95,12 @@
|
|||
android:entryValues="@array/app_settings_theme_values"
|
||||
android:persistent="false" />
|
||||
|
||||
<ListPreference
|
||||
android:key="language"
|
||||
android:icon="@drawable/ic_language"
|
||||
android:title="@string/app_settings_language_title"
|
||||
android:persistent="false" />
|
||||
|
||||
<Preference
|
||||
android:key="reset_hints"
|
||||
android:title="@string/app_settings_reset_hints"
|
||||
|
|
Loading…
Reference in a new issue