Created
January 25, 2023 05:54
-
-
Save ericksli/f5d4047c920de4a510fe0cce581a8d85 to your computer and use it in GitHub Desktop.
Android 13 Per-app Language Preferences
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.swiftzer.metroride.common | |
enum class AppLocale { | |
SystemDefault, | |
English, | |
Chinese, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.swiftzer.metroride.common | |
import java.util.* | |
enum class EquivalentAppLocale(val locale: Locale) { | |
English( | |
locale = Locale.Builder() | |
.setLanguage("en") | |
.setRegion("HK") | |
.build(), | |
), | |
Chinese( | |
locale = Locale.Builder() | |
.setLanguage("zh") | |
.setRegion("HK") | |
.setScript("Hant") | |
.build(), | |
), | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.swiftzer.metroride.local.system | |
import android.content.BroadcastReceiver | |
import android.content.Context | |
import android.content.Intent | |
import dagger.hilt.android.AndroidEntryPoint | |
import net.swiftzer.metroride.data.system.LocaleDataSource | |
import javax.inject.Inject | |
@AndroidEntryPoint | |
class LocaleChangedBroadcastReceiver : BroadcastReceiver() { | |
@Inject | |
lateinit var localeDataSource: LocaleDataSource | |
override fun onReceive(context: Context, intent: Intent) { | |
if (intent.action != Intent.ACTION_LOCALE_CHANGED) return | |
localeDataSource.fetchAppLocale() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.swiftzer.metroride.data.system | |
import kotlinx.coroutines.flow.Flow | |
import net.swiftzer.metroride.common.AppLocale | |
import net.swiftzer.metroride.common.EquivalentAppLocale | |
interface LocaleDataSource { | |
val appLocale: Flow<AppLocale> | |
val equivalentAppLocale: Flow<EquivalentAppLocale> | |
fun fetchAppLocale() | |
fun updateLocale(locale: AppLocale) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.swiftzer.metroride.local.system | |
import android.content.res.Resources | |
import androidx.appcompat.app.AppCompatDelegate | |
import androidx.core.os.ConfigurationCompat | |
import androidx.core.os.LocaleListCompat | |
import kotlinx.coroutines.flow.Flow | |
import kotlinx.coroutines.flow.MutableSharedFlow | |
import kotlinx.coroutines.flow.distinctUntilChanged | |
import kotlinx.coroutines.flow.map | |
import net.swiftzer.metroride.common.AppLocale | |
import net.swiftzer.metroride.common.EquivalentAppLocale | |
import net.swiftzer.metroride.data.system.LocaleDataSource | |
import javax.inject.Inject | |
import javax.inject.Singleton | |
@Singleton | |
class LocaleDataSourceImpl @Inject constructor() : LocaleDataSource { | |
private val _appLocale = MutableSharedFlow<AppLocale>(replay = 1) | |
override val appLocale: Flow<AppLocale> = _appLocale.distinctUntilChanged() | |
override val equivalentAppLocale: Flow<EquivalentAppLocale> = _appLocale | |
.map(::resolveSystemLocale) | |
.distinctUntilChanged() | |
override fun fetchAppLocale() { | |
val currentLocale = AppCompatDelegate.getApplicationLocales() | |
val appLocale = when { | |
currentLocale.toLanguageTags().startsWith("zh") -> AppLocale.Chinese | |
currentLocale.toLanguageTags().startsWith("en") -> AppLocale.English | |
else -> AppLocale.SystemDefault | |
} | |
_appLocale.tryEmit(appLocale) | |
} | |
private fun resolveSystemLocale(appLocale: AppLocale): EquivalentAppLocale = when (appLocale) { | |
AppLocale.SystemDefault -> { | |
val systemLocale = | |
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0]!! | |
when { | |
systemLocale.language == "zh" && systemLocale.script == "Hant" -> EquivalentAppLocale.Chinese | |
else -> EquivalentAppLocale.English | |
} | |
} | |
AppLocale.English -> EquivalentAppLocale.English | |
AppLocale.Chinese -> EquivalentAppLocale.Chinese | |
} | |
override fun updateLocale(locale: AppLocale) { | |
val localeList = when (locale) { | |
AppLocale.SystemDefault -> LocaleListCompat.getEmptyLocaleList() | |
AppLocale.English -> LocaleListCompat.forLanguageTags("en") | |
AppLocale.Chinese -> LocaleListCompat.forLanguageTags("zh-Hant-HK") | |
} | |
_appLocale.tryEmit(locale) | |
AppCompatDelegate.setApplicationLocales(localeList) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment