Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ android {
lint {
lintConfig = file("lint.xml")
}

androidResources {
generateLocaleConfig true
}
}

allOpen {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,14 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
</application>
</manifest>
117 changes: 61 additions & 56 deletions app/src/main/kotlin/com/vrem/util/LocaleUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,80 +20,85 @@ package com.vrem.util
import java.util.Locale
import java.util.SortedMap

private object SyncAvoid {
val defaultLocale: Locale = Locale.getDefault()
val countryCodes: Set<String> = Locale.getISOCountries().toSet()
val availableLocales: List<Locale> = Locale.getAvailableLocales().filter { countryCodes.contains(it.country) }

val countriesLocales: SortedMap<String, Locale> =
availableLocales
.associateBy { it.country.toCapitalize(Locale.getDefault()) }
.toSortedMap()
val supportedLocales: List<Locale> =
setOf(
BULGARIAN,
DUTCH,
GREEK,
HUNGARIAN,
Locale.SIMPLIFIED_CHINESE,
Locale.TRADITIONAL_CHINESE,
Locale.ENGLISH,
Locale.FRENCH,
Locale.GERMAN,
Locale.ITALIAN,
Locale.JAPANESE,
POLISH,
PORTUGUESE_BRAZIL,
PORTUGUESE_PORTUGAL,
SPANISH,
RUSSIAN,
TURKISH,
UKRAINIAN,
defaultLocale,
).toList()
}
private val currentLocale: Locale get() = Locale.getDefault()
private val countryCodes: Set<String> = Locale.getISOCountries().toSet()
private val availableLocales: List<Locale> = Locale.getAvailableLocales().filter { countryCodes.contains(it.country) }
private val countriesLocales: SortedMap<String, Locale> =
availableLocales
.associateBy { it.country.toCapitalize(currentLocale) }
.toSortedMap()

val BULGARIAN: Locale = Locale.forLanguageTag("bg")
val CHINESE_SIMPLIFIED: Locale = Locale.forLanguageTag("zh-Hans")
val CHINESE_TRADITIONAL: Locale = Locale.forLanguageTag("zh-Hant")
val DUTCH: Locale = Locale.forLanguageTag("nl")
val ENGLISH: Locale = Locale.forLanguageTag("en")
val FRENCH: Locale = Locale.forLanguageTag("fr")
val GERMAN: Locale = Locale.forLanguageTag("de")
val GREEK: Locale = Locale.forLanguageTag("el")
val HUNGARIAN: Locale = Locale.forLanguageTag("hu")
val ITALIAN: Locale = Locale.forLanguageTag("it")
val JAPANESE: Locale = Locale.forLanguageTag("ja")
val POLISH: Locale = Locale.forLanguageTag("pl")
val PORTUGUESE_PORTUGAL: Locale = Locale.forLanguageTag("pt-PT")
val PORTUGUESE_BRAZIL: Locale = Locale.forLanguageTag("pt-BR")
val SPANISH: Locale = Locale.forLanguageTag("es")
val PORTUGUESE_PORTUGAL: Locale = Locale.forLanguageTag("pt-PT")
val RUSSIAN: Locale = Locale.forLanguageTag("ru")
val SPANISH: Locale = Locale.forLanguageTag("es")
val TURKISH: Locale = Locale.forLanguageTag("tr")
val UKRAINIAN: Locale = Locale.forLanguageTag("uk")

private const val SEPARATOR: String = "_"
val baseSupportedLocales: List<Locale> =
listOf(
BULGARIAN,
CHINESE_SIMPLIFIED,
CHINESE_TRADITIONAL,
DUTCH,
ENGLISH,
FRENCH,
GERMAN,
GREEK,
HUNGARIAN,
ITALIAN,
JAPANESE,
POLISH,
PORTUGUESE_BRAZIL,
PORTUGUESE_PORTUGAL,
RUSSIAN,
SPANISH,
TURKISH,
UKRAINIAN,
)

fun findByCountryCode(countryCode: String): Locale =
SyncAvoid.availableLocales.firstOrNull { countryCode.toCapitalize(Locale.getDefault()) == it.country }
?: SyncAvoid.defaultLocale
availableLocales.firstOrNull { countryCode.uppercase(Locale.ROOT) == it.country }
?: currentLocale

fun allCountries(): List<Locale> = SyncAvoid.countriesLocales.values.toList()
fun allCountries(): List<Locale> = countriesLocales.values.toList()

fun findByLanguageTag(languageTag: String): Locale {
val languageTagPredicate: (Locale) -> Boolean = {
val locale: Locale = fromLanguageTag(languageTag)
it.language == locale.language && it.country == locale.country
}
return SyncAvoid.supportedLocales.firstOrNull(languageTagPredicate) ?: SyncAvoid.defaultLocale
}
fun supportedLanguages(): List<Locale> = (baseSupportedLocales + currentLocale).distinct()

fun supportedLanguages(): List<Locale> = SyncAvoid.supportedLocales
fun supportedLanguageTags(): List<String> = listOf("") + baseSupportedLocales.map { it.toLanguageTag() }

fun defaultCountryCode(): String = SyncAvoid.defaultLocale.country
private fun normalizeLanguageTag(languageTag: String): String = languageTag.replace('_', '-').trim()

fun defaultLanguageTag(): String = toLanguageTag(SyncAvoid.defaultLocale)
fun findByLanguageTag(languageTag: String): Locale {
val normalizedLanguageTag = normalizeLanguageTag(languageTag)
if (normalizedLanguageTag.isEmpty()) return currentLocale

fun toLanguageTag(locale: Locale): String = locale.language + SEPARATOR + locale.country
val target = Locale.forLanguageTag(normalizedLanguageTag)
if (target.language.isEmpty()) return currentLocale

private fun fromLanguageTag(languageTag: String): Locale {
val codes: Array<String> = languageTag.split(SEPARATOR).toTypedArray()
return when (codes.size) {
1 -> Locale.forLanguageTag(codes[0])
2 -> Locale.forLanguageTag("${codes[0]}-${codes[1].toCapitalize(Locale.getDefault())}")
else -> SyncAvoid.defaultLocale
}
return baseSupportedLocales.find { it == target }
?: baseSupportedLocales.find { it.language == target.language && it.script == target.script }
?: baseSupportedLocales.find { it.language == target.language && it.country == target.country }
?: baseSupportedLocales.find { it.language == target.language }
?: currentLocale
}

fun currentCountryCode(): String = currentLocale.country

fun currentLanguageTag(): String = currentLocale.toLanguageTag()

fun toLanguageTag(locale: Locale): String = locale.toLanguageTag()

fun Locale.toSupportedLocaleTag(): String = findByLanguageTag(this.toLanguageTag()).toLanguageTag()
2 changes: 2 additions & 0 deletions app/src/main/kotlin/com/vrem/util/StringUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ fun String.Companion.nullToEmpty(value: String?): String = value ?: String.EMPTY
fun String.specialTrim(): String = this.trim { it <= ' ' }.replace(" +".toRegex(), String.SPACE_SEPARATOR)

fun String.toCapitalize(locale: Locale): String = this.replaceFirstChar { word -> word.uppercase(locale) }

fun String.titlecaseFirst(locale: Locale): String = replaceFirstChar { it.titlecase(locale) }
22 changes: 15 additions & 7 deletions app/src/main/kotlin/com/vrem/wifianalyzer/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package com.vrem.wifianalyzer

import android.content.Context
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.res.Configuration
Expand All @@ -26,18 +25,17 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
import com.vrem.annotation.OpenClass
import com.vrem.util.createContext
import com.vrem.wifianalyzer.navigation.NavigationMenu
import com.vrem.wifianalyzer.navigation.NavigationMenuControl
import com.vrem.wifianalyzer.navigation.NavigationMenuController
import com.vrem.wifianalyzer.navigation.options.OptionMenu
import com.vrem.wifianalyzer.settings.Repository
import com.vrem.wifianalyzer.settings.Settings
import com.vrem.wifianalyzer.wifi.accesspoint.ConnectionView
import com.vrem.wifianalyzer.wifi.scanner.ScannerService

Expand All @@ -52,15 +50,13 @@ class MainActivity :
internal lateinit var optionMenu: OptionMenu
internal lateinit var connectionView: ConnectionView

override fun attachBaseContext(newBase: Context) =
super.attachBaseContext(newBase.createContext(Settings(Repository(newBase)).languageLocale()))

override fun onCreate(savedInstanceState: Bundle?) {
val mainContext = MainContext.INSTANCE
mainContext.initialize(this, largeScreen)

val settings = mainContext.settings
settings.initializeDefaultValues()
settings.syncLanguage()
setTheme(settings.themeStyle().themeNoActionBar)

mainReload = MainReload(settings)
Expand Down Expand Up @@ -120,6 +116,18 @@ class MainActivity :
sharedPreferences: SharedPreferences,
key: String?,
) {
val languageKey = getString(R.string.language_key)
if (key == languageKey) {
val languageTag = sharedPreferences.getString(languageKey, "")
val locales =
languageTag
?.takeIf { it.isNotEmpty() }
?.let(LocaleListCompat::forLanguageTags)
?: LocaleListCompat.getEmptyLocaleList()

AppCompatDelegate.setApplicationLocales(locales)
}

val mainContext = MainContext.INSTANCE
if (mainReload.shouldReload(mainContext.settings)) {
MainContext.INSTANCE.scannerService.stop()
Expand Down
16 changes: 1 addition & 15 deletions app/src/main/kotlin/com/vrem/wifianalyzer/MainReload.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package com.vrem.wifianalyzer
import com.vrem.wifianalyzer.settings.Settings
import com.vrem.wifianalyzer.settings.ThemeStyle
import com.vrem.wifianalyzer.wifi.accesspoint.ConnectionViewType
import java.util.Locale

class MainReload(
settings: Settings,
Expand All @@ -29,11 +28,8 @@ class MainReload(
private set
var connectionViewType: ConnectionViewType
private set
var languageLocale: Locale
private set

fun shouldReload(settings: Settings): Boolean =
themeChanged(settings) || connectionViewTypeChanged(settings) || languageChanged(settings)
fun shouldReload(settings: Settings): Boolean = themeChanged(settings) || connectionViewTypeChanged(settings)

private fun connectionViewTypeChanged(settings: Settings): Boolean {
val currentConnectionViewType = settings.connectionViewType()
Expand All @@ -53,18 +49,8 @@ class MainReload(
return themeChanged
}

private fun languageChanged(settings: Settings): Boolean {
val settingLanguageLocale = settings.languageLocale()
val languageLocaleChanged = languageLocale != settingLanguageLocale
if (languageLocaleChanged) {
languageLocale = settingLanguageLocale
}
return languageLocaleChanged
}

init {
themeStyle = settings.themeStyle()
connectionViewType = settings.connectionViewType()
languageLocale = settings.languageLocale()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ package com.vrem.wifianalyzer.settings

import android.content.Context
import android.util.AttributeSet
import com.vrem.util.defaultCountryCode
import com.vrem.util.currentCountryCode
import com.vrem.wifianalyzer.MainContext
import com.vrem.wifianalyzer.wifi.band.WiFiChannelCountry
import java.util.Locale

private fun data(): List<Data> {
val currentLocale: Locale = MainContext.INSTANCE.settings.languageLocale()
val currentLocale: Locale = MainContext.INSTANCE.settings.appLocale()
return WiFiChannelCountry
.findAll()
.map { Data(it.countryCode, it.countryName(currentLocale)) }
Expand All @@ -35,4 +35,4 @@ private fun data(): List<Data> {
class CountryPreference(
context: Context,
attrs: AttributeSet,
) : CustomPreference(context, attrs, data(), defaultCountryCode())
) : CustomPreference(context, attrs, data(), currentCountryCode())
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ package com.vrem.wifianalyzer.settings

import android.content.Context
import android.util.AttributeSet
import com.vrem.util.defaultLanguageTag
import com.vrem.util.supportedLanguages
import com.vrem.util.toCapitalize
import com.vrem.util.toLanguageTag
import com.vrem.util.supportedLanguageTags
import com.vrem.util.titlecaseFirst
import com.vrem.wifianalyzer.R
import java.util.Locale

private fun data(): List<Data> =
supportedLanguages()
.map { map(it) }
.sorted()

private fun map(it: Locale): Data = Data(toLanguageTag(it), it.getDisplayName(it).toCapitalize(Locale.getDefault()))
private fun data(context: Context): List<Data> =
supportedLanguageTags().map { tag ->
if (tag.isEmpty()) {
Data("", context.getString(R.string.system_default))
} else {
val locale = Locale.forLanguageTag(tag)
Data(tag, locale.getDisplayName(locale).titlecaseFirst(locale))
}
}

class LanguagePreference(
context: Context,
attrs: AttributeSet,
) : CustomPreference(context, attrs, data(), defaultLanguageTag())
) : CustomPreference(context, attrs, data(context), "")
18 changes: 11 additions & 7 deletions app/src/main/kotlin/com/vrem/wifianalyzer/settings/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
package com.vrem.wifianalyzer.settings

import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.appcompat.app.AppCompatDelegate
import com.vrem.annotation.OpenClass
import com.vrem.util.buildMinVersionQ
import com.vrem.util.defaultCountryCode
import com.vrem.util.defaultLanguageTag
import com.vrem.util.currentCountryCode
import com.vrem.util.findByLanguageTag
import com.vrem.util.findOne
import com.vrem.util.findSet
import com.vrem.util.ordinals
import com.vrem.util.toSupportedLocaleTag
import com.vrem.wifianalyzer.R
import com.vrem.wifianalyzer.navigation.MAIN_NAVIGATION
import com.vrem.wifianalyzer.navigation.NavigationMenu
Expand Down Expand Up @@ -67,12 +68,15 @@ class Settings(

fun wiFiBand(wiFiBand: WiFiBand): Unit = repository.save(R.string.wifi_band_key, wiFiBand.ordinal)

fun countryCode(): String = repository.string(R.string.country_code_key, defaultCountryCode())
fun countryCode(): String = repository.string(R.string.country_code_key, currentCountryCode())

fun languageLocale(): Locale {
val defaultLanguageTag = defaultLanguageTag()
val languageTag = repository.string(R.string.language_key, defaultLanguageTag)
return findByLanguageTag(languageTag)
fun appLocale(): Locale = findByLanguageTag(AppCompatDelegate.getApplicationLocales().toLanguageTags())

fun syncLanguage() {
val appLocaleTag = appLocale().toSupportedLocaleTag()
if (appLocaleTag != repository.string(R.string.language_key, "")) {
repository.save(R.string.language_key, appLocaleTag)
}
}

fun sortBy(): SortBy = settingsFind(SortBy.entries, R.string.sort_by_key, SortBy.STRENGTH)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ class VendorService(
fun findVendorName(address: String = String.EMPTY): String = vendorData.macs[address.clean()].orEmpty()

fun findMacAddresses(vendorName: String = String.EMPTY): List<String> =
vendorData.vendors[vendorName.uppercase(Locale.getDefault())].orEmpty()
vendorData.vendors[vendorName.uppercase(Locale.ROOT)].orEmpty()

fun findVendors(vendorName: String = String.EMPTY): List<String> {
val name = vendorName.uppercase(Locale.getDefault())
val name = vendorName.uppercase(Locale.ROOT)
return vendorData.vendors
.filterKeys { filter(it, name) }
.keys
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class ChannelAvailableFragment : Fragment() {
private fun update() {
val settings = MainContext.INSTANCE.settings
val countryCode = settings.countryCode()
val languageLocale = settings.languageLocale()
val languageLocale = settings.appLocale()
binding.apply {
val textViews =
listOf(
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/resources.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unqualifiedResLocale=en-US
Loading