parent
a9d5994070
commit
f79f922838
|
@ -68,7 +68,9 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.WireguardAutoTunnel">
|
android:theme="@style/Theme.WireguardAutoTunnel"
|
||||||
|
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||||
|
>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package com.zaneschepke.wireguardautotunnel
|
package com.zaneschepke.wireguardautotunnel
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
|
||||||
import android.os.StrictMode
|
import android.os.StrictMode
|
||||||
import android.os.StrictMode.ThreadPolicy
|
import android.os.StrictMode.ThreadPolicy
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
import com.zaneschepke.logcatter.LogReader
|
import com.zaneschepke.logcatter.LogReader
|
||||||
import com.zaneschepke.wireguardautotunnel.data.datastore.LocaleStorage
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||||
|
@ -22,10 +22,6 @@ import javax.inject.Inject
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class WireGuardAutoTunnel : Application() {
|
class WireGuardAutoTunnel : Application() {
|
||||||
|
|
||||||
val localeStorage: LocaleStorage by lazy {
|
|
||||||
LocaleStorage(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ApplicationScope
|
@ApplicationScope
|
||||||
lateinit var applicationScope: CoroutineScope
|
lateinit var applicationScope: CoroutineScope
|
||||||
|
@ -56,6 +52,13 @@ class WireGuardAutoTunnel : Application() {
|
||||||
} else {
|
} else {
|
||||||
Timber.plant(ReleaseTree())
|
Timber.plant(ReleaseTree())
|
||||||
}
|
}
|
||||||
|
applicationScope.launch {
|
||||||
|
appStateRepository.getLocale()?.let {
|
||||||
|
val locale = LocaleUtil.getLocaleFromPrefCode(it)
|
||||||
|
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(locale)
|
||||||
|
AppCompatDelegate.setApplicationLocales(appLocale)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!isRunningOnTv()) {
|
if (!isRunningOnTv()) {
|
||||||
applicationScope.launch(ioDispatcher) {
|
applicationScope.launch(ioDispatcher) {
|
||||||
if (appStateRepository.isLocalLogsEnabled()) {
|
if (appStateRepository.isLocalLogsEnabled()) {
|
||||||
|
@ -66,10 +69,6 @@ class WireGuardAutoTunnel : Application() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
|
||||||
super.attachBaseContext(LocaleUtil.getLocalizedContext(base, LocaleStorage(base).getPreferredLocale()))
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var instance: WireGuardAutoTunnel
|
lateinit var instance: WireGuardAutoTunnel
|
||||||
private set
|
private set
|
||||||
|
|
|
@ -27,6 +27,7 @@ class DataStoreManager(
|
||||||
val pinLockEnabled = booleanPreferencesKey("PIN_LOCK_ENABLED")
|
val pinLockEnabled = booleanPreferencesKey("PIN_LOCK_ENABLED")
|
||||||
val tunnelStatsExpanded = booleanPreferencesKey("TUNNEL_STATS_EXPANDED")
|
val tunnelStatsExpanded = booleanPreferencesKey("TUNNEL_STATS_EXPANDED")
|
||||||
val isLocalLogsEnabled = booleanPreferencesKey("LOCAL_LOGS_ENABLED")
|
val isLocalLogsEnabled = booleanPreferencesKey("LOCAL_LOGS_ENABLED")
|
||||||
|
val locale = stringPreferencesKey("LOCALE")
|
||||||
val theme = stringPreferencesKey("THEME")
|
val theme = stringPreferencesKey("THEME")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.data.datastore
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
|
||||||
|
|
||||||
class LocaleStorage(context: Context) {
|
|
||||||
private var preferences: SharedPreferences = context.getSharedPreferences("sp", Context.MODE_PRIVATE)
|
|
||||||
|
|
||||||
fun getPreferredLocale(): String {
|
|
||||||
return preferences.getString("preferred_locale", LocaleUtil.OPTION_PHONE_LANGUAGE)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setPreferredLocale(localeCode: String) {
|
|
||||||
preferences.edit().putString("preferred_locale", localeCode).apply()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ data class GeneralState(
|
||||||
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
||||||
val isTunnelStatsExpanded: Boolean = IS_TUNNEL_STATS_EXPANDED,
|
val isTunnelStatsExpanded: Boolean = IS_TUNNEL_STATS_EXPANDED,
|
||||||
val isLocalLogsEnabled: Boolean = IS_LOGS_ENABLED_DEFAULT,
|
val isLocalLogsEnabled: Boolean = IS_LOGS_ENABLED_DEFAULT,
|
||||||
|
val locale: String? = null,
|
||||||
val theme: Theme = Theme.AUTOMATIC,
|
val theme: Theme = Theme.AUTOMATIC,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -33,5 +33,9 @@ interface AppStateRepository {
|
||||||
|
|
||||||
suspend fun setLocalLogsEnabled(enabled: Boolean)
|
suspend fun setLocalLogsEnabled(enabled: Boolean)
|
||||||
|
|
||||||
|
suspend fun setLocale(localeTag: String)
|
||||||
|
|
||||||
|
suspend fun getLocale(): String?
|
||||||
|
|
||||||
val generalStateFlow: Flow<GeneralState>
|
val generalStateFlow: Flow<GeneralState>
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,14 @@ class DataStoreAppStateRepository(
|
||||||
dataStoreManager.saveToDataStore(DataStoreManager.isLocalLogsEnabled, enabled)
|
dataStoreManager.saveToDataStore(DataStoreManager.isLocalLogsEnabled, enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun setLocale(localeTag: String) {
|
||||||
|
dataStoreManager.saveToDataStore(DataStoreManager.locale, localeTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getLocale(): String? {
|
||||||
|
return dataStoreManager.getFromStore(DataStoreManager.locale)
|
||||||
|
}
|
||||||
|
|
||||||
override val generalStateFlow: Flow<GeneralState> =
|
override val generalStateFlow: Flow<GeneralState> =
|
||||||
dataStoreManager.preferencesFlow.map { prefs ->
|
dataStoreManager.preferencesFlow.map { prefs ->
|
||||||
prefs?.let { pref ->
|
prefs?.let { pref ->
|
||||||
|
@ -93,6 +101,7 @@ class DataStoreAppStateRepository(
|
||||||
?: GeneralState.PIN_LOCK_ENABLED_DEFAULT,
|
?: GeneralState.PIN_LOCK_ENABLED_DEFAULT,
|
||||||
isTunnelStatsExpanded = pref[DataStoreManager.tunnelStatsExpanded] ?: GeneralState.IS_TUNNEL_STATS_EXPANDED,
|
isTunnelStatsExpanded = pref[DataStoreManager.tunnelStatsExpanded] ?: GeneralState.IS_TUNNEL_STATS_EXPANDED,
|
||||||
isLocalLogsEnabled = pref[DataStoreManager.isLocalLogsEnabled] ?: GeneralState.IS_LOGS_ENABLED_DEFAULT,
|
isLocalLogsEnabled = pref[DataStoreManager.isLocalLogsEnabled] ?: GeneralState.IS_LOGS_ENABLED_DEFAULT,
|
||||||
|
locale = pref[DataStoreManager.locale],
|
||||||
theme = getTheme(),
|
theme = getTheme(),
|
||||||
)
|
)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui
|
package com.zaneschepke.wireguardautotunnel.ui
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.wireguard.android.backend.WgQuickBackend
|
import com.wireguard.android.backend.WgQuickBackend
|
||||||
|
@ -15,6 +17,8 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil.OPTION_PHONE_LANGUAGE
|
||||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
@ -147,6 +151,14 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onLocaleChange(localeTag: String) = viewModelScope.launch {
|
||||||
|
val locale = LocaleUtil.getLocaleFromPrefCode(localeTag)
|
||||||
|
val storageLocale = if (localeTag == OPTION_PHONE_LANGUAGE) OPTION_PHONE_LANGUAGE else locale
|
||||||
|
appDataRepository.appState.setLocale(storageLocale)
|
||||||
|
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(locale)
|
||||||
|
AppCompatDelegate.setApplicationLocales(appLocale)
|
||||||
|
}
|
||||||
|
|
||||||
fun onToggleRestartAtBoot() = viewModelScope.launch {
|
fun onToggleRestartAtBoot() = viewModelScope.launch {
|
||||||
with(uiState.value.settings) {
|
with(uiState.value.settings) {
|
||||||
appDataRepository.settings.save(
|
appDataRepository.settings.save(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui
|
package com.zaneschepke.wireguardautotunnel.ui
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
@ -35,8 +34,6 @@ import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.toRoute
|
import androidx.navigation.toRoute
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.datastore.LocaleStorage
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
|
||||||
|
@ -59,7 +56,6 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.logs.LogsScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.support.logs.LogsScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme
|
import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate
|
import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -68,12 +64,6 @@ import kotlin.system.exitProcess
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val localeStorage: LocaleStorage by lazy {
|
|
||||||
(application as WireGuardAutoTunnel).localeStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var oldPrefLocaleCode: String
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var appStateRepository: AppStateRepository
|
lateinit var appStateRepository: AppStateRepository
|
||||||
|
|
||||||
|
@ -185,7 +175,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
AppearanceScreen()
|
AppearanceScreen()
|
||||||
}
|
}
|
||||||
composable<Route.Language> {
|
composable<Route.Language> {
|
||||||
LanguageScreen(localeStorage)
|
LanguageScreen(appUiState, viewModel)
|
||||||
}
|
}
|
||||||
composable<Route.Display> {
|
composable<Route.Display> {
|
||||||
DisplayScreen(appUiState)
|
DisplayScreen(appUiState)
|
||||||
|
@ -225,19 +215,4 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(newBase: Context) {
|
|
||||||
oldPrefLocaleCode = LocaleStorage(newBase).getPreferredLocale()
|
|
||||||
applyOverrideConfiguration(LocaleUtil.getLocalizedConfiguration(oldPrefLocaleCode))
|
|
||||||
super.attachBaseContext(newBase)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
val currentLocaleCode = LocaleStorage(this).getPreferredLocale()
|
|
||||||
if (oldPrefLocaleCode != currentLocaleCode) {
|
|
||||||
recreate() // locale is changed, restart the activity to update
|
|
||||||
oldPrefLocaleCode = currentLocaleCode
|
|
||||||
}
|
|
||||||
super.onResume()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
|
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
||||||
import kotlin.let
|
|
||||||
|
|
||||||
@androidx.compose.runtime.Composable
|
@androidx.compose.runtime.Composable
|
||||||
fun IconSurfaceButton(title: String, onClick: () -> Unit, selected: Boolean, leadingIcon: ImageVector? = null, description: String? = null) {
|
fun IconSurfaceButton(title: String, onClick: () -> Unit, selected: Boolean, leadingIcon: ImageVector? = null, description: String? = null) {
|
||||||
|
|
|
@ -90,8 +90,8 @@ fun ConfigScreen(tunnelId: Int) {
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
var configType by remember { mutableStateOf<ConfigType?>(null) }
|
var configType by remember { mutableStateOf<ConfigType?>(null) }
|
||||||
val derivedConfigType = remember {
|
val derivedConfigType = remember {
|
||||||
derivedStateOf<ConfigType> {
|
derivedStateOf {
|
||||||
configType ?: if (!uiState.hasAmneziaProperties()) ConfigType.WIREGUARD else ConfigType.AMNEZIA
|
configType ?: if (!uiState.isAmneziaEnabled) ConfigType.WIREGUARD else ConfigType.AMNEZIA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val saved by viewModel.saved.collectAsStateWithLifecycle(null)
|
val saved by viewModel.saved.collectAsStateWithLifecycle(null)
|
||||||
|
@ -181,8 +181,8 @@ fun ConfigScreen(tunnelId: Int) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) { padding ->
|
||||||
Column(Modifier.padding(it)) {
|
Column(Modifier.padding(padding)) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
|
@ -243,12 +243,12 @@ fun ConfigScreen(tunnelId: Int) {
|
||||||
.clickable { showAuthPrompt = true },
|
.clickable { showAuthPrompt = true },
|
||||||
value = uiState.interfaceProxy.privateKey,
|
value = uiState.interfaceProxy.privateKey,
|
||||||
visualTransformation =
|
visualTransformation =
|
||||||
if ((tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID.toInt()) || isAuthenticated) {
|
if ((tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated) {
|
||||||
VisualTransformation.None
|
VisualTransformation.None
|
||||||
} else {
|
} else {
|
||||||
PasswordVisualTransformation()
|
PasswordVisualTransformation()
|
||||||
},
|
},
|
||||||
enabled = (tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID.toInt()) || isAuthenticated,
|
enabled = (tunnelId == Constants.MANUAL_TUNNEL_CONFIG_ID) || isAuthenticated,
|
||||||
onValueChange = { value -> viewModel.onPrivateKeyChange(value) },
|
onValueChange = { value -> viewModel.onPrivateKeyChange(value) },
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|
|
@ -18,9 +18,6 @@ data class ConfigUiState(
|
||||||
var tunnelName: String = "",
|
var tunnelName: String = "",
|
||||||
val isAmneziaEnabled: Boolean = false,
|
val isAmneziaEnabled: Boolean = false,
|
||||||
) {
|
) {
|
||||||
fun hasAmneziaProperties(): Boolean {
|
|
||||||
return this.interfaceProxy.junkPacketCount != ""
|
|
||||||
}
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(config: Config): ConfigUiState {
|
fun from(config: Config): ConfigUiState {
|
||||||
val proxyPeers = config.peers.map { PeerProxy.from(it) }
|
val proxyPeers = config.peers.map { PeerProxy.from(it) }
|
||||||
|
@ -77,6 +74,7 @@ data class ConfigUiState(
|
||||||
return from(config).copy(
|
return from(config).copy(
|
||||||
tunnelName = tunnel.name,
|
tunnelName = tunnel.name,
|
||||||
tunnel = tunnel,
|
tunnel = tunnel,
|
||||||
|
isAmneziaEnabled = config.`interface`.junkPacketCount.isPresent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,39 +11,27 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.data.datastore.LocaleStorage
|
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.SelectedLabel
|
import com.zaneschepke.wireguardautotunnel.ui.common.SelectedLabel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SelectionItemButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.SelectionItemButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
|
||||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.navigateAndForget
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
||||||
import timber.log.Timber
|
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LanguageScreen(localeStorage: LocaleStorage) {
|
fun LanguageScreen(appUiState: AppUiState, appViewModel: AppViewModel) {
|
||||||
val navController = LocalNavController.current
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
val collator = Collator.getInstance(Locale.getDefault())
|
val collator = Collator.getInstance(Locale.getDefault())
|
||||||
|
|
||||||
val currentLocale = remember { mutableStateOf(LocaleUtil.OPTION_PHONE_LANGUAGE) }
|
|
||||||
|
|
||||||
val locales = LocaleUtil.supportedLocales.map {
|
val locales = LocaleUtil.supportedLocales.map {
|
||||||
val tag = it.replace("_", "-")
|
val tag = it.replace("_", "-")
|
||||||
Locale.forLanguageTag(tag)
|
Locale.forLanguageTag(tag)
|
||||||
|
@ -54,28 +42,17 @@ fun LanguageScreen(localeStorage: LocaleStorage) {
|
||||||
locales.sortedWith(compareBy(collator) { it.getDisplayName(it) }).toList()
|
locales.sortedWith(compareBy(collator) { it.getDisplayName(it) }).toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
currentLocale.value = localeStorage.getPreferredLocale()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onChangeLocale(locale: String) {
|
|
||||||
Timber.d("Setting preferred locale: $locale")
|
|
||||||
localeStorage.setPreferredLocale(locale)
|
|
||||||
LocaleUtil.applyLocalizedContext(context, locale)
|
|
||||||
navController.navigateAndForget(Route.Main)
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopNavBar(stringResource(R.string.language))
|
TopNavBar(stringResource(R.string.language))
|
||||||
},
|
},
|
||||||
) {
|
) { padding ->
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize().padding(it)
|
.fillMaxSize().padding(padding)
|
||||||
.padding(horizontal = 24.dp.scaledWidth()).windowInsetsPadding(WindowInsets.navigationBars),
|
.padding(horizontal = 24.dp.scaledWidth()).windowInsetsPadding(WindowInsets.navigationBars),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
|
@ -83,10 +60,10 @@ fun LanguageScreen(localeStorage: LocaleStorage) {
|
||||||
SelectionItemButton(
|
SelectionItemButton(
|
||||||
buttonText = stringResource(R.string.automatic),
|
buttonText = stringResource(R.string.automatic),
|
||||||
onClick = {
|
onClick = {
|
||||||
onChangeLocale(LocaleUtil.OPTION_PHONE_LANGUAGE)
|
appViewModel.onLocaleChange(LocaleUtil.OPTION_PHONE_LANGUAGE)
|
||||||
},
|
},
|
||||||
trailing = {
|
trailing = {
|
||||||
if (currentLocale.value == LocaleUtil.OPTION_PHONE_LANGUAGE) {
|
if (appUiState.generalState.locale == LocaleUtil.OPTION_PHONE_LANGUAGE) {
|
||||||
SelectedLabel()
|
SelectedLabel()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -96,13 +73,18 @@ fun LanguageScreen(localeStorage: LocaleStorage) {
|
||||||
}
|
}
|
||||||
items(sortedLocales, key = { it }) { locale ->
|
items(sortedLocales, key = { it }) { locale ->
|
||||||
SelectionItemButton(
|
SelectionItemButton(
|
||||||
buttonText = locale.getDisplayLanguage(locale).capitalize(locale) +
|
buttonText = locale.getDisplayLanguage(locale).replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() } +
|
||||||
if (locale.toLanguageTag().contains("-")) " (${locale.getDisplayCountry(locale).capitalize(locale)})" else "",
|
if (locale.toLanguageTag().contains("-")) {
|
||||||
|
" (${locale.getDisplayCountry(locale)
|
||||||
|
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }})"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
onChangeLocale(locale.toLanguageTag())
|
appViewModel.onLocaleChange(locale.toLanguageTag())
|
||||||
},
|
},
|
||||||
trailing = {
|
trailing = {
|
||||||
if (locale.toLanguageTag() == currentLocale.value) {
|
if (locale.toLanguageTag() == appUiState.generalState.locale) {
|
||||||
SelectedLabel()
|
SelectedLabel()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.util
|
package com.zaneschepke.wireguardautotunnel.util
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Build
|
|
||||||
import android.os.LocaleList
|
|
||||||
import androidx.core.os.ConfigurationCompat
|
import androidx.core.os.ConfigurationCompat
|
||||||
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
object LocaleUtil {
|
object LocaleUtil {
|
||||||
private const val DEFAULT_LANG = "en"
|
private const val DEFAULT_LANG = "en"
|
||||||
|
@ -19,7 +14,7 @@ object LocaleUtil {
|
||||||
* when preference value = "sys_def" returns the locale of current system
|
* when preference value = "sys_def" returns the locale of current system
|
||||||
* else it returns the locale code e.g. "en", "bn" etc.
|
* else it returns the locale code e.g. "en", "bn" etc.
|
||||||
*/
|
*/
|
||||||
fun getLocaleFromPrefCode(prefCode: String): Locale {
|
fun getLocaleFromPrefCode(prefCode: String): String {
|
||||||
val localeCode = if (prefCode != OPTION_PHONE_LANGUAGE) {
|
val localeCode = if (prefCode != OPTION_PHONE_LANGUAGE) {
|
||||||
prefCode
|
prefCode
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,71 +25,6 @@ object LocaleUtil {
|
||||||
DEFAULT_LANG
|
DEFAULT_LANG
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Locale.forLanguageTag(localeCode)
|
return localeCode
|
||||||
}
|
|
||||||
|
|
||||||
fun getLocalizedConfiguration(prefLocaleCode: String): Configuration {
|
|
||||||
val locale = getLocaleFromPrefCode(prefLocaleCode)
|
|
||||||
return getLocalizedConfiguration(locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLocalizedConfiguration(locale: Locale): Configuration {
|
|
||||||
val config = Configuration()
|
|
||||||
return config.apply {
|
|
||||||
config.setLayoutDirection(locale)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
config.setLocale(locale)
|
|
||||||
val localeList = LocaleList(locale)
|
|
||||||
LocaleList.setDefault(localeList)
|
|
||||||
config.setLocales(localeList)
|
|
||||||
} else {
|
|
||||||
config.setLocale(locale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLocalizedContext(baseContext: Context, prefLocaleCode: String?): Context {
|
|
||||||
if (prefLocaleCode == null) return baseContext
|
|
||||||
val currentLocale = getLocaleFromPrefCode(prefLocaleCode)
|
|
||||||
val baseLocale = getLocaleFromConfiguration(baseContext.resources.configuration)
|
|
||||||
Locale.setDefault(currentLocale)
|
|
||||||
return if (!baseLocale.toString().equals(currentLocale.toString(), ignoreCase = true)) {
|
|
||||||
val config = getLocalizedConfiguration(currentLocale)
|
|
||||||
baseContext.createConfigurationContext(config)
|
|
||||||
baseContext
|
|
||||||
} else {
|
|
||||||
baseContext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun applyLocalizedContext(baseContext: Context, prefLocaleCode: String) {
|
|
||||||
val currentLocale = getLocaleFromPrefCode(prefLocaleCode)
|
|
||||||
val baseLocale = getLocaleFromConfiguration(baseContext.resources.configuration)
|
|
||||||
Locale.setDefault(currentLocale)
|
|
||||||
if (!baseLocale.toString().equals(currentLocale.toString(), ignoreCase = true)) {
|
|
||||||
val config = getLocalizedConfiguration(currentLocale)
|
|
||||||
baseContext.resources.updateConfiguration(config, baseContext.resources.displayMetrics)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
private fun getLocaleFromConfiguration(configuration: Configuration): Locale {
|
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
configuration.locales.get(0)
|
|
||||||
} else {
|
|
||||||
configuration.locale
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLocalizedResources(resources: Resources, prefLocaleCode: String): Resources {
|
|
||||||
val locale = getLocaleFromPrefCode(prefLocaleCode)
|
|
||||||
val config = resources.configuration
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
config.locale = locale
|
|
||||||
config.setLayoutDirection(locale)
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
resources.updateConfiguration(config, resources.displayMetrics)
|
|
||||||
return resources
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue