Skip to content

Commit

Permalink
SettingsActivity into Kotlin, improve in-app locale, fix RTL layout
Browse files Browse the repository at this point in the history
  • Loading branch information
michelesalvador committed Feb 21, 2024
1 parent bd2b3c1 commit abbecec
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 212 deletions.
8 changes: 3 additions & 5 deletions app/src/main/java/app/familygem/BaseActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ public class BaseActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Renews activity title when in-app language is changed
// Updates activity title when one in-app language is selected
try {
int label = getPackageManager().getActivityInfo(getComponentName(), 0).labelRes;
if (label != 0) {
setTitle(label);
}
int title = getPackageManager().getActivityInfo(getComponentName(), 0).labelRes;
if (title != 0) setTitle(title);
} catch (Exception ignored) {
}
}
Expand Down
29 changes: 6 additions & 23 deletions app/src/main/java/app/familygem/Global.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package app.familygem;

import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.multidex.MultiDexApplication;

import com.google.gson.Gson;
Expand All @@ -17,7 +15,6 @@

import java.io.File;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class Global extends MultiDexApplication {
Expand Down Expand Up @@ -55,10 +52,13 @@ public class Global extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
// Exception handler
defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
context = getApplicationContext();

// App context
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) context = getApplicationContext();
else context = ContextCompat.getContextForLanguage(getApplicationContext()); // Context with app locale
// App settings
File settingsFile = new File(context.getFilesDir(), "settings.json");
// Renames "preferenze.json" to "settings.json" (introduced in version 0.8)
File preferencesFile = new File(context.getFilesDir(), "preferenze.json");
Expand Down Expand Up @@ -139,7 +139,6 @@ private static String updateSettings(String json) {

// Italian translated to English (version 0.8)
.replace("\"alberi\":", "\"trees\":")
.replace("\"alberi\":", "\"trees\":")
.replace("\"idAprendo\":", "\"openTree\":")
.replace("\"autoSalva\":", "\"autoSave\":")
.replace("\"caricaAlbero\":", "\"loadTree\":")
Expand All @@ -154,20 +153,4 @@ private static String updateSettings(String json) {
.replace("\"grado\":", "\"grade\":")
.replace("\"data\":", "\"dateId\":");
}

@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { // Tiramisu doesn't need this
// Keeps the app locale if system language is changed while the app is running
Locale appLocale = AppCompatDelegate.getApplicationLocales().get(0);
if (appLocale != null) {
// Doesn't update directly 'newConfig' because it would create a configuration change loop
Configuration resConfig = getResources().getConfiguration();
resConfig.setLocale(appLocale);
// Keeps locale for static global context
getApplicationContext().getResources().updateConfiguration(resConfig, null);
}
}
super.onConfigurationChanged(newConfig);
}
}
179 changes: 0 additions & 179 deletions app/src/main/java/app/familygem/SettingsActivity.java

This file was deleted.

134 changes: 134 additions & 0 deletions app/src/main/java/app/familygem/SettingsActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package app.familygem

import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.LocaleManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.os.LocaleListCompat
import app.familygem.databinding.SettingsActivityBinding
import org.xmlpull.v1.XmlPullParser
import java.util.Locale

class SettingsActivity : BaseActivity() {

private lateinit var binding: SettingsActivityBinding
private lateinit var languages: MutableList<Language>

/** The actual Language of the app, otherwise the "system language". */
private val actualLanguage: Language
get() {
val firstLocale = AppCompatDelegate.getApplicationLocales()[0]
if (firstLocale != null) {
for (i in 1 until languages.size) {
val language = languages[i]
if (firstLocale.toString().startsWith(language.code!!)) return language
}
}
return languages[0]
}

override fun onCreate(bundle: Bundle?) {
super.onCreate(bundle)
binding = SettingsActivityBinding.inflate(layoutInflater)
setContentView(binding.root)

// Auto save
val saveSwitch = binding.settingsAutoSave
saveSwitch.isChecked = Global.settings.autoSave
saveSwitch.setOnCheckedChangeListener { _, checked ->
Global.settings.autoSave = checked
Global.settings.save()
}

// Load tree at startup
val loadSwitch = binding.settingsLoadTree
loadSwitch.isChecked = Global.settings.loadTree
loadSwitch.setOnCheckedChangeListener { _, checked ->
Global.settings.loadTree = checked
Global.settings.save()
}

// Expert mode
val expertSwitch = binding.settingsExpert
expertSwitch.isChecked = Global.settings.expert
expertSwitch.setOnCheckedChangeListener { _, checked ->
Global.settings.expert = checked
Global.settings.save()
}

// Language picker
languages = ArrayList()
languages.add(Language(null, 0)) // System language
// Gets languages from locales_config.xml
val xpp: XmlPullParser = resources.getXml(R.xml.locales_config)
while (xpp.eventType != XmlPullParser.END_DOCUMENT) {
if (xpp.eventType == XmlPullParser.START_TAG && xpp.name == "locale") {
val percent = xpp.getAttributeValue(null, "percent")
languages.add(Language(xpp.getAttributeValue(0), percent?.toInt() ?: 100))
}
xpp.next()
}
languages.sort()
val languageView = binding.settingsLanguage
val actual = actualLanguage
languageView.text = actual.toString()
val languageArray = languages.map { it.toString() }.toTypedArray()
languageView.setOnClickListener { view: View ->
AlertDialog.Builder(view.context)
.setSingleChoiceItems(languageArray, languages.indexOf(actual)) { dialog, item ->
// Sets app locale and store it for the future
val appLocale = LocaleListCompat.forLanguageTags(languages[item].code)
AppCompatDelegate.setApplicationLocales(appLocale)
// Updates app global context for this session only
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
binding.settingsLayout.postDelayed({
Global.context = ContextCompat.getContextForLanguage(applicationContext)
}, 50) // Waits just a bit to get the correct locale
}
// Removes switches to force KitKat to update their language
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
val layout = binding.settingsLayout
layout.removeView(saveSwitch)
layout.removeView(loadSwitch)
layout.removeView(expertSwitch)
recreate()
}
dialog.dismiss()
}.show()
}

// About
binding.settingsAbout.setOnClickListener {
startActivity(Intent(this@SettingsActivity, AboutActivity::class.java))
}
}

inner class Language(val code: String?, private val percent: Int) : Comparable<Language> {
override fun toString(): String {
return if (code == null) {
// Returns the string "System language" on the device locale, not on the app locale
val configuration = Configuration(resources.configuration)
val supportedLocales = languages.filterNot { it.code == null }.map { it.code }.toTypedArray()
val deviceLocale = LocaleManagerCompat.getSystemLocales(Global.context).getFirstMatch(supportedLocales)
configuration.setLocale(deviceLocale)
createConfigurationContext(configuration).getText(R.string.system_language).toString()
} else {
val locale = Locale(code)
var txt = locale.getDisplayLanguage(locale)
txt = txt.substring(0, 1).uppercase() + txt.substring(1)
if (percent < 100) txt += " ($percent%)"
txt
}
}

override fun compareTo(other: Language): Int {
return if (other.code == null) 1
else toString().compareTo(other.toString())
}
}
}
Loading

0 comments on commit abbecec

Please sign in to comment.