У меня есть приложение с многоязычной поддержкой, вы можете установить свой язык по своему усмотрению, но
я хочу вызвать локаль прямо из навигатора, чтобы пользователь может установить язык приложения прямо из навигатора, не переходя к предпочтениям
Вот локаль
object LocaleUtils {
fun getLocalesUsedInProject(context: Context, projectLocales: Array<String>, defaultLocaleText:
String): LocalePair {
val localesEntry = arrayOfNulls<String>(projectLocales.size)
for (i in projectLocales.indices) {
val localesEntryValue = projectLocales[i]
val locale = getLocaleFromString(localesEntryValue)
val displayLanguage = locale.getDisplayLanguage(locale)
val displayCountry = locale.getDisplayCountry(locale)
if (displayCountry.isEmpty()) {
localesEntry[i] = displayLanguage.firstLetterUppercase()
} else {
localesEntry[i] = "${displayLanguage.firstLetterUppercase()} -
${displayCountry.firstLetterUppercase()}"
}
}
//sort
val localeTreeMap = TreeMap<String, String>()
for (i in projectLocales.indices) {
localeTreeMap[localesEntry[i]!!] = projectLocales[i]
}
val finalLocaleEntries = ArrayList<String>(localeTreeMap.size + 1).apply { add(0,
defaultLocaleText) }
val finalLocaleEntryValues = ArrayList<String>(localeTreeMap.size + 1).apply { add(0, "") }
var i = 1
for ((key, value) in localeTreeMap) {
finalLocaleEntries.add(i, key)
finalLocaleEntryValues.add(i, value)
i++
}
return LocalePair(finalLocaleEntries.toTypedArray(), finalLocaleEntryValues.toTypedArray())
}
private fun getLocaleFromString(string: String): Locale {
/**
* See [android.content.res.AssetManager.getLocales]
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return Locale.forLanguageTag(string)
}
//Best effort on determining the locale
val separators = arrayOf("_", "-")
for (separator in separators) {
//see if there is a language and a country
if (string.contains(separator)) {
val splittedLocale = string.split(separator.toRegex()).dropLastWhile { it.isEmpty()
}.toTypedArray()
if (splittedLocale.size == 2) {
return Locale(splittedLocale[0], splittedLocale[1])
}
}
}
return Locale(string)
}
}
class LocalePair(val localeEntries: Array<String>, val localeEntryValues: Array<String>)
@Suppress("DEPRECATION")
fun ContextWrapper.wrap(language: String): ContextWrapper {
val config = baseContext.resources.configuration
val sysLocale: Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.getSystemLocale()
} else {
this.getSystemLocaleLegacy()
}
if (language.isNotEmpty() && sysLocale.language != language) {
val locale = Locale(language)
Locale.setDefault(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.setSystemLocale(locale)
} else {
this.setSystemLocaleLegacy(locale)
}
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val context = baseContext.createConfigurationContext(config)
ContextWrapper(context)
} else {
baseContext.resources.updateConfiguration(config, baseContext.resources.displayMetrics)
ContextWrapper(baseContext)
}
}
@Suppress("DEPRECATION")
fun ContextWrapper.getSystemLocaleLegacy(): Locale = baseContext.resources.configuration.locale
@TargetApi(Build.VERSION_CODES.N)
fun ContextWrapper.getSystemLocale(): Locale = baseContext.resources.configuration.locales[0]
@Suppress("DEPRECATION")
fun Context.getLocaleLanguages(): List<String> {
val locales = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val locales = ArrayList<Locale>()
for (i in 0 until resources.configuration.locales.size()) {
locales.add(resources.configuration.locales.get(i))
}
locales
} else arrayListOf(resources.configuration.locale)
return locales.map { it.language }
}
@Suppress("DEPRECATION")
fun ContextWrapper.setSystemLocaleLegacy(locale: Locale) {
baseContext.resources.configuration.locale = locale
}
@TargetApi(Build.VERSION_CODES.N)
fun ContextWrapper.setSystemLocale(locale: Locale) {
baseContext.resources.configuration.setLocale(locale)
}
fun Context.getContextWithLocale(appLocale: String?): Context {
appLocale.takeIf { !it.isNullOrEmpty() }?.let {
return ContextWrapper(this).wrap(it)
}
return this
}
Действие PrefrenceUI
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
class PreferencesUi : BasePreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
override fun getXml() = R.xml.preferences_ui
override fun getTitleId() = R.string.interface_prefs_screen
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
prepareLocaleList()
setupTheme()
}
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
super.onCreatePreferences(bundle, s)
val groupVideoPreference = preferenceManager.findPreference<EditTextPreference>
("video_group_size")
groupVideoPreference?.setOnBindEditTextListener { editText ->
editText.inputType = InputType.TYPE_CLASS_NUMBER
}
groupVideoPreference?.summaryProvider = Preference.SummaryProvider<EditTextPreference> {
preference ->
val text = preference.text
if (TextUtils.isEmpty(text)) {
""
} else {
getString(R.string.video_group_size_summary, text)
}
}
}
private fun setupTheme() {
val prefs = preferenceScreen.sharedPreferences
if (!prefs.contains(KEY_APP_THEME)) {
var theme = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
if (prefs.getBoolean(KEY_DAYNIGHT, false) && !AndroidDevices.canUseSystemNightMode()) {
theme = AppCompatDelegate.MODE_NIGHT_AUTO
} else if (prefs.contains(KEY_BLACK_THEME)) {
theme = if (prefs.getBoolean(KEY_BLACK_THEME, false))
AppCompatDelegate.MODE_NIGHT_YES
else
AppCompatDelegate.MODE_NIGHT_NO
}
prefs.edit().putString(KEY_APP_THEME, theme.toString()).apply()
}
}
override fun onStart() {
super.onStart()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onStop() {
super.onStop()
preferenceScreen.sharedPreferences
.unregisterOnSharedPreferenceChangeListener(this)
}
override fun onPreferenceTreeClick(preference: Preference): Boolean {
if (preference.key == null) return false
when (preference.key) {
PREF_TV_UI -> {
Settings.tvUI = (preference as TwoStatePreference).isChecked
(activity as PreferencesActivity).setRestartApp()
return true
}
SHOW_VIDEO_THUMBNAILS -> {
Settings.showVideoThumbs = (preference as TwoStatePreference).isChecked
(activity as PreferencesActivity).setRestart()
return true
}
"media_seen" -> requireActivity().setResult(RESULT_UPDATE_SEEN_MEDIA)
KEY_ARTISTS_SHOW_ALL -> (activity as PreferencesActivity).updateArtists()
}
return super.onPreferenceTreeClick(preference)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
"set_locale" -> {
(activity as PreferencesActivity).setRestart()
UiTools.restartDialog(requireActivity())
}
"browser_show_all_files", "video_min_group_length" -> (activity as
PreferencesActivity).setRestart()
KEY_APP_THEME -> (activity as PreferencesActivity).exitAndRescan()
LIST_TITLE_ELLIPSIZE -> {
Settings.listTitleEllipsize = sharedPreferences.getString(LIST_TITLE_ELLIPSIZE,
"0")?.toInt() ?: 0
(activity as PreferencesActivity).setRestart()
}
"video_group_size" -> {
val goupSizeValue = try {
Settings.getInstance(requireActivity()).getString(key, "6")?.toInt() ?: 6
} catch (e: NumberFormatException) {
6
}
Medialibrary.getInstance().setVideoGroupsPrefixLength(goupSizeValue)
(activity as PreferencesActivity).setRestart()
}
}
}
private fun prepareLocaleList() {
val localePair = LocaleUtils.getLocalesUsedInProject(requireActivity(),
BuildConfig.TRANSLATION_ARRAY, getString(R.string.device_default))
val lp = findPreference<ListPreference>("set_locale")
lp?.entries = localePair.localeEntries
lp?.entryValues = localePair.localeEntryValues
}
}
Навигатор
class Navigator: NavigationView.OnNavigationItemSelectedListener, LifecycleObserver, INavigator {
private val defaultFragmentId= R.id.nav_video
override var currentFragmentId : Int = 0
var currentFragment: Fragment? = null
private set
private lateinit var activity: MainActivity
private lateinit var settings: SharedPreferences
private var extensionsService: ExtensionManagerService? = null
override lateinit var navigationView: NavigationView
override lateinit var drawerLayout: HackyDrawerLayout
override lateinit var drawerToggle: ActionBarDrawerToggle
override lateinit var extensionsManager: ExtensionsManager
override var extensionServiceConnection: ServiceConnection? = null
override var extensionManagerService: ExtensionManagerService? = null
private val isExtensionServiceBinded: Boolean
get() = extensionServiceConnection != null
override fun MainActivity.setupNavigation(state: Bundle?) {
activity = this
this@Navigator.settings = settings
currentFragmentId = intent.getIntExtra(EXTRA_TARGET, 0)
if (state !== null) {
currentFragment = supportFragmentManager.getFragment(state, "current_fragment")
} else {
if (intent.getBooleanExtra(EXTRA_UPGRADE, false)) {
/*
* The sliding menu is automatically opened when the user closes
* the info dialog. If (for any reason) the dialog is not shown,
* open the menu after a short delay.
*/
lifecycleScope.launchWhenStarted {
delay(500L)
drawerLayout.openDrawer(navigationView)
}
}
}
lifecycle.addObserver(this@Navigator)
navigationView = findViewById(R.id.navigation)
navigationView.menu.findItem(R.id.nav_history).isVisible = settings.getBoolean(PLAYBACK_HISTORY,
true)
drawerLayout = findViewById(R.id.root_container)
drawerToggle = ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open,
R.string.drawer_close)
drawerLayout.addDrawerListener(drawerToggle)
drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START)
val headerView = navigationView.getHeaderView(0)
if (BuildConfig.DEBUG) {
headerView.findViewById<TextView>(R.id.nav_header_title).text =
"${getString(R.string.app_name)} - ${BuildConfig.VERSION_NAME}"
}
headerView.findViewById<ImageView>(R.id.nav_header_background).setOnClickListener {
showSecondaryFragment(SecondaryActivity.ABOUT)
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
if (currentFragment === null && !currentIdIsExtension()) showFragment(if (currentFragmentId != 0)
currentFragmentId else settings.getInt("fragment_id", defaultFragmentId))
navigationView.setNavigationItemSelectedListener(this)
if (BuildConfig.DEBUG) createExtensionServiceConnection()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
navigationView.setNavigationItemSelectedListener(null)
if (isExtensionServiceBinded) {
activity.unbindService(extensionServiceConnection!!)
extensionServiceConnection = null
}
if (currentIdIsExtension())
settings.edit()
.putString("current_extension_name",
extensionsManager.getExtensions(activity.application, false)
[currentFragmentId].componentName().packageName)
.apply()
}
private fun getNewFragment(id: Int): Fragment {
return when (id) {
R.id.nav_audio -> AudioBrowserFragment()
R.id.nav_directories -> FileBrowserFragment()
R.id.nav_playlists -> PlaylistFragment()
R.id.nav_history -> HistoryFragment()
R.id.nav_network -> NetworkBrowserFragment()
R.id.nav_mrl -> MRLPanelFragment()
R.id.nav_about -> AboutFragment()
R.id.nav_whatsapp -> TabActivity()
R.id.nav_language -> LanguageNav()
else -> {
val group =
Integer.valueOf(Settings.getInstance(activity.applicationContext).
getString("video_min_group_length",
"-1")!!)
when {
group > 0 -> VideoGridFragment().apply {
arguments = Bundle(1).apply {
putSerializable(KEY_GROUPING, VideoGroupingType.NAME)
}
}
group == 0 -> VideoGridFragment().apply {
arguments = Bundle(1).apply {
putSerializable(KEY_GROUPING, VideoGroupingType.FOLDER)
}
}
else -> VideoGridFragment()
}
}
}
}
private fun showFragment(id: Int) {
val tag = getTag(id)
val fragment = getNewFragment(id)
showFragment(fragment, id, tag)
}
override fun forceLoadVideoFragment() {
showFragment(R.id.nav_video)
}
private fun showFragment(fragment: Fragment, id: Int, tag: String = getTag(id)) {
val fm = activity.supportFragmentManager
if (currentFragment is BaseBrowserFragment) fm.popBackStackImmediate("root",
FragmentManager.POP_BACK_STACK_INCLUSIVE)
val ft = fm.beginTransaction()
ft.replace(R.id.fragment_placeholder, fragment, tag)
if (BuildConfig.DEBUG) ft.commit()
else ft.commitAllowingStateLoss()
updateCheckedItem(id)
currentFragment = fragment
currentFragmentId = id
}
private fun showSecondaryFragment(fragmentTag: String, param: String? = null) {
val i = Intent(activity, SecondaryActivity::class.java)
i.putExtra("fragment", fragmentTag)
param?.let { i.putExtra("param", it) }
activity.startActivityForResult(i, SecondaryActivity.ACTIVITY_RESULT_SECONDARY)
// Slide down the audio player if needed.
activity.slideDownAudioPlayer()
}
override fun currentIdIsExtension() = idIsExtension(currentFragmentId)
private fun idIsExtension(id: Int) = id in 1..100
private fun clearBackstackFromClass(clazz: Class<*>) {
val fm = activity.supportFragmentManager
while (clazz.isInstance(currentFragment)) if (!fm.popBackStackImmediate()) break
}
override fun reloadPreferences() {
currentFragmentId = settings.getInt("fragment_id", defaultFragmentId)
}
override fun closeDrawer() {
drawerLayout.closeDrawer(navigationView)
}
private fun getTag(id: Int) = when (id) {
R.id.nav_settings -> ID_PREFERENCES
R.id.nav_audio -> ID_AUDIO
R.id.nav_playlists -> ID_PLAYLISTS
R.id.nav_directories -> ID_DIRECTORIES
R.id.nav_history -> ID_HISTORY
R.id.nav_mrl -> ID_MRL
R.id.nav_network -> ID_NETWORK
R.id.nav_about -> ID_ABOUT
R.id.nav_whatsapp ->ID_WHATSAPP
R.id.nav_language -> ID_lANGUAGE
else -> ID_VIDEO
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
val id = item.itemId
val current = currentFragment
if (item.groupId == R.id.extensions_group) {
if (currentFragmentId == id) {
clearBackstackFromClass(ExtensionBrowser::class.java)
activity.closeDrawer()
return false
} else
extensionsService?.openExtension(id)
} else {
if (isExtensionServiceBinded) extensionsService?.disconnect()
if (current == null) {
activity.closeDrawer()
return false
}
if (currentFragmentId == id) { /* Already selected */
// Go back at root level of current mProvider
if ((current as? BaseBrowserFragment)?.isStarted() == false) {
activity.supportFragmentManager.popBackStackImmediate("root",
FragmentManager.POP_BACK_STACK_INCLUSIVE)
} else {
activity.closeDrawer()
return false
}
} else when (id) {
R.id.nav_settings -> activity.startActivityForResult(Intent(activity,
PreferencesActivity::class.java), ACTIVITY_RESULT_PREFERENCES)
else -> {
activity.slideDownAudioPlayer()
showFragment(id)
}
}
}
activity.closeDrawer()
return true
}
override fun displayExtensionItems(extensionId: Int, title: String, items: List<VLCExtensionItem>,
showParams: Boolean, refresh: Boolean) {
if (refresh && currentFragment is ExtensionBrowser) {
(currentFragment as ExtensionBrowser).doRefresh(title, items)
} else {
val fragment = ExtensionBrowser()
fragment.arguments = Bundle().apply {
putParcelableArrayList(ExtensionBrowser.KEY_ITEMS_LIST, ArrayList(items))
putBoolean(ExtensionBrowser.KEY_SHOW_FAB, showParams)
putString(ExtensionBrowser.KEY_TITLE, title)
}
extensionsService?.let { fragment.setExtensionService(it) }
when {
currentFragment !is ExtensionBrowser -> //case: non-extension to extension root
showFragment(fragment, extensionId, title)
currentFragmentId == extensionId -> //case: extension root to extension sub dir
showFragment(fragment, extensionId, title)
else -> { //case: extension to other extension root
clearBackstackFromClass(ExtensionBrowser::class.java)
showFragment(fragment, extensionId, title)
}
}
}
navigationView.menu.findItem(extensionId).isCheckable = true
updateCheckedItem(extensionId)
}
private fun updateCheckedItem(id: Int) {
when (id) {
R.id.nav_settings -> return
else -> {
val currentId = currentFragmentId
val target = navigationView.menu.findItem(id)
if (id != currentId && target != null) {
val current = navigationView.menu.findItem(currentId)
if (current != null) current.isChecked = false
target.isChecked = true
/* Save the tab status in pref */
settings.edit().putInt("fragment_id", id).apply()
}
}
}
}
private fun loadPlugins() {
val plugins = extensionsManager.getExtensions(activity, true)
if (plugins.isEmpty()) {
extensionServiceConnection?.let { activity.unbindService(it) }
extensionServiceConnection = null
extensionManagerService?.stopSelf()
return
}
val extensionGroup = navigationView.menu.findItem(R.id.extensions_group)
extensionGroup.subMenu.clear()
for (id in plugins.indices) {
val extension = plugins[id]
val key = "extension_" + extension.componentName().packageName
if (settings.contains(key)) {
extensionsManager.displayPlugin(activity, id, extension, settings.getBoolean(key, false))
} else {
extensionsManager.showExtensionPermissionDialog(activity, id, extension, key)
}
}
if (extensionGroup.subMenu.size() == 0) extensionGroup.isVisible = false
onPluginsLoaded()
navigationView.invalidate()
}
private fun onPluginsLoaded() {
if (currentFragment == null && currentIdIsExtension())
if (extensionsManager.previousExtensionIsEnabled(activity.application))
extensionManagerService?.openExtension(currentFragmentId)
else
showFragment(R.id.nav_video)
}
private fun createExtensionServiceConnection() {
extensionServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
extensionManagerService = (service as ExtensionManagerService.LocalBinder).service.apply
{
setExtensionManagerActivity(activity)
}
loadPlugins()
}
override fun onServiceDisconnected(name: ComponentName) {}
}
// Bind service which discoverves au connects toplugins
if (!activity.bindService(Intent(activity,
ExtensionManagerService::class.java), extensionServiceConnection!!,
Context.BIND_AUTO_CREATE))
extensionServiceConnection = null
}
}
interface INavigator {
var navigationView: NavigationView
var currentFragmentId : Int
var drawerLayout: HackyDrawerLayout
var drawerToggle: ActionBarDrawerToggle
var extensionsManager: ExtensionsManager
var extensionServiceConnection: ServiceConnection?
var extensionManagerService: ExtensionManagerService?
fun MainActivity.setupNavigation(state: Bundle?)
fun currentIdIsExtension() : Boolean
fun displayExtensionItems(extensionId: Int, title: String, items: List<VLCExtensionItem>, showParams:
Boolean, refresh: Boolean)
fun reloadPreferences()
fun closeDrawer()
fun forceLoadVideoFragment()
вот чего я хочу достичь