diff --git a/app/src/main/java/me/ash/reader/App.kt b/app/src/main/java/me/ash/reader/App.kt index ef073f2..d04971a 100644 --- a/app/src/main/java/me/ash/reader/App.kt +++ b/app/src/main/java/me/ash/reader/App.kt @@ -77,6 +77,9 @@ class App : Application(), Configuration.Provider { } } + private fun dataStoreInit() { + } + private suspend fun accountInit() { if (accountRepository.isNoAccount()) { val account = accountRepository.addDefaultAccount() @@ -85,9 +88,6 @@ class App : Application(), Configuration.Provider { } } - private fun dataStoreInit() { - } - private fun workerInit() { rssRepository.get().doSync() } diff --git a/app/src/main/java/me/ash/reader/MainActivity.kt b/app/src/main/java/me/ash/reader/MainActivity.kt index 1473493..b4f2ebe 100644 --- a/app/src/main/java/me/ash/reader/MainActivity.kt +++ b/app/src/main/java/me/ash/reader/MainActivity.kt @@ -18,7 +18,9 @@ import coil.memory.MemoryCache import coil.request.* import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CompletableDeferred +import me.ash.reader.data.preference.LanguagesPreference import me.ash.reader.data.preference.SettingsProvider +import me.ash.reader.ui.ext.languages import me.ash.reader.ui.page.common.HomeEntry @AndroidEntryPoint @@ -28,6 +30,13 @@ class MainActivity : ComponentActivity(), ImageLoader { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) Log.i("RLog", "onCreate: ${ProfileInstallerInitializer().create(this)}") + + // Set the language + LanguagesPreference.fromValue(languages).let { + if (it == LanguagesPreference.UseDeviceLanguages) return@let + it.setLocale(this) + } + setContent { SettingsProvider { HomeEntry() diff --git a/app/src/main/java/me/ash/reader/data/preference/LanguagesPreference.kt b/app/src/main/java/me/ash/reader/data/preference/LanguagesPreference.kt new file mode 100644 index 0000000..6e10370 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/LanguagesPreference.kt @@ -0,0 +1,86 @@ +package me.ash.reader.data.preference + +import android.content.Context +import android.os.LocaleList +import android.util.Log +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import me.ash.reader.R +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put +import java.util.* + +sealed class LanguagesPreference(val value: Int) : Preference() { + object UseDeviceLanguages : LanguagesPreference(0) + object English : LanguagesPreference(1) + object ChineseSimplified : LanguagesPreference(2) + + override fun put(context: Context, scope: CoroutineScope) { + scope.launch { + context.dataStore.put( + DataStoreKeys.Languages, + value + ) + setLocale(context) + } + } + + fun getDesc(context: Context): String = + when (this) { + UseDeviceLanguages -> context.getString(R.string.use_device_languages) + English -> context.getString(R.string.english) + ChineseSimplified -> context.getString(R.string.chinese_simplified) + } + + fun getLocale(): Locale = + when (this) { + UseDeviceLanguages -> LocaleList.getDefault().get(0) + English -> Locale("en", "US") + ChineseSimplified -> Locale("zh", "CN") + } + + fun setLocale(context: Context) { + val locale = getLocale() + + Log.i("Rlog", "setLocale: $locale, ${LocaleList.getDefault().get(0)}") + + val resources = context.resources + val metrics = resources.displayMetrics + val configuration = resources.configuration + configuration.setLocale(locale) + configuration.setLocales(LocaleList(locale)) + context.createConfigurationContext(configuration) + resources.updateConfiguration(configuration, metrics) + + val appResources = context.applicationContext.resources + val appMetrics = appResources.displayMetrics + val appConfiguration = appResources.configuration + appConfiguration.setLocale(locale) + appConfiguration.setLocales(LocaleList(locale)) + context.applicationContext.createConfigurationContext(appConfiguration) + appResources.updateConfiguration(appConfiguration, appMetrics) + } + + companion object { + val default = UseDeviceLanguages + val values = listOf(UseDeviceLanguages, English, ChineseSimplified) + + fun fromPreferences(preferences: Preferences): LanguagesPreference = + when (preferences[DataStoreKeys.Languages.key]) { + 0 -> UseDeviceLanguages + 1 -> English + 2 -> ChineseSimplified + else -> default + } + + fun fromValue(value: Int): LanguagesPreference = + when (value) { + 0 -> UseDeviceLanguages + 1 -> English + 2 -> ChineseSimplified + else -> default + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/Settings.kt b/app/src/main/java/me/ash/reader/data/preference/Settings.kt index c97e359..70996c7 100644 --- a/app/src/main/java/me/ash/reader/data/preference/Settings.kt +++ b/app/src/main/java/me/ash/reader/data/preference/Settings.kt @@ -37,6 +37,8 @@ data class Settings( val flowArticleListTime: FlowArticleListTimePreference = FlowArticleListTimePreference.default, val flowArticleListDateStickyHeader: FlowArticleListDateStickyHeaderPreference = FlowArticleListDateStickyHeaderPreference.default, val flowArticleListTonalElevation: FlowArticleListTonalElevationPreference = FlowArticleListTonalElevationPreference.default, + + val languages: LanguagesPreference = LanguagesPreference.default, ) fun Preferences.toSettings(): Settings { @@ -68,6 +70,8 @@ fun Preferences.toSettings(): Settings { this ), flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this), + + languages = LanguagesPreference.fromPreferences(this), ) } @@ -109,6 +113,8 @@ fun SettingsProvider( LocalFlowFilterBarFilled provides settings.flowFilterBarFilled, LocalFlowFilterBarPadding provides settings.flowFilterBarPadding, LocalFlowFilterBarTonalElevation provides settings.flowFilterBarTonalElevation, + + LocalLanguages provides settings.languages, ) { content() } @@ -162,3 +168,6 @@ val LocalFlowArticleListDateStickyHeader = compositionLocalOf { FlowArticleListDateStickyHeaderPreference.default } val LocalFlowArticleListTonalElevation = compositionLocalOf { FlowArticleListTonalElevationPreference.default } + +val LocalLanguages = + compositionLocalOf { LanguagesPreference.default } diff --git a/app/src/main/java/me/ash/reader/ui/component/Banner.kt b/app/src/main/java/me/ash/reader/ui/component/Banner.kt index f00e857..3e235bf 100644 --- a/app/src/main/java/me/ash/reader/ui/component/Banner.kt +++ b/app/src/main/java/me/ash/reader/ui/component/Banner.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import me.ash.reader.ui.theme.palette.alwaysLight @@ -43,7 +44,7 @@ fun Banner( Surface( modifier = modifier .fillMaxWidth() - .height(88.dp), + .height(if (!desc.isNullOrBlank()) 88.dp else Dp.Unspecified), color = Color.Unspecified, ) { Row( diff --git a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt index 68dc81b..53c3e0c 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt @@ -39,6 +39,9 @@ val Context.initialPage: Int val Context.initialFilter: Int get() = this.dataStore.get(DataStoreKeys.InitialFilter) ?: 2 +val Context.languages: Int + get() = this.dataStore.get(DataStoreKeys.Languages) ?: 0 + suspend fun DataStore.put(dataStoreKeys: DataStoreKeys, value: T) { this.edit { withContext(Dispatchers.IO) { @@ -244,4 +247,9 @@ sealed class DataStoreKeys { override val key: Preferences.Key get() = intPreferencesKey("initialFilter") } + + object Languages : DataStoreKeys() { + override val key: Preferences.Key + get() = intPreferencesKey("languages") + } } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt index f8b3943..88a2ccd 100644 --- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt +++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt @@ -27,6 +27,7 @@ import me.ash.reader.ui.page.settings.color.DarkTheme import me.ash.reader.ui.page.settings.color.feeds.FeedsPageStyle import me.ash.reader.ui.page.settings.color.flow.FlowPageStyle import me.ash.reader.ui.page.settings.interaction.Interaction +import me.ash.reader.ui.page.settings.languages.Languages import me.ash.reader.ui.page.settings.tips.TipsAndSupport import me.ash.reader.ui.page.startup.StartupPage import me.ash.reader.ui.theme.AppTheme @@ -146,6 +147,11 @@ fun HomeEntry( Interaction(navController) } + // Languages + animatedComposable(route = RouteName.LANGUAGES) { + Languages(navController = navController) + } + // Tips & Support animatedComposable(route = RouteName.TIPS_AND_SUPPORT) { TipsAndSupport(navController) diff --git a/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt b/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt index 2de9d92..b0f6eba 100644 --- a/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt +++ b/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt @@ -21,6 +21,9 @@ object RouteName { // Interaction const val INTERACTION = "interaction" + // Languages + const val LANGUAGES = "languages" + // Tips & Support const val TIPS_AND_SUPPORT = "tips_and_support" } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index 0cb21dc..5a88950 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -143,8 +143,11 @@ fun SettingsPage( title = stringResource(R.string.languages), desc = stringResource(R.string.languages_desc), icon = Icons.Outlined.Language, - enable = false, - ) {} + ) { + navController.navigate(RouteName.LANGUAGES) { + launchSingleTop = true + } + } } item { SelectableSettingGroupItem( @@ -156,6 +159,7 @@ fun SettingsPage( launchSingleTop = true } } + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt index 991762a..8c35bd2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt @@ -153,7 +153,7 @@ fun FlowPageStyle( text = stringResource(R.string.article_list) ) SettingItem( - title = stringResource(R.string.display_feed_favicon), + title = stringResource(R.string.feed_favicons), onClick = { (!articleListFeedIcon).put(context, scope) }, @@ -163,7 +163,7 @@ fun FlowPageStyle( } } SettingItem( - title = stringResource(R.string.display_feed_name), + title = stringResource(R.string.feed_names), onClick = { (!articleListFeedName).put(context, scope) }, @@ -173,14 +173,14 @@ fun FlowPageStyle( } } SettingItem( - title = stringResource(R.string.display_article_image), + title = stringResource(R.string.article_images), enable = false, onClick = {}, ) { Switch(activated = false, enable = false) } SettingItem( - title = stringResource(R.string.display_article_desc), + title = stringResource(R.string.article_desc), onClick = { (!articleListDesc).put(context, scope) }, @@ -190,7 +190,7 @@ fun FlowPageStyle( } } SettingItem( - title = stringResource(R.string.display_article_date), + title = stringResource(R.string.article_date), onClick = { (!articleListTime).put(context, scope) }, diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt b/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt new file mode 100644 index 0000000..786b17f --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt @@ -0,0 +1,106 @@ +package me.ash.reader.ui.page.settings.languages + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.KeyboardArrowRight +import androidx.compose.material.icons.outlined.Lightbulb +import androidx.compose.material.icons.rounded.ArrowBack +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import me.ash.reader.R +import me.ash.reader.data.preference.LanguagesPreference +import me.ash.reader.data.preference.LocalLanguages +import me.ash.reader.ui.component.Banner +import me.ash.reader.ui.component.DisplayText +import me.ash.reader.ui.component.FeedbackIconButton +import me.ash.reader.ui.page.settings.SettingItem +import me.ash.reader.ui.theme.palette.onLight + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Languages( + navController: NavHostController, +) { + val context = LocalContext.current + val languages = LocalLanguages.current + val scope = rememberCoroutineScope() + + Scaffold( + modifier = Modifier + .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) + .statusBarsPadding() + .navigationBarsPadding(), + containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, + topBar = { + SmallTopAppBar( + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface + ), + title = {}, + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } + }, + actions = {} + ) + }, + content = { + LazyColumn { + item(key = languages.value) { + DisplayText(text = stringResource(R.string.languages), desc = "") + Spacer(modifier = Modifier.height(16.dp)) + Banner( + title = stringResource(R.string.help_translate), + icon = Icons.Outlined.Lightbulb, + action = { + Icon( + imageVector = Icons.Outlined.KeyboardArrowRight, + contentDescription = stringResource(R.string.go_to), + ) + }, + ) { + context.startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(context.getString(R.string.github_link)) + ) + ) + } + Spacer(modifier = Modifier.height(16.dp)) + } + item { + LanguagesPreference.values.map { + SettingItem( + title = it.getDesc(context), + onClick = { + it.put(context, scope) + }, + ) { + RadioButton(selected = it == languages, onClick = { + it.put(context, scope) + }) + } + } + } + } + } + ) +} diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 0994f75..a0667ac 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1,5 +1,4 @@ - Read You 全部 %1$d 项已归档 未读 @@ -90,7 +89,9 @@ 交互 启动时、触感反馈 语言 - 英语、中文 + English、简体中文 + 跟随系统设置 + 帮助我们翻译 提示和支持 关于、开源 欢迎 @@ -120,8 +121,6 @@ 阅读页面 捐赠 开放源代码许可 - https://github.com/Ashinch/ReadYou - https://t.me/ReadYouApp https://gitee.com/api/v5/repos/Ashinch/ReadYou/releases/latest 更新日志 更新 @@ -140,11 +139,11 @@ 漩涡书院 两端边距 - 显示文章发布时间 - 显示文章描述 - 显示文章插图 - 显示订阅源名称 - 显示订阅源图标 + 文章发布时间 + 文章描述 + 文章插图 + 订阅源名称 + 订阅源图标 文章发布日期粘性标签(实验性) 文章列表 分组列表 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index afac7df..f5da3bb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Read You + Read You All %1$d Archived Items Unread @@ -91,7 +91,11 @@ Interaction On start, haptic feedback Languages - English, Chinese + English, 简体中文 + Help us translate + Use Device Languages + English + 简体中文 Tips & support About, open source Welcome @@ -121,8 +125,8 @@ Reading Page Sponsor Open Source Licenses - https://github.com/Ashinch/ReadYou - https://t.me/ReadYouApp + https://github.com/Ashinch/ReadYou + https://t.me/ReadYouApp https://api.github.com/repos/Ashinch/ReadYou/releases/latest Change Log Update @@ -141,12 +145,12 @@ Reddit value Padding on Both Ends - Display Article Publish Time - Display Article Descriptions - Display Article Images - Display Feed Names - Display Feed Favicons - Article Publish Date Sticky Header (Experimental) + Article Published Time + Article Descriptions + Article Images + Feed Names + Feed Favicons + Article Published Date Sticky Header (Experimental) Article List Group List Always Expand