package com.fireworksdk.bridge.utils import android.app.Activity import android.content.Context import androidx.fragment.app.FragmentActivity import com.fireworksdk.bridge.components.base.FWBaseFragment import java.util.Locale class FWLanguageUtil private constructor(context: Context) { private val sharedPreferences by lazy { context.getSharedPreferences(LOCALE_SP, Context.MODE_PRIVATE) } private val initAppLocale: Locale = Locale.getDefault() /** * local string, e.g. ar, ar-AE, pt-BR */ private var localeString: String? = null init { localeString = sharedPreferences.getString(LOCALE_KEY, null) } fun getCurrentSettingLocale(): Locale { val localeLanguage = localeString if (localeLanguage.isNullOrBlank()) { return Locale(initAppLocale.language, initAppLocale.country) } else { if (localeLanguage.contains("-")) { val locales = localeLanguage.split("-") if (locales.size > 1) { return Locale(locales[0], locales[1]) } } return Locale(localeLanguage) } } fun shouldOverrideLanguage(language: String): Boolean { if (localeString.isNullOrBlank()) { // not update language in local string return false } return !localeString.equals(language, true) } fun updateBaseContextLocale(baseContext: Context): Context { val currentLocaleString = localeString if (currentLocaleString.isNullOrBlank()) { Locale.setDefault(initAppLocale) return updateResourcesLocale(baseContext, initAppLocale) } val locale = getLocaleByLanguage(currentLocaleString) Locale.setDefault(locale) return updateResourcesLocale(baseContext, locale) } private fun getLocaleByLanguage(language: String): Locale { if (language.contains("-")) { val locales = language.split("-") if (locales.size > 1) { return Locale(locales[0], locales[1]) } } return Locale(language) } private fun updateResourcesLocale(context: Context, locale: Locale): Context { val resources = context.resources val configuration = resources.configuration configuration.setLocale(locale) configuration.setLayoutDirection(locale) // [Fix: PiP overlay locale issue on Android 16] // Removed: updateConfiguration() is deprecated since API 25 and on Android 16 its behavior // is unreliable — it can corrupt the internal resource table of the Resources object. // The Configuration.locale updates correctly (hence RTL layout works), but the AssetManager's // resource table index may fail to switch to the target language's string pool, causing // string lookups to fallback to English. Only createConfigurationContext() should be used. // context.resources.updateConfiguration(configuration, context.resources.displayMetrics) return context.createConfigurationContext(configuration) } /** * return * true: need to execute change language. * false: don't need to execute change language. */ fun isSameLanguageWithCache(inputLocaleString: String?): Boolean { return (inputLocaleString.isNullOrBlank() && localeString.isNullOrBlank()) || localeString == inputLocaleString } fun changeLanguage(inputLocaleString: String?): Boolean { // no change if (isSameLanguageWithCache(inputLocaleString)) { return true } // clear local if empty input locale if (inputLocaleString.isNullOrBlank()) { localeString = null updateSpLanguage(inputLocaleString) return true } // invalid input locale if (!isValidLocale(inputLocaleString)) { return false } // update locale localeString = inputLocaleString updateSpLanguage(inputLocaleString) return true } private fun updateSpLanguage(l: String?) { if (l.isNullOrBlank()) { sharedPreferences.edit().apply { remove(LOCALE_KEY) }.apply() } else { sharedPreferences.edit().apply { putString(LOCALE_KEY, l) }.apply() } } fun restartActivity(activity: Activity?) { if (activity is FragmentActivity) { val fragments = activity.supportFragmentManager.fragments for (f in fragments) { if (f is FWBaseFragment && f.removeWhenRecreateActivity()) { FWFragmentUtil.removeFragment(activity, f) } } } activity?.recreate() } private fun isValidLocale(locale: String?): Boolean { locale ?: return false val list = Locale.getAvailableLocales() if (locale.contains("-")) { val locales = locale.split("-") if (locales.size > 1) { // zh-TW and zh-HK isn't a stander language and country config for locale, need script to be combined with /* if ("zh-TW".equals(locale, true) || "zh-HK".equals(locale, true)) { loc = Locale.Builder().setLocale(loc).setScript("Hant").build() } */ return if ("zh-TW".equals(locale, true) || "zh-HK".equals(locale, true)) { true } else { list.contains(Locale(locales[0], locales[1])) } } } return list.contains(Locale(locale)) } companion object : FWSingletonHolder(::FWLanguageUtil) { private const val LOCALE_SP = "fw_locale_sp" private const val LOCALE_KEY = "fw_locale_key" } }