Skip to content

Commit 2b7c7d7

Browse files
refactor(i18n): Refactor language change implementation
This commit refactors the in-app language switching mechanism to improve its reliability and eliminate visual glitches. The previous approach, which relied on `AppCompatDelegate.setApplicationLocales()` and `activity.recreate()`, has been replaced with a more robust context-wrapping strategy. The key changes include: - **`LauncherLocaleManager`**: - Introduced `wrapContext(base: Context)` to create a new context with the selected locale configuration. - Removed the `applyAppLanguage` function, which used the now-deprecated `AppCompatDelegate` method for this purpose. - **`MainActivity`**: - Overrides `attachBaseContext` to wrap the base context with the localized one provided by `LauncherLocaleManager`. - **`LocalizedResources` object**: - A new singleton object that provides a cached, localized `Resources` instance to ensure the entire app consistently uses the correct strings. - **ContextExtensions**: - `getLocalizedString` and `getLocalizedStringArray` now use `LocalizedResources.get()` to fetch translated strings correctly. - **Settings**: - Changing the app language or theme now triggers a launcher restart via a new `reloadLauncher()` utility function, which calls `exitProcess(0)`. This ensures all components are re-initialized properly with the new configuration, preventing issues seen with `activity.recreate()`.
1 parent 15df16b commit 2b7c7d7

File tree

5 files changed

+72
-18
lines changed

5 files changed

+72
-18
lines changed

app/src/main/java/com/github/codeworkscreativehub/common/ContextExtensions.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,8 @@ fun Context.isBiometricEnabled(): Boolean {
320320
}
321321

322322
fun getLocalizedString(@StringRes resId: Int, vararg args: Any): String {
323-
val context = Mlauncher.getContext()
323+
val context = LocalizedResources.get(Mlauncher.getContext())
324+
324325
return if (args.isEmpty()) {
325326
context.getString(resId)
326327
} else {
@@ -329,8 +330,9 @@ fun getLocalizedString(@StringRes resId: Int, vararg args: Any): String {
329330
}
330331

331332
fun getLocalizedStringArray(@ArrayRes resId: Int): Array<String> {
332-
val context = Mlauncher.getContext()
333-
return context.resources.getStringArray(resId)
333+
val context = LocalizedResources.get(Mlauncher.getContext())
334+
335+
return context.getStringArray(resId)
334336
}
335337

336338

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,60 @@
11
package com.github.codeworkscreativehub.common
22

3-
import androidx.appcompat.app.AppCompatDelegate
4-
import com.github.codeworkscreativehub.mlauncher.Mlauncher
3+
import android.content.Context
4+
import android.content.res.Resources
55
import com.github.codeworkscreativehub.mlauncher.data.Constants
66
import com.github.codeworkscreativehub.mlauncher.data.Prefs
7+
import java.util.Locale
78

89
object LauncherLocaleManager {
9-
fun applyAppLanguage() {
10-
val prefs = Prefs(Mlauncher.getContext())
1110

12-
val primaryLocale = prefs.appLanguage.locale()
11+
fun wrapContext(base: Context): Context {
12+
val prefs = Prefs(base)
1313

14-
val localeList = if (prefs.appLanguage == Constants.Language.System) {
15-
androidx.core.os.LocaleListCompat.getEmptyLocaleList()
16-
} else {
17-
androidx.core.os.LocaleListCompat.create(primaryLocale)
14+
if (prefs.appLanguage == Constants.Language.System) {
15+
return base
1816
}
19-
AppCompatDelegate.setApplicationLocales(localeList)
17+
18+
val locale = prefs.appLanguage.locale()
19+
Locale.setDefault(locale)
20+
21+
val config = base.resources.configuration
22+
config.setLocale(locale)
23+
config.setLayoutDirection(locale)
24+
25+
return base.createConfigurationContext(config)
26+
}
27+
28+
fun updateLanguage(context: Context, language: Constants.Language) {
29+
val prefs = Prefs(context)
30+
prefs.appLanguage = language
2031
}
2132
}
2233

34+
object LocalizedResources {
35+
36+
@Volatile
37+
private var cachedResources: Resources? = null
38+
39+
fun get(context: Context): Resources {
40+
if (cachedResources == null) {
41+
synchronized(this) {
42+
if (cachedResources == null) {
43+
val wrapped = LauncherLocaleManager.wrapContext(
44+
context.applicationContext
45+
)
46+
cachedResources = wrapped.resources
47+
}
48+
}
49+
}
50+
return cachedResources!!
51+
}
52+
53+
fun invalidate() {
54+
cachedResources = null
55+
}
56+
}
57+
58+
59+
2360

app/src/main/java/com/github/codeworkscreativehub/mlauncher/MainActivity.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,15 @@ class MainActivity : AppCompatActivity() {
117117
}
118118
}
119119

120+
override fun attachBaseContext(newBase: Context) {
121+
super.attachBaseContext(
122+
LauncherLocaleManager.wrapContext(newBase)
123+
)
124+
}
125+
120126
override fun onCreate(savedInstanceState: Bundle?) {
121127
super.onCreate(savedInstanceState)
122128

123-
LauncherLocaleManager.applyAppLanguage()
124-
125129
// Enables edge-to-edge mode
126130
enableEdgeToEdge()
127131

app/src/main/java/com/github/codeworkscreativehub/mlauncher/helper/SystemUtils.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import android.graphics.PorterDuffColorFilter
2323
import android.graphics.Typeface
2424
import android.graphics.drawable.Drawable
2525
import android.os.Build
26+
import android.os.Looper
2627
import android.os.Process
2728
import android.os.UserHandle
2829
import android.os.UserManager
@@ -67,6 +68,7 @@ import java.text.SimpleDateFormat
6768
import java.util.Calendar
6869
import java.util.Locale
6970
import java.util.TimeZone
71+
import kotlin.system.exitProcess
7072

7173
fun emptyString(): String {
7274
return ""
@@ -215,6 +217,12 @@ fun ismlauncherDefault(context: Context): Boolean {
215217
}
216218
}
217219

220+
fun reloadLauncher() {
221+
android.os.Handler(Looper.getMainLooper()).postDelayed({
222+
exitProcess(0)
223+
}, 100)
224+
}
225+
218226
fun helpFeedbackButton(context: Context) {
219227
val uri = "https://github.com/CodeWorksCreativeHub/mLauncher".toUri()
220228
val intent = Intent(Intent.ACTION_VIEW, uri)

app/src/main/java/com/github/codeworkscreativehub/mlauncher/ui/SettingsFragment.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import androidx.lifecycle.ViewModelProvider
5353
import androidx.navigation.fragment.findNavController
5454
import com.github.codeworkscreativehub.common.AppLogger
5555
import com.github.codeworkscreativehub.common.LauncherLocaleManager
56+
import com.github.codeworkscreativehub.common.LocalizedResources
5657
import com.github.codeworkscreativehub.common.getLocalizedString
5758
import com.github.codeworkscreativehub.common.isBiometricEnabled
5859
import com.github.codeworkscreativehub.common.isGestureNavigationEnabled
@@ -84,6 +85,7 @@ import com.github.codeworkscreativehub.mlauncher.helper.hideStatusBar
8485
import com.github.codeworkscreativehub.mlauncher.helper.isSystemInDarkMode
8586
import com.github.codeworkscreativehub.mlauncher.helper.ismlauncherDefault
8687
import com.github.codeworkscreativehub.mlauncher.helper.openAppInfo
88+
import com.github.codeworkscreativehub.mlauncher.helper.reloadLauncher
8789
import com.github.codeworkscreativehub.mlauncher.helper.setThemeMode
8890
import com.github.codeworkscreativehub.mlauncher.helper.setTopPadding
8991
import com.github.codeworkscreativehub.mlauncher.helper.showNavigationBar
@@ -556,7 +558,7 @@ class SettingsFragment : BaseFragment() {
556558
Constants.Theme.System -> isSystemInDarkMode(requireContext())
557559
}
558560
setThemeMode(requireContext(), isDark, binding.settingsView)
559-
requireActivity().recreate()
561+
reloadLauncher()
560562
}
561563
}
562564
)
@@ -588,8 +590,9 @@ class SettingsFragment : BaseFragment() {
588590
val newLanguage = languageEntries[newLanguageIndex]
589591
selectedLanguage = newLanguage // Update state
590592
prefs.appLanguage = newLanguage // Persist in preferences
591-
LauncherLocaleManager.applyAppLanguage()
592-
requireActivity().recreate() // force reload with new language
593+
LauncherLocaleManager.updateLanguage(requireContext(), newLanguage)
594+
LocalizedResources.invalidate()
595+
reloadLauncher() // force reload with new language
593596
}
594597
}
595598
)

0 commit comments

Comments
 (0)