Skip to content

Commit

Permalink
feat: Add language selector (#260)
Browse files Browse the repository at this point in the history
* feat: Add language selector

* fix: Fix LTR/RTL issue at startup

* fix: Try fix chinese

* fix: Use getDisplayName with locale

* fix: Setup page crash

* fix: Building on Windows

* feat: Move system language value to the ConstantUtil

* perf: Modifiy codes

* fix: Sort locales

* perf: Simplify ContextResWrapper

* fix: Add translation to repository classes and do not use title for storages anymore

* feat: Migrate to AndroidX support library

* Ref: https://developer.android.com/guide/topics/resources/app-languages#androidx-impl

* fix: Crash on API 32

---------

Co-authored-by: Xayah <zds1249475336@gmail.com>
  • Loading branch information
frknkrc44 and XayahSuSuSu authored Jul 10, 2024
1 parent 3eb7cb1 commit 14bdcb0
Show file tree
Hide file tree
Showing 38 changed files with 268 additions and 91 deletions.
21 changes: 21 additions & 0 deletions source/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ android {
versionName = libs.versions.versionName.get()

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

buildConfigField("String[]", "SUPPORTED_LOCALES", generateSupportedLocales())
}

// TODO Force enable the latest libsu
Expand Down Expand Up @@ -84,6 +86,25 @@ android {
}
}

fun generateSupportedLocales(): String {
val foundLocales = StringBuilder()
foundLocales.append("new String[]{")

fileTree("src/main/res").visit {
if(file.path.endsWith("strings.xml")){
var languageCode = file.parent.replace("\\", "/").split('/').last()
.replace("values-", "").replace("-r", "-")
if (languageCode == "values") {
languageCode = "en"
}
foundLocales.append("\"").append(languageCode).append("\"").append(",")
}
}

foundLocales.append("}")
return foundLocales.toString().replace(",}","}")
}

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
Expand Down
9 changes: 9 additions & 0 deletions source/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,14 @@
android:name=".MainActivity"
android:exported="false"
android:windowSoftInputMode="adjustResize" />

<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.xayah.databackup

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.ExperimentalLayoutApi
Expand Down Expand Up @@ -44,13 +44,14 @@ import com.xayah.feature.main.settings.about.PageAboutSettings
import com.xayah.feature.main.settings.about.PageTranslatorsSettings
import com.xayah.feature.main.settings.backup.PageBackupSettings
import com.xayah.feature.main.settings.blacklist.PageBlackList
import com.xayah.feature.main.settings.language.PageLanguageSelector
import com.xayah.feature.main.settings.restore.PageRestoreSettings
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
class MainActivity : AppCompatActivity() {
@ExperimentalCoroutinesApi
@ExperimentalAnimationApi
@ExperimentalFoundationApi
Expand Down Expand Up @@ -146,6 +147,9 @@ class MainActivity : ComponentActivity() {
composable(MainRoutes.RestoreSettings.route) {
PageRestoreSettings()
}
composable(MainRoutes.LanguageSettings.route) {
PageLanguageSelector()
}
composable(MainRoutes.BlackList.route) {
PageBlackList()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.xayah.databackup
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
Expand All @@ -16,7 +16,7 @@ import com.xayah.feature.setup.MainActivity as SetupActivity

@SuppressLint("CustomSplashScreen")
@AndroidEntryPoint
class SplashActivity : ComponentActivity() {
class SplashActivity : AppCompatActivity() {

// Workaround for HarmonyOS
@ExperimentalAnimationApi
Expand Down
1 change: 1 addition & 0 deletions source/app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,5 @@
<string name="disabled">禁用</string>
<string name="kill_app_options">杀死应用选项</string>
<string name="kill_app_options_desc">备份应用前行为</string>
<string name="language">语言</string>
</resources>
1 change: 1 addition & 0 deletions source/app/src/main/res/values-zh-rHK/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,5 @@
<string name="disabled">禁用</string>
<string name="kill_app_options">殺死應用選項</string>
<string name="kill_app_options_desc">備份應用前行爲</string>
<string name="language">語言</string>
</resources>
1 change: 1 addition & 0 deletions source/app/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,5 @@
<string name="disabled">禁用</string>
<string name="kill_app_options">殺死應用選項</string>
<string name="kill_app_options_desc">備份應用前行爲</string>
<string name="language">語言</string>
</resources>
1 change: 1 addition & 0 deletions source/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -348,4 +348,5 @@
<string name="disabled">Disabled</string>
<string name="kill_app_options">Killing app options</string>
<string name="kill_app_options_desc">Behavior before backup</string>
<string name="language">Language</string>
</resources>
2 changes: 1 addition & 1 deletion source/app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="Theme.DataBackup" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.DataBackup" parent="Theme.AppCompat.Light.NoActionBar" />

<style name="Theme.DataBackup.SplashScreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/ic_launcher_background</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ object BuildConfigUtil {
val VERSION_CODE = runCatching { fromBuildConfig("VERSION_CODE") as Int }.getOrDefault(0).toLong()
val FLAVOR_feature = runCatching { fromBuildConfig("FLAVOR_feature") as String }.getOrDefault("")
val FLAVOR_abi = runCatching { fromBuildConfig("FLAVOR_abi") as String }.getOrDefault("")
val SUPPORTED_LOCALES = runCatching { fromBuildConfig("SUPPORTED_LOCALES") as Array<String> }.getOrDefault(arrayOf())
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class DirectoryRepository @Inject constructor(
// Custom storage
val dir = DirectoryUpsertEntity(
id = directoryDao.queryId(parent = parent, child = child),
title = context.getString(R.string.custom_directory),
title = "",
parent = parent,
child = child,
storageType = StorageType.CUSTOM,
Expand Down Expand Up @@ -92,7 +92,7 @@ class DirectoryRepository @Inject constructor(
internalDirs.add(
DirectoryUpsertEntity(
id = directoryDao.queryId(parent = storageItem, child = child),
title = context.getString(R.string.internal_storage),
title = "",
parent = storageItem,
child = child,
storageType = StorageType.INTERNAL,
Expand All @@ -112,7 +112,7 @@ class DirectoryRepository @Inject constructor(
externalDirs.add(
DirectoryUpsertEntity(
id = directoryDao.queryId(parent = storageItem, child = child),
title = context.getString(R.string.external_storage),
title = "",
parent = storageItem,
child = child,
storageType = StorageType.EXTERNAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object ConstantUtil {
const val CONFIGURATIONS_KEY_FILE = "file"
const val FTP_ANONYMOUS_USERNAME = "anonymous" // https://www.rfc-editor.org/rfc/rfc1635
const val FTP_ANONYMOUS_PASSWORD = "guest"
const val LANGUAGE_SYSTEM = "auto"
val SupportedExternalStorageFormat = listOf(
"sdfat",
"fuseblk",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ val KeyBackupUserIdIndex = stringPreferencesKey("backup_user_id_index")
val KeyRestoreUserIdIndex = stringPreferencesKey("restore_user_id_index")
val KeyCustomSUFile = stringPreferencesKey("custom_su_file")
val KeyKillAppOption = stringPreferencesKey("kill_app_option")
val KeyLanguage = stringPreferencesKey("language")


// -----------------------------------------Read-----------------------------------------
Expand All @@ -37,6 +38,7 @@ fun Context.readUserIdList() = readStoreString(key = KeyUserIdList, defValue = "
fun Context.readBackupUserIdIndex() = readStoreString(key = KeyBackupUserIdIndex, defValue = "[0]").map { Gson().fromJson<List<Int>>(it, object : TypeToken<List<Int>>() {}.type) }
fun Context.readRestoreUserIdIndex() = readStoreString(key = KeyRestoreUserIdIndex, defValue = "[0]").map { Gson().fromJson<List<Int>>(it, object : TypeToken<List<Int>>() {}.type) }
fun Context.readKillAppOption() = readStoreString(key = KeyKillAppOption, defValue = "").map { KillAppOption.of(it) }
fun Context.readLanguage() = readStoreString(key = KeyLanguage, defValue = ConstantUtil.LANGUAGE_SYSTEM)

/**
* The final path for saving the backup.
Expand All @@ -59,3 +61,4 @@ suspend fun Context.saveRestoreUserIdIndex(value: List<Int>) = saveStoreString(k
suspend fun Context.saveBackupSavePath(value: String) = saveStoreString(key = KeyBackupSavePath, value = value.trim())
suspend fun Context.saveCustomSUFile(value: String) = saveStoreString(key = KeyCustomSUFile, value = value.trim())
suspend fun Context.saveKillAppOption(value: KillAppOption) = saveStoreString(key = KeyKillAppOption, value = value.name.trim())
suspend fun Context.saveLanguage(value: String) = saveStoreString(key = KeyLanguage, value = value.trim())
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.xayah.core.model.database
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.xayah.core.model.R
import com.xayah.core.model.StorageType
import com.xayah.core.model.util.formatSize

Expand Down Expand Up @@ -34,6 +35,12 @@ data class DirectoryEntity(

val totalBytesDisplay: String
get() = totalBytes.toDouble().formatSize()

val titleResId get() = when(storageType) {
StorageType.INTERNAL -> R.string.internal_storage
StorageType.EXTERNAL -> R.string.external_storage
StorageType.CUSTOM -> R.string.custom_directory
}
}

@Entity
Expand Down
3 changes: 3 additions & 0 deletions source/core/model/src/main/res/values/ids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@
<item name="backup" type="string" />
<item name="restore" type="string" />
<item name="batch" type="string" />
<item name="internal_storage" type="string" />
<item name="external_storage" type="string" />
<item name="custom_directory" type="string" />
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ sealed class MainRoutes(val route: String) {
}
data object BackupSettings : MainRoutes(route = "main_backup_settings")
data object RestoreSettings : MainRoutes(route = "main_restore_settings")
data object LanguageSettings : MainRoutes(route = "main_language_settings")
data object BlackList : MainRoutes(route = "main_blacklist")
data object Configurations : MainRoutes(route = "main_configurations")
data object About : MainRoutes(route = "main_about")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.xayah.core.util

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri

fun Context.toBrowser(url: String) = startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
fun Context.getActivity(): Activity = this as Activity
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.xayah.core.util

import android.content.Context
import androidx.core.app.LocaleManagerCompat
import com.xayah.core.datastore.ConstantUtil
import java.util.Locale

object LanguageUtil {
fun getSystemLocale(context: Context) = LocaleManagerCompat.getSystemLocales(context).get(0)!!

fun String.toLocale(context: Context): Locale = if (this == ConstantUtil.LANGUAGE_SYSTEM) {
getSystemLocale(context)
} else {
Locale.forLanguageTag(this)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.xayah.core.util

import android.Manifest
import android.app.Activity
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
Expand Down Expand Up @@ -35,7 +34,7 @@ object NotificationUtil {

fun requestPermissions(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(context as Activity, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
ActivityCompat.requestPermissions(context.getActivity(), arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
} else {
runCatching {
val intent = Intent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import android.content.Intent
import android.content.pm.ApplicationInfo
import android.os.Build
import androidx.compose.material3.ExperimentalMaterial3Api
import com.xayah.core.common.util.toLineString
import com.xayah.core.common.util.BuildConfigUtil
import com.xayah.core.common.util.toLineString
import com.xayah.core.util.DateUtil
import java.io.PrintWriter
import java.io.StringWriter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.xayah.feature.crash

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
Expand Down Expand Up @@ -36,7 +36,7 @@ import com.xayah.core.ui.token.PaddingTokens
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
class MainActivity : AppCompatActivity() {
@ExperimentalMaterial3Api
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ fun PageDashboard() {

if (directoryState != null) {
OverviewStorageCard(
StringResourceToken.fromString(directoryState!!.title),
StringResourceToken.fromStringId(directoryState!!.titleResId),
SegmentProgress(used = directoryState!!.usedBytes, total = directoryState!!.totalBytes),
SegmentProgress(used = directoryState!!.childUsedBytes, total = directoryState!!.totalBytes),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package com.xayah.feature.main.dashboard

import android.content.Context
import androidx.compose.material3.ExperimentalMaterial3Api
import com.xayah.core.data.repository.DirectoryRepository
import com.xayah.core.datastore.readLastBackupTime
import com.xayah.core.model.database.DirectoryEntity
import com.xayah.core.ui.viewmodel.BaseViewModel
import com.xayah.core.ui.viewmodel.IndexUiEffect
import com.xayah.core.ui.viewmodel.UiIntent
import com.xayah.core.ui.viewmodel.UiState
import com.xayah.core.data.repository.DirectoryRepository
import com.xayah.core.datastore.readLastBackupTime
import com.xayah.core.model.database.DirectoryEntity
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ fun DirectoryCard(item: DirectoryEntity, performHapticFeedback: Boolean = false,
DirectoryCard(
selected = item.selected,
performHapticFeedback = performHapticFeedback,
title = StringResourceToken.fromString(item.title),
title = StringResourceToken.fromStringId(item.titleResId),
icon = item.icon(),
path = StringResourceToken.fromString(item.pathDisplay()),
error = if (item.error.isEmpty()) null else StringResourceToken.fromString(item.error),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.xayah.feature.main.directory

import androidx.activity.ComponentActivity
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -27,6 +26,7 @@ import com.xayah.core.ui.model.getActionMenuDeleteItem
import com.xayah.core.ui.model.getActionMenuReturnItem
import com.xayah.core.ui.token.SizeTokens
import com.xayah.core.ui.util.fromStringId
import com.xayah.core.util.getActivity

@ExperimentalFoundationApi
@ExperimentalLayoutApi
Expand Down Expand Up @@ -94,7 +94,7 @@ fun PageDirectory() {

item {
CustomDirectoryCard(enabled = uiState.updating.not()) {
viewModel.emitIntentOnIO(IndexUiIntent.Add(context = context as ComponentActivity))
viewModel.emitIntentOnIO(IndexUiIntent.Add(context = context.getActivity()))
}
}

Expand Down
Loading

0 comments on commit 14bdcb0

Please sign in to comment.