Changing the App Language not working correctly (Kotlin) - android

I have a Splash screen in my kotlin app, where I do some stuff like loading data. Currently it is only in german and I wanted to add english as additional language.
I've create the strings resources and so on.
Now I have added a bit of code to the Splash-Screen, where it checks, if the user already set a preferenced language (saved in sharedPreferences).
When there is nothing saved, a simple alertdialog pops up asking the user for his preference and sets everything accordingly.
Then the splashscreen is reloaded. When I choose english on my device (system language is german), the splash screen gets the correct string resources (english).
But when it changes to the MainActivity, it is german again. Only after restarting the app, everything is in english.
Here is how I change the language on my splashscreen:
private lateinit var mainIntent : Intent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
var localeToSet: String? = prefs.getString("language", "")
if(localeToSet == null || localeToSet.equals("")){
val languages = arrayOf("Deutsch", "English")
val langSelectorBuilder = AlertDialog.Builder(this)
langSelectorBuilder.setTitle(R.string.selectLanguageText)
langSelectorBuilder.setCancelable(false)
langSelectorBuilder.setSingleChoiceItems(languages, -1) { dialog, selection ->
when(selection) {
0 -> {
localeToSet = "de"
setLocale("de")
}
1 -> {
localeToSet = "en"
setLocale("en")
}
}
recreate()
dialog.dismiss()
}.setOnDismissListener {
Handler().postDelayed({
startActivity(mainIntent)
finish()
}, SPLASH_TIME_OUT)
}
langSelectorBuilder.create().show()
}
else{
setLocale(localeToSet!!)
Handler().postDelayed({
startActivity(mainIntent)
finish()
}, SPLASH_TIME_OUT)
}
setContentView(R.layout.activity_splash_screen)
}
private fun createIntent(localeToSet: String){
mainIntent = Intent(this#SplashScreenActivity, MainActivity::class.java).apply {
//Do stuff and pass data to mainactivity
}
private fun setLocale(localeToSet: String) {
createIntent(localeToSet)
val config = resources.configuration
val locale = Locale(localeToSet)
Locale.setDefault(locale)
config.locale = locale
resources.updateConfiguration(config, resources.displayMetrics)
val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val editor: SharedPreferences.Editor = prefs.edit()
editor.putString("language", localeToSet)
editor.apply()
}

Nevermind, found the issue:
val langSelectorBuilder = AlertDialog.Builder(this)
langSelectorBuilder.setTitle(R.string.selectLanguageText)
langSelectorBuilder.setCancelable(false)
langSelectorBuilder.setSingleChoiceItems(languages, -1) { dialog, selection ->
when(selection) {
0 -> {
localeToSet = "de"
setLocale("de")
}
1 -> {
localeToSet = "en"
setLocale("en")
}
}
recreate()
dialog.dismiss()
}.setOnDismissListener {
finish()
}
langSelectorBuilder.create().show()
Without starting the MainActivity here, it works like it should

Related

SharedPreferences not working when Activity is started from somewhere else?

I have a ThreadActivity with two functions, saveContacts and loadContacts. They both use sharedpreferences and Gson to save an ArrayList consisting of Objects called SimpleContacts. Somehow it cannot retrieve data from sharedpreferences once I start the Activity from somewhere else. (I tried loading instantly after saving and that works, but not if I close the Activity and re-open it)
The save function:
private fun saveContact() {
val gson = Gson()
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
try {
val editor = sharedPreferences.edit()
val json = gson.toJson(participants)
editor.putString(threadId.toString()+"_Contact", json)
editor.apply()
} catch(e: Exception) {
e.printStackTrace()
}
}
The load function:
private fun loadContact() {
val gson = Gson()
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
val type = object : TypeToken<ArrayList<SimpleContact?>?>() {}.type
try {
val json = sharedPreferences.getString(threadId.toString()+"_Contact", "")
participants = gson.fromJson(json, type)
} catch(e: Exception) {
e.printStackTrace()
}
}
I have 2 Activities that can open this ThreadActivity, if I start it from the same one, it all works perfectly fine. But when I use the other Activity to start it, the sharedPrefs are empty.
Launch Activity that works (I don't know if its because its the way the Intent is build so I will write them both here):
private fun launchThreadActivity(phoneNumber: String, name: String) {
hideKeyboard()
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
val numbers = phoneNumber.split(";").toSet()
val number = if (numbers.size == 1) phoneNumber else Gson().toJson(numbers)
Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, getThreadId(numbers))
putExtra(THREAD_TITLE, name)
putExtra(THREAD_TEXT, text)
putExtra(THREAD_NUMBER, number)
if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
putExtra(THREAD_ATTACHMENT_URI, uri?.toString())
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
putExtra(THREAD_ATTACHMENT_URIS, uris)
}
startActivity(this)
}
}
Start Activity that does not work:
Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, (it as Conversation).threadId)
putExtra(THREAD_TITLE, it.title)
putExtra("fromMain", true)
startActivity(this)
}
Nevermind, it was my mistake.
When saveContact was called the threadId was not initialized yet. So basically the keys were always different.

setting language before setContentView results in unending loop

I have a fragment with settings of an app that saves the user selected mode and language
( a bit of code:
binding.languageButton.setOnClickListener{
builder = AlertDialog.Builder(requireContext())
builder.setTitle(getString(R.string.setLanguage))
builder.setItems(langArray) { _, which ->
val sharedPref = requireActivity().getSharedPreferences("Settings", Context.MODE_PRIVATE).edit()
sharedPref.putString("LANGUAGE", langArray[which]).apply()
checkUserPreferences()
changeLanguage(langArray[which])
binding.languageButton.text = langArray[which]
}
builder.create()
builder.show()
}
}
private fun changeLanguage(language: String) {
if(language != binding.languageButton.text.toString()){
val local = Locale(language)
val dm = resources.displayMetrics
val con = resources.configuration
con.locale = local
resources.updateConfiguration(con, dm)
val refresh = Intent(
requireContext(),
MainActivity::class.java
)
refresh.putExtra(binding.languageButton.text.toString(), language)
startActivity(refresh)
}
}
and that part (as mentioned) saves mode and selected language to sharedPreferences that I later want to use in mainActivity and other fragments, and I've put in MainActivity:
private fun loadPreferences(){
val preferences = getSharedPreferences("Settings", Activity.MODE_PRIVATE)
Log.i(TAG, preferences.getString("LANGUAGE", "eng").toString())
Log.i(TAG, preferences.getInt("MODE", 1).toString())
val local = Locale(preferences.getString("LANGUAGE", "eng").toString())
val dm = resources.displayMetrics
val con = resources.configuration
con.locale = local
resources.updateConfiguration(con, dm)
val refresh = Intent(
this.baseContext,
MainActivity::class.java
)
refresh.putExtra(preferences.getString("LANGUAGE", "eng").toString(),
preferences.getString("LANGUAGE", "eng"))
startActivity(refresh)
}
and this is referenced in:
class MainActivity : AppCompatActivity() {
// TODO: IT'S THE ONE
companion object {
private const val TAG = "MainActivity"
private const val CHANNEL_ID = "plantz_app_channel_01"
private const val notificationId = 909
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadPreferences()
setContentView(R.layout.activity_main)
// ...
and after running the app It only shows the app logo and logs sharedPreferences but the app doesn't go any further, I've tried to tweak with it for a bit but It hasn't done much, any ideas what should I change to make it work?
Thanks in advance :)
This infinity loop happens because when the MainActivity is going to be opened you call loadPreferences() in onCreate method where you open a new instance of MainActivity from there.
So the infinity loop goes like below:
onCreate() method of MainActivity is called
loadPreferences() method is called
in loadPreferences() after you have set the language from SharedPreferences you start a new instance of your MainActivity which redirects you back to step 1.
To avoid this infinity loop remove the below lines from your loadPreferences() method:
val refresh = Intent(this.baseContext,MainActivity::class.java)
refresh.putExtra(preferences.getString("LANGUAGE", "eng").toString(), preferences.getString("LANGUAGE", "eng"))
startActivity(refresh)

Localization not working after app restart

I'm including localization to translate my app intro three different languages, after setting up the translated strings of each language, I go to preference settings and I check a checkbox and and the translation works fine, the problem is that when I restart the app , localization even though I have saved the chosen language in sharedpreference and retreiving it in mainactivity
*This is how I'm setting the languages
var sharedPreferences = requireContext().getSharedPreferences("prefs",
Context.MODE_PRIVATE)
var editor = sharedPreferences.edit()
spanishCheckBox.setOnPreferenceChangeListener(object : Preference.OnPreferenceChangeListener{
override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean {
var isSpanishChecked = newValue as Boolean
if(isSpanishChecked){
var Lang = "es"
editor.putString("key",Lang)
editor.apply()
var local = Locale(Lang)
var configuration = Configuration()
configuration.locale = local
resources.updateConfiguration(configuration,resources.displayMetrics)
englishCheckBox.isChecked = false
frenchCheckBox.isChecked = false
Intent(requireContext(),MainActivity::class.java).also {
it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(it)
}
}
return true
}
})
This is how i m retreiving data in mainactivity
fun LoadLanguageConfiguration(){
var sharedpreferences = getSharedPreferences("prefs", Context.MODE_PRIVATE)
var langCode = sharedpreferences.getString("key","")
var local = Locale(langCode)
var configuration = Configuration()
configuration.locale = local
baseContext.createConfigurationContext(configuration)
}
The translation only works when I set the language in the app, but when I restart the app, it goes back to default language which is English.
The problem is that I was not starting my language loading method at the start and before everything, so I put it as first method in my oncreate and everything works as supposed to.

How to write test cases for switch condition in android kotlin

I need to write test case for the switch condition in kotlin.
Class.kt
fun getEnvSwitchURL(applicationContext: Context, envSwitchInfo: String): String {
val resources = applicationContext.getResources()
val assetManager = resources.getAssets()
val properties = Properties()
try {
val inputStream = assetManager.open("configuration.properties")
properties.load(inputStream)
val urlPref = applicationContext.getSharedPreferences(SELECTED_ENV, Context.MODE_PRIVATE)
val editor = urlPref.edit()
when (envSwitchInfo) {
"Production" ->{
editor.putString("selectedUrl", properties.getProperty("prodUrl"))
editor.apply()
selectedUrl=properties.getProperty("prodUrl")
}
"Development" ->{
editor.putString("selectedUrl", properties.getProperty("devUrl"))
editor.apply()
selectedUrl=properties.getProperty("devUrl")
}
"Testing" ->{
editor.putString("selectedUrl", properties.getProperty("testUrl"))
editor.apply()
selectedUrl=properties.getProperty("testUrl")
}
}
inputStream.close()
}
return selectedUrl
}
test.kt
#BeforeEach
fun runBeforeTest() {
testApplicationContext = Mockito.mock(Context::class.java)
testResource = Mockito.mock(Resources::class.java)
testAsset = Mockito.mock(AssetManager::class.java)
testInputStream = Mockito.mock(InputStream::class.java)
testSharedPref=Mockito.mock(SharedPreferences::class.java)
testEditor=Mockito.mock(SharedPreferences.Editor::class.java)
testProperties=Mockito.mock(Properties::class.java)
testProperties.setProperty("prodUrl", "Value");
}
#Test
fun getEnvSwitchURL() {
Mockito.`when`(testApplicationContext.getResources()).thenReturn(testResource)
Mockito.`when`(testResource.assets).thenReturn(testAsset)
Mockito.`when`(testAsset.open(Mockito.anyString())).thenReturn(testInputStream)
PowerMockito.whenNew(Properties::class.java).withNoArguments().thenReturn(testProperties)
Mockito.doNothing().`when`(testProperties).load(Mockito.any(InputStream::class.java))
Mockito.`when`(testApplicationContext.getSharedPreferences(anyString(),anyInt())).thenReturn(testSharedPref)
Mockito.`when`(testSharedPref.edit()).thenReturn(testEditor)
envSwitchUtils.getEnvSwitchURL(testApplicationContext, testEnvSwitchInfo)
}
Above written test case is working fine. I need to find out how to write test case for switch condition for the above class. Kindly help me to write the same
I haven't answered your question, but perhaps refactoring your code slightly makes it more obvious to test:
private val SELECTED_ENV = "";
fun getEnvSwitchURL(applicationContext: Context, envSwitchInfo: String): String {
val resources = applicationContext.resources
val assetManager = resources.assets
val properties = Properties()
val selectedUrl: String
try {
val inputStream = assetManager.open("configuration.properties")
properties.load(inputStream)
val urlPref = applicationContext.getSharedPreferences(SELECTED_ENV, Context.MODE_PRIVATE)
val editor = urlPref.edit()
selectedUrl = get(envSwitchInfo, properties)
editor.putString("selectedUrl", selectedUrl)
editor.apply()
inputStream.close()
}
return selectedUrl
}
fun get(envSwitchInfo: String, properties: Properties): String {
when (envSwitchInfo) {
"Production" -> {
return properties.getProperty("prodUrl")
}
"Development" -> {
return properties.getProperty("devUrl")
}
"Testing" -> {
return properties.getProperty("testUrl")
}
else -> throw IllegalStateException("Unhandled environment $envSwitchInfo")
}
}
You could do a lot more here, look into the Single Responsibilty Principle. This is a start, for unit testing you don't want to test that SharePreferences works correctly because then you are testing the platform and not your code. You may want to test only that when you pass an environment like "Production", then the selectedUrl you get is returned.
Testing inputs and outputs as described above would be something like this:
String url = envSwitchUtils.getEnvSwitchURL(testApplicationContext, "Production")
assertEquals(url, "http://myProdUrl")
and another test
String url = envSwitchUtils.getEnvSwitchURL(testApplicationContext, "Development")
assertEquals(url, "http://myDevUrl")

How to change language in kotlin (locale)

I have 2 string files "en" and "tr". When I change my telephone's language string files change automatically(I didn't write extra code for this result and I don't know how this happen). I want change string files with programmatically.
I used this code. I get Toast message but language doesn't change.WHY? I used these code before for another application which I write with java not Kotlin and these code work fine. Please don't say duplicate because I read a lot of questions. I try a lot of things until now 4 hours.
override fun onResume() {
buttonDate()
changeLanguage()
super.onResume()
}
fun changeLanguage(){
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
val language = sharedPreferences.getString("language","bak")
Toast.makeText(applicationContext,language,Toast.LENGTH_SHORT).show()
if(language=="English"){
Toast.makeText(applicationContext,"English",Toast.LENGTH_SHORT).show()
language("")
}else if(language=="Turkish"){
Toast.makeText(applicationContext,"Turkish",Toast.LENGTH_SHORT).show()
language("tr")
}
}
fun language(language: String){
val locale = Locale(language)
Locale.setDefault(locale)
val resources = getResources()
val configuration = resources.getConfiguration()
configuration.locale = locale
resources.updateConfiguration(configuration, resources.getDisplayMetrics())
}
You need to update configuration even before onCreate is called. To do that
create a BaseActivity class like this
open class BaseActivity : AppCompatActivity() {
companion object {
public var dLocale: Locale? = null
}
init {
updateConfig(this)
}
fun updateConfig(wrapper: ContextThemeWrapper) {
if(dLocale==Locale("") ) // Do nothing if dLocale is null
return
Locale.setDefault(dLocale)
val configuration = Configuration()
configuration.setLocale(dLocale)
wrapper.applyOverrideConfiguration(configuration)
}
}
Extend you activities from this class.
Set dLocale in you App class like this:
class App : Application() {
override fun onCreate() {
super.onCreate()
var change = ""
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val language = sharedPreferences.getString("language", "bak")
if (language == "Turkish") {
change="tr"
} else if (language=="English" ) {
change = "en"
}else {
change =""
}
BaseActivity.dLocale = Locale(change) //set any locale you want here
}
}
You will also need to set App class in your manifest file like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
//..
<application
android:name=".App"
//..>
</application>
</manifest>
Note: We should set dLocale only in App onCreate to ensure that all activities have same language.

Categories

Resources